Skip to content
File

Blob: src/worker/lib/ai/mock.ts

typescript42 lines
1import type { AiUsage } from "@/shared/ai";
2import type {
3 AiChatMessage,
4 AiChatOptions,
5 AiClient,
6 AiFrame,
7 AiSummarizeOptions,
8 AiSummarizeResult,
9} from "@/worker/lib/ai/types";
10 
11const MOCK_CHAT_PREFIX = "[mock-chat] ";
12const MOCK_SUMMARY_PREFIX = "[mock-summary] ";
13const MOCK_USAGE: AiUsage = { promptTokens: 10, completionTokens: 5, totalTokens: 15 };
14 
15export function createMockAiClient(): AiClient {
16 return {
17 async chat(messages: AiChatMessage[], _opts?: AiChatOptions): Promise<AsyncIterable<AiFrame>> {
18 const lastUser = [...messages].reverse().find((m) => m.role === "user");
19 const source = lastUser ? lastUser.content : messages.map((m) => m.content).join(" ");
20 const body = MOCK_CHAT_PREFIX + source.replace(/\s+/g, " ").trim().slice(0, 120);
21 const tokens = splitIntoTokens(body);
22 
23 return (async function* (): AsyncGenerator<AiFrame> {
24 for (const token of tokens) {
25 yield { type: "chunk", text: token };
26 }
27 yield { type: "usage", usage: MOCK_USAGE };
28 })();
29 },
30 
31 async summarize(text: string, _opts?: AiSummarizeOptions): Promise<AiSummarizeResult> {
32 const condensed = text.replace(/\s+/g, " ").trim().slice(0, 160);
33 return { summary: MOCK_SUMMARY_PREFIX + condensed, usage: MOCK_USAGE };
34 },
35 };
36}
37 
38function splitIntoTokens(text: string): string[] {
39 const matches = text.match(/\S+\s*/g);
40 return matches ?? [text];
41}