mirror of
https://github.com/docmost/docmost.git
synced 2026-05-18 15:34:05 +08:00
b76f5adaad
* feat(ee): AI menu * - Add insert below and copy option * prebuild @editor-ext * sanitize output * clear existing output * switch to menu component * refactor directory * separator * refactor directory * support more languages * pass markdown to model * fix: close AI menu on page change * enhance text input and preview styling * fix: Use absolute positioning for the AI menu * make preview scrollable * activation controls * enhance bubble menu * sync * set width * fix line break * switch terminologies * cloud * buffer --------- Co-authored-by: Philipinho <16838612+Philipinho@users.noreply.github.com>
84 lines
2.0 KiB
TypeScript
84 lines
2.0 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 aiAnswers(
|
|
params: IPageSearchParams,
|
|
onChunk?: (chunk: { content?: string; sources?: any[] }) => void,
|
|
): Promise<IAiSearchResponse> {
|
|
const response = await fetch("/api/ai/answers", {
|
|
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[] = [];
|
|
let buffer = "";
|
|
|
|
if (reader) {
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
buffer += decoder.decode(value, { stream: true });
|
|
const lines = buffer.split("\n");
|
|
|
|
// Keep the last incomplete line in the buffer
|
|
buffer = lines.pop() || "";
|
|
|
|
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 };
|
|
}
|