155 lines
4.8 KiB
TypeScript
155 lines
4.8 KiB
TypeScript
import { apiFetch, readSSE, type SSESummary } from "@/lib/api";
|
||||
|
|
import type { Page } from "@/lib/admin-api";
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 投顾 Agent(/api/advisor-agent/*)适配层。
|
|||
|
|
* 该系列接口的成功码是 0 而不是 200(agent_success),已在 lib/api.ts 统一兼容。
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
export interface AgentMessage {
|
|||
|
|
role?: string;
|
|||
|
|
content?: string;
|
|||
|
|
[key: string]: unknown;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface AgentDraft {
|
|||
|
|
draft_id?: string;
|
|||
|
|
id?: string;
|
|||
|
|
title?: string | null;
|
|||
|
|
content?: string | null;
|
|||
|
|
intent?: string | null;
|
|||
|
|
status?: string | null;
|
|||
|
|
customer_id?: number | null;
|
|||
|
|
create_time?: string | null;
|
|||
|
|
update_time?: string | null;
|
|||
|
|
structured_data?: Record<string, unknown> | null;
|
|||
|
|
[key: string]: unknown;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface DataQueryResult {
|
|||
|
|
query_id?: string;
|
|||
|
|
answer?: string;
|
|||
|
|
summary?: string;
|
|||
|
|
columns?: string[];
|
|||
|
|
rows?: Record<string, unknown>[];
|
|||
|
|
total?: number;
|
|||
|
|
page?: number;
|
|||
|
|
page_size?: number;
|
|||
|
|
[key: string]: unknown;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface FundAnalysisResult {
|
|||
|
|
analysis_text?: string;
|
|||
|
|
items?: Record<string, unknown>[];
|
|||
|
|
[key: string]: unknown;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface TalkScriptResult {
|
|||
|
|
script?: string;
|
|||
|
|
content?: string;
|
|||
|
|
scene?: string;
|
|||
|
|
[key: string]: unknown;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 会话上下文:scope=customer 时必须带 customer_id,否则后端会尝试从问题文本里猜客户。 */
|
|||
|
|
export interface ChatScope {
|
|||
|
|
sessionId?: string;
|
|||
|
|
customerId?: number | null;
|
|||
|
|
scope?: "customer" | "advisor";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function qs(params: Record<string, string | number | undefined | null>) {
|
|||
|
|
const search = new URLSearchParams();
|
|||
|
|
for (const [key, value] of Object.entries(params)) {
|
|||
|
|
if (value !== undefined && value !== null && value !== "") search.set(key, String(value));
|
|||
|
|
}
|
|||
|
|
const text = search.toString();
|
|||
|
|
return text ? `?${text}` : "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export const assistantApi = {
|
|||
|
|
/** GET /session/{session_id}/history —— 读取本人短期 Agent 会话记录。 */
|
|||
|
|
sessionHistory: (sessionId: string) =>
|
|||
|
|
apiFetch<AgentMessage[]>(`/advisor-agent/session/${encodeURIComponent(sessionId)}/history`),
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* POST /chat/stream —— 响应是 text/event-stream,但后端整包输出,
|
|||
|
|
* 因此读完整条响应后一次性返回聚合结果(非流式渲染)。
|
|||
|
|
*/
|
|||
|
|
chatOnce(
|
|||
|
|
body: {
|
|||
|
|
query: string;
|
|||
|
|
session_id?: string;
|
|||
|
|
scope?: "customer" | "advisor";
|
|||
|
|
customer_id?: number | null;
|
|||
|
|
},
|
|||
|
|
signal?: AbortSignal
|
|||
|
|
): Promise<SSESummary> {
|
|||
|
|
return readSSE("/advisor-agent/chat/stream", body, signal);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/** POST /data-query —— 自然语言查客户数据(非流式)。 */
|
|||
|
|
dataQuery: (body: {
|
|||
|
|
query: string;
|
|||
|
|
session_id?: string;
|
|||
|
|
max_rows?: number;
|
|||
|
|
page?: number;
|
|||
|
|
page_size?: number;
|
|||
|
|
}) =>
|
|||
|
|
apiFetch<DataQueryResult>("/advisor-agent/data-query", {
|
|||
|
|
method: "POST",
|
|||
|
|
body: JSON.stringify(body),
|
|||
|
|
}),
|
|||
|
|
|
|||
|
|
/** GET /draft/list */
|
|||
|
|
drafts: (params: { customer_id?: number; status?: string; page?: number; page_size?: number } = {}) =>
|
|||
|
|
apiFetch<Page<AgentDraft> & { items?: AgentDraft[] }>(`/advisor-agent/draft/list${qs(params)}`),
|
|||
|
|
|
|||
|
|
/** GET /draft/{draft_id} */
|
|||
|
|
draft: (draftId: string) =>
|
|||
|
|
apiFetch<AgentDraft>(`/advisor-agent/draft/${encodeURIComponent(draftId)}`),
|
|||
|
|
|
|||
|
|
/** PUT /draft/{draft_id}/save */
|
|||
|
|
saveDraft: (
|
|||
|
|
draftId: string,
|
|||
|
|
body: { title?: string; content?: string; structured_data?: Record<string, unknown> }
|
|||
|
|
) =>
|
|||
|
|
apiFetch<AgentDraft>(`/advisor-agent/draft/${encodeURIComponent(draftId)}/save`, {
|
|||
|
|
method: "PUT",
|
|||
|
|
body: JSON.stringify(body),
|
|||
|
|
}),
|
|||
|
|
|
|||
|
|
/** POST /draft/{draft_id}/operate —— 后端目前仅支持 operation=discard。 */
|
|||
|
|
operateDraft: (draftId: string, operation: "discard") =>
|
|||
|
|
apiFetch<AgentDraft>(`/advisor-agent/draft/${encodeURIComponent(draftId)}/operate`, {
|
|||
|
|
method: "POST",
|
|||
|
|
body: JSON.stringify({ operation }),
|
|||
|
|
}),
|
|||
|
|
|
|||
|
|
/** POST /rebalance/run —— 异步受理,返回 accepted/queued,草稿稍后生成。 */
|
|||
|
|
runRebalance: (query: string) =>
|
|||
|
|
apiFetch<{ accepted?: boolean; status?: string }>("/advisor-agent/rebalance/run", {
|
|||
|
|
method: "POST",
|
|||
|
|
body: JSON.stringify({ query }),
|
|||
|
|
}),
|
|||
|
|
|
|||
|
|
/** POST /fund-analysis —— query 里需包含基金代码,如「分析 000001」。 */
|
|||
|
|
fundAnalysis: (query: string) =>
|
|||
|
|
apiFetch<FundAnalysisResult>("/advisor-agent/fund-analysis", {
|
|||
|
|
method: "POST",
|
|||
|
|
body: JSON.stringify({ query }),
|
|||
|
|
}),
|
|||
|
|
|
|||
|
|
/** POST /generate-talk-script */
|
|||
|
|
generateTalkScript: (query: string) =>
|
|||
|
|
apiFetch<TalkScriptResult>("/advisor-agent/generate-talk-script", {
|
|||
|
|
method: "POST",
|
|||
|
|
body: JSON.stringify({ query }),
|
|||
|
|
}),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/** 生成本地会话 ID(后端 session_id 为空时自动创建,这里只是前端约定的稳定键)。 */
|
|||
|
|
export function newSessionId(): string {
|
|||
|
|
return `advisor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|||
|
|
}
|