feat: analysis pipeline with staged SSE
Adds @openuidev/lang-core (one-line reason: the prompt-side library must build in a non-React Node runtime; react-lang's createContext crashes the route's server bundle). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
13bd2e6bfd
commit
ac4cbdd9e6
+24
-3
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod/v4";
|
||||
import { digestForModel, type Digest } from "./digest.ts";
|
||||
import { openAiTextDeltas } from "./stream.ts";
|
||||
import type { AnalystConfig } from "./settings.ts";
|
||||
|
||||
export class AnalystError extends Error {}
|
||||
@@ -36,11 +37,11 @@ export function extractJson(text: string): unknown {
|
||||
}
|
||||
|
||||
export async function proposeCandidates(cfg: AnalystConfig, digest: Digest, note: string,
|
||||
fetchImpl: typeof fetch = fetch): Promise<Candidate[]> {
|
||||
fetchImpl: typeof fetch = fetch, signal?: AbortSignal): Promise<Candidate[]> {
|
||||
const res = await fetchImpl(`${cfg.baseUrl}/chat/completions`, {
|
||||
method: "POST",
|
||||
method: "POST", signal,
|
||||
headers: { "content-type": "application/json", authorization: `Bearer ${cfg.apiKey}` },
|
||||
body: JSON.stringify({ model: cfg.model, temperature: 0.6, ...cfg.extra, stream: false,
|
||||
body: JSON.stringify({ model: cfg.model, temperature: 0.6, max_tokens: 2048, ...cfg.extra, stream: false,
|
||||
messages: [{ role: "system", content: CANDIDATE_SYSTEM_PROMPT },
|
||||
{ role: "user", content: JSON.stringify({ digest: digestForModel(digest), user_note: note || null }) }] }),
|
||||
}).catch((e) => { throw new AnalystError(`Could not reach ${cfg.baseUrl}: ${(e as Error).message}`); });
|
||||
@@ -56,3 +57,23 @@ export async function proposeCandidates(cfg: AnalystConfig, digest: Digest, note
|
||||
}
|
||||
return parsed.data.candidates;
|
||||
}
|
||||
|
||||
export const REPORT_SYSTEM_PROMPT = (openuiPrompt: string) => `${openuiPrompt}
|
||||
|
||||
You are writing the final answer for a DBA holding one SQL Server execution plan. The ranking you are given was
|
||||
computed by Jev from typed judgments. It is not your opinion: do not re-rank it, soften it, or hide it. Quote each
|
||||
option's scores as given. A rewrite must return the same rows as the original — when in doubt keep the original text
|
||||
and say why. Never invent a number that is not in the input.`;
|
||||
|
||||
export async function* streamReport(cfg: AnalystConfig, payload: unknown, openuiPrompt: string,
|
||||
fetchImpl: typeof fetch = fetch, signal?: AbortSignal): AsyncGenerator<string> {
|
||||
const res = await fetchImpl(`${cfg.baseUrl}/chat/completions`, {
|
||||
method: "POST", signal,
|
||||
headers: { "content-type": "application/json", authorization: `Bearer ${cfg.apiKey}` },
|
||||
body: JSON.stringify({ model: cfg.model, temperature: 0.3, max_tokens: 2048, ...cfg.extra, stream: true,
|
||||
messages: [{ role: "system", content: REPORT_SYSTEM_PROMPT(openuiPrompt) },
|
||||
{ role: "user", content: JSON.stringify(payload) }] }),
|
||||
}).catch((e) => { throw new AnalystError(`Could not reach ${cfg.baseUrl}: ${(e as Error).message}`); });
|
||||
if (!res.ok || !res.body) throw new AnalystError(`Analyst model returned HTTP ${res.status} while writing the report.`);
|
||||
yield* openAiTextDeltas(res);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user