feat: plan 18
This commit is contained in:
@@ -139,9 +139,28 @@ export interface ChatAnswer {
|
||||
artifact_scan?: { ok: boolean; output: string };
|
||||
tool_output_scan?: { ok: boolean; output: string };
|
||||
};
|
||||
synthesis?: {
|
||||
mode: 'deterministic' | 'model' | string;
|
||||
reason?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
class?: string;
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
cost_source?: string;
|
||||
};
|
||||
actor: SettingsActor;
|
||||
}
|
||||
|
||||
export type ChatStreamPhase = Partial<ChatAnswer> & {
|
||||
phase?: 'draft' | 'final';
|
||||
answer: string;
|
||||
decision: string;
|
||||
certified: boolean;
|
||||
mode: string;
|
||||
sources: ChatSource[];
|
||||
};
|
||||
|
||||
export interface ChatAction {
|
||||
id: string;
|
||||
label: string;
|
||||
@@ -219,6 +238,44 @@ export const api = {
|
||||
post<{ proposal: any; applied: any; audit_verify: { ok: boolean; output: string } }>('approvals/decide', body, actorHeaders(actor)),
|
||||
askChat: (actor: SettingsActor, body: { message: string; chatId?: string; agentId?: string; skillId?: string; delegationLevel?: number }) =>
|
||||
post<ChatAnswer>('chat/ask', body, actorHeaders(actor)),
|
||||
askChatStream: async (
|
||||
actor: SettingsActor,
|
||||
body: { message: string; chatId?: string; agentId?: string; skillId?: string; delegationLevel?: number },
|
||||
onPhase: (phase: ChatStreamPhase) => void,
|
||||
): Promise<void> => {
|
||||
const base = import.meta.env.VITE_API_BASE_URL ?? '/api/v1';
|
||||
const resp = await fetch(`${base}/chat/ask/stream`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...actorHeaders(actor) },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!resp.ok || !resp.body) {
|
||||
throw new Error(`stream failed: ${resp.status}`);
|
||||
}
|
||||
const reader = resp.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buf = '';
|
||||
const flush = (line: string) => {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) return;
|
||||
try {
|
||||
onPhase(JSON.parse(trimmed) as ChatStreamPhase);
|
||||
} catch {
|
||||
/* ignore partial/non-JSON chunk */
|
||||
}
|
||||
};
|
||||
for (;;) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buf += decoder.decode(value, { stream: true });
|
||||
let idx: number;
|
||||
while ((idx = buf.indexOf('\n')) >= 0) {
|
||||
flush(buf.slice(0, idx));
|
||||
buf = buf.slice(idx + 1);
|
||||
}
|
||||
}
|
||||
flush(buf);
|
||||
},
|
||||
verifyChatAudit: () => get<{ ok: boolean; output: string }>('chat/audit/verify'),
|
||||
replayChat: (chatId = '', turnId = '', tenant = '') => get<ChatReplay>(`chat/replay?chatId=${encodeURIComponent(chatId)}&turnId=${encodeURIComponent(turnId)}&tenant=${encodeURIComponent(tenant)}`),
|
||||
chatActions: (actor: SettingsActor) => getWithHeaders<{ success: boolean; actions: ChatAction[] }>('chat/actions', actorHeaders(actor)),
|
||||
|
||||
Reference in New Issue
Block a user