mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
9fb16bc842
* WIP * AI module - init * WIP * sync * WIP * refactor naming * new columns * sync * sync * fix search bug * stream response * WIP * feat embeddings sync * refine * Add workspaceId to page events * refine * WIP * add translation string * sync * reset ai answer on query change * hide AI search in cloud * capture streaming error * sync
80 lines
1.9 KiB
TypeScript
80 lines
1.9 KiB
TypeScript
import api from "@/lib/api-client.ts";
|
|
import { IPageSearchParams } from "@/features/search/types/search.types.ts";
|
|
|
|
export interface IAiSearchResponse {
|
|
answer: string;
|
|
sources?: Array<{
|
|
pageId: string;
|
|
title: string;
|
|
slugId: string;
|
|
spaceSlug: string;
|
|
similarity: number;
|
|
distance: number;
|
|
chunkIndex: number;
|
|
excerpt: string;
|
|
}>;
|
|
}
|
|
|
|
export async function askAi(
|
|
params: IPageSearchParams,
|
|
onChunk?: (chunk: { content?: string; sources?: any[] }) => void,
|
|
): Promise<IAiSearchResponse> {
|
|
const response = await fetch("/api/ai/ask", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
credentials: "include",
|
|
body: JSON.stringify(params),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const reader = response.body?.getReader();
|
|
const decoder = new TextDecoder();
|
|
|
|
let answer = "";
|
|
let sources: any[] = [];
|
|
|
|
if (reader) {
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
const lines = chunk.split("\n");
|
|
|
|
for (const line of lines) {
|
|
if (line.startsWith("data: ")) {
|
|
const data = line.slice(6);
|
|
if (data === "[DONE]") break;
|
|
|
|
try {
|
|
const parsed = JSON.parse(data);
|
|
if (parsed.error) {
|
|
throw new Error(parsed.error);
|
|
}
|
|
if (parsed.content) {
|
|
answer += parsed.content;
|
|
onChunk?.({ content: parsed.content });
|
|
}
|
|
if (parsed.sources) {
|
|
sources = parsed.sources;
|
|
onChunk?.({ sources: parsed.sources });
|
|
}
|
|
} catch (e) {
|
|
if (e instanceof Error) {
|
|
throw e;
|
|
}
|
|
// Skip invalid JSON
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return { answer, sources };
|
|
}
|