update
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import type { QuickQuestionsResponse, SSEMessage } from './index';
|
||||
|
||||
const AGENT_API_BASE = '/api/v1';
|
||||
|
||||
export async function getQuickQuestions(): Promise<QuickQuestionsResponse> {
|
||||
return {
|
||||
questions: [
|
||||
{ id: '1', question: '请总结最新入库法规对电池安全的核心要求', category: '法规解读' },
|
||||
{ id: '2', question: '我上传的制度文档与新能源法规有哪些潜在冲突?', category: '差距分析' },
|
||||
{ id: '3', question: '请给出法规依据,并按条款列出整改建议', category: '整改建议' },
|
||||
{ id: '4', question: '请解释 UN-ECE 与 GB 标准在网络安全方面的差异', category: '标准对比' },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function parseSSEChunk(raw: string, onMessage: (data: SSEMessage) => void) {
|
||||
const blocks = raw.split('\n\n');
|
||||
for (const block of blocks) {
|
||||
if (!block.trim()) continue;
|
||||
|
||||
let eventName = 'message';
|
||||
const dataLines: string[] = [];
|
||||
|
||||
for (const line of block.split('\n')) {
|
||||
if (line.startsWith('event:')) {
|
||||
eventName = line.slice(6).trim();
|
||||
} else if (line.startsWith('data:')) {
|
||||
dataLines.push(line.slice(5).trim());
|
||||
}
|
||||
}
|
||||
|
||||
const joined = dataLines.join('\n');
|
||||
if (!joined) continue;
|
||||
|
||||
if (eventName === 'sources') {
|
||||
try {
|
||||
const docs = JSON.parse(joined) as Array<Record<string, unknown>>;
|
||||
onMessage({
|
||||
type: 'retrieved',
|
||||
docs: docs.map((doc, index) => ({
|
||||
id: String(doc.doc_id || doc.index || index + 1),
|
||||
score: Number(doc.score || 0),
|
||||
preview: String(doc.content || doc.snippet || ''),
|
||||
doc_name: String(doc.doc_name || doc.filename || `引用 ${index + 1}`),
|
||||
clause: String(doc.clause_number || doc.section_title || '法规片段'),
|
||||
doc_id: doc.doc_id ? String(doc.doc_id) : undefined,
|
||||
download_url: doc.doc_id ? `${AGENT_API_BASE}/documents/download/${String(doc.doc_id)}` : undefined,
|
||||
})),
|
||||
});
|
||||
} catch {
|
||||
// Ignore malformed source payloads.
|
||||
}
|
||||
} else if (eventName === 'content') {
|
||||
onMessage({ type: 'chunk', text: joined });
|
||||
} else if (eventName === 'done') {
|
||||
onMessage({ type: 'done', text: joined });
|
||||
} else if (eventName === 'error') {
|
||||
onMessage({ type: 'error', text: joined });
|
||||
} else if (eventName === 'status') {
|
||||
onMessage({ type: 'status', text: joined });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function ragChat(
|
||||
query: string,
|
||||
topK: number = 5,
|
||||
onMessage: (data: SSEMessage) => void,
|
||||
onError?: (error: Error) => void,
|
||||
onComplete?: () => void
|
||||
): Promise<void> {
|
||||
try {
|
||||
const response = await fetch(`${AGENT_API_BASE}/agent/chat/stream`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'text/event-stream',
|
||||
},
|
||||
body: JSON.stringify({ query, top_k: topK }),
|
||||
});
|
||||
|
||||
if (!response.ok || !response.body) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const parts = buffer.split('\n\n');
|
||||
buffer = parts.pop() || '';
|
||||
parseSSEChunk(parts.join('\n\n'), onMessage);
|
||||
}
|
||||
|
||||
if (buffer.trim()) {
|
||||
parseSSEChunk(buffer, onMessage);
|
||||
}
|
||||
|
||||
if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
} catch (error) {
|
||||
if (onError) {
|
||||
onError(error instanceof Error ? error : new Error(String(error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type { QuickQuestionsResponse, SSEMessage };
|
||||
Reference in New Issue
Block a user