add vitepress docs
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 演示专用的假 SSE 流。
|
||||
*
|
||||
* `useAiChat` 要求 `request` 返回一个 `Response`,真实的实现由后端提供。
|
||||
* 这里用 ReadableStream 按内置协议逐条吐出事件,让文档站无需后端也能跑通
|
||||
* 流式对话的完整链路。
|
||||
*
|
||||
* 内置协议的事件名与载荷字段(见 `parseDefaultEvent` / `extractText` 等):
|
||||
* - `thinking` 载荷取 `thought` / `thinking` / `reasoning`
|
||||
* - `answer` 载荷取 `answer` / `text` / `content`
|
||||
* - `tool-status` 载荷取 `text` / `message` / `status`
|
||||
* - `citation` 载荷为引用对象
|
||||
* - `done` 载荷可带 `conversationId` 与 `duration`
|
||||
*/
|
||||
|
||||
/** 单条 SSE 事件。 */
|
||||
interface MockEvent {
|
||||
event: string;
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** 每个事件之间的间隔,模拟真实的流式节奏。 */
|
||||
const STEP_DELAY = 420;
|
||||
|
||||
function buildEvents(question: string): MockEvent[] {
|
||||
return [
|
||||
{ event: "thinking", data: { thought: `先理解问题:「${question}」` } },
|
||||
{ event: "thinking", data: { thought: "然后确认需要的数据口径与时间范围。" } },
|
||||
{ event: "tool-status", data: { text: "正在查询数据" } },
|
||||
{ event: "answer", data: { text: "根据当前口径," } },
|
||||
{ event: "answer", data: { text: `关于「${question}」的结论如下:` } },
|
||||
{ event: "answer", data: { text: "\n\n- 近 30 天整体**环比上升 12%**" } },
|
||||
{ event: "answer", data: { text: "\n- 其中移动端贡献了主要增量" } },
|
||||
{ event: "citation", data: { source: "分析中心", dataDomain: "订单域", timeRange: "近 30 天" } },
|
||||
{ event: "done", data: { conversationId: "demo-conversation", duration: STEP_DELAY * 9 } }
|
||||
];
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一段假的流式回复。
|
||||
*
|
||||
* @param question 用户提问,会回显在回答里
|
||||
* @param signal 取消信号,用户点「停止」时中断
|
||||
*/
|
||||
export function createMockReply(question: string, signal?: AbortSignal): Response {
|
||||
const encoder = new TextEncoder();
|
||||
const events = buildEvents(question);
|
||||
let index = 0;
|
||||
|
||||
const stream = new ReadableStream<Uint8Array>({
|
||||
async pull(controller) {
|
||||
if (signal?.aborted) {
|
||||
controller.error(new DOMException("已停止生成", "AbortError"));
|
||||
return;
|
||||
}
|
||||
if (index >= events.length) {
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
const item = events[index];
|
||||
index += 1;
|
||||
controller.enqueue(encoder.encode(`event: ${item.event}\ndata: ${JSON.stringify(item.data)}\n\n`));
|
||||
await sleep(STEP_DELAY);
|
||||
}
|
||||
});
|
||||
|
||||
return new Response(stream, {
|
||||
headers: { "Content-Type": "text/event-stream" }
|
||||
});
|
||||
}
|
||||
|
||||
/** 构造一条用于静态展示的消息。 */
|
||||
export function createMessage(
|
||||
id: number,
|
||||
role: "user" | "assistant",
|
||||
content: string,
|
||||
extra: Partial<{ thinkingList: Array<{ id: number; content: string }>; citationList: unknown[] }> = {}
|
||||
) {
|
||||
return {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
thinkingList: extra.thinkingList ?? [],
|
||||
citationList: extra.citationList ?? [],
|
||||
createdAt: Date.now() - (10 - id) * 60_000,
|
||||
statusText: "",
|
||||
duration: role === "assistant" ? 2400 : undefined
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user