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)}`;
|
||
}
|