mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 02:42:24 +03:00
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host. Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean. Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
158 lines
6.5 KiB
TypeScript
158 lines
6.5 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
const { buildZaiStreamingBody, parseZaiFrame, collectZaiNonStreaming } = await import(
|
|
"../../open-sse/executors/zai-web/stream.ts"
|
|
);
|
|
|
|
/**
|
|
* Hard Rule #6 — "never silently swallow errors in SSE streams".
|
|
*
|
|
* HTTP-level failures are already handled: `fetchUpstream` turns any `!ok`
|
|
* response into a `makeErrorResult` with the sanitized body. The gap is a
|
|
* **200 whose SSE body carries an error payload** — `parseZaiFrame` returns
|
|
* null for it, `drainSseDeltas` drops it, and the stream closes with an empty
|
|
* assistant message + stop + [DONE]. The caller sees a successful empty
|
|
* completion: HTTP 200, `out=0`, "complete". That is the shape reported on
|
|
* #8451, and it makes a rejected signature, an expired captcha and a stale
|
|
* token all look identical.
|
|
*
|
|
* Scope note: returning null for a *contentless* frame is deliberate and
|
|
* live-validated — z.ai sends phase frames with no `delta_content`, and
|
|
* `executor-zai-web.test.ts` pins that ("returns null for frames with no usable
|
|
* delta"). So this only adds recognition of affirmatively error-shaped frames;
|
|
* "nothing parseable arrived" is left alone, because on this protocol that is
|
|
* not by itself evidence of failure.
|
|
*/
|
|
|
|
function sseStream(...frames: string[]): ReadableStream<Uint8Array> {
|
|
const enc = new TextEncoder();
|
|
return new ReadableStream({
|
|
start(c) {
|
|
for (const f of frames) c.enqueue(enc.encode(`data: ${f}\n\n`));
|
|
c.close();
|
|
},
|
|
});
|
|
}
|
|
|
|
async function readAll(stream: ReadableStream): Promise<string> {
|
|
const reader = stream.getReader();
|
|
const dec = new TextDecoder();
|
|
let out = "";
|
|
for (;;) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
out += dec.decode(value as Uint8Array, { stream: true });
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const emitChunk = (
|
|
controller: ReadableStreamDefaultController,
|
|
delta: Record<string, unknown>,
|
|
finish?: string
|
|
) => {
|
|
const payload = JSON.stringify({
|
|
choices: [{ index: 0, delta, finish_reason: finish ?? null }],
|
|
});
|
|
controller.enqueue(new TextEncoder().encode(`data: ${payload}\n\n`));
|
|
};
|
|
|
|
const contentOf = (sse: string) =>
|
|
[...sse.matchAll(/"content":"([^"]*)"/g)].map((m) => m[1]).join("");
|
|
|
|
test("parseZaiFrame classifies an error-shaped frame instead of discarding it", () => {
|
|
assert.equal(parseZaiFrame({ error: "captcha expired" })?.error, "captcha expired");
|
|
assert.equal(
|
|
parseZaiFrame({ error: { detail: "signature invalid" } })?.error,
|
|
"signature invalid"
|
|
);
|
|
assert.equal(
|
|
parseZaiFrame({ data: { error: { message: "token expired" } } })?.error,
|
|
"token expired"
|
|
);
|
|
});
|
|
|
|
test("an error frame is terminal", () => {
|
|
assert.equal(parseZaiFrame({ error: "nope" })?.done, true);
|
|
});
|
|
|
|
test("REGRESSION GUARD: contentless frames are still skipped, not reported as errors", () => {
|
|
// Live-validated behaviour — z.ai emits phase frames with no delta_content.
|
|
// Pinned by executor-zai-web.test.ts; re-asserted here because the error path
|
|
// added below runs in the same function.
|
|
assert.equal(parseZaiFrame({ data: { phase: "answer" } }), null);
|
|
assert.equal(parseZaiFrame({ type: "chat:completion", data: { phase: "thinking" } }), null);
|
|
assert.equal(parseZaiFrame({}), null);
|
|
assert.equal(parseZaiFrame(null), null);
|
|
assert.equal(parseZaiFrame("not-an-object"), null);
|
|
});
|
|
|
|
test("a 200 stream carrying an error frame surfaces it instead of finishing empty", async () => {
|
|
const upstream = sseStream(JSON.stringify({ error: { detail: "signature invalid" } }));
|
|
const out = await readAll(buildZaiStreamingBody(upstream, emitChunk, null));
|
|
|
|
assert.match(contentOf(out), /signature invalid/, "the upstream's diagnosis must reach the caller");
|
|
assert.match(contentOf(out), /\[Z\.ai error\]/, "tagged like the other web executors");
|
|
assert.ok(out.includes('"finish_reason":"stop"'));
|
|
assert.ok(out.includes("[DONE]"), "the stream still terminates cleanly for the client");
|
|
});
|
|
|
|
test("an error frame after partial content still surfaces, keeping what was streamed", async () => {
|
|
const upstream = sseStream(
|
|
JSON.stringify({ type: "chat:completion", data: { delta_content: "partial", phase: "answer" } }),
|
|
JSON.stringify({ error: "stream aborted upstream" })
|
|
);
|
|
const out = await readAll(buildZaiStreamingBody(upstream, emitChunk, null));
|
|
|
|
assert.match(contentOf(out), /partial/, "already-streamed content is preserved");
|
|
assert.match(contentOf(out), /stream aborted upstream/, "and the failure is appended, not dropped");
|
|
});
|
|
|
|
test("control: a well-formed stream is untouched", async () => {
|
|
const upstream = sseStream(
|
|
JSON.stringify({ type: "chat:completion", data: { delta_content: "hello", phase: "answer" } }),
|
|
JSON.stringify({ type: "chat:completion", data: { phase: "done", done: true } })
|
|
);
|
|
const out = await readAll(buildZaiStreamingBody(upstream, emitChunk, null));
|
|
|
|
assert.equal(contentOf(out), "hello");
|
|
assert.ok(!out.includes("[Z.ai error]"), "the happy path must stay clean");
|
|
});
|
|
|
|
test("control: a phase-only stream is not turned into an error", async () => {
|
|
// The exact case the deliberate-null design exists for.
|
|
const upstream = sseStream(
|
|
JSON.stringify({ type: "chat:completion", data: { phase: "answer" } }),
|
|
JSON.stringify({ type: "chat:completion", data: { delta_content: "hi", phase: "answer" } }),
|
|
JSON.stringify({ type: "chat:completion", data: { phase: "done", done: true } })
|
|
);
|
|
const out = await readAll(buildZaiStreamingBody(upstream, emitChunk, null));
|
|
|
|
assert.equal(contentOf(out), "hi");
|
|
assert.ok(!out.includes("[Z.ai error]"));
|
|
});
|
|
|
|
// ── Non-streaming path (collectZaiNonStreaming) ───────────────────────────────
|
|
|
|
test("collectZaiNonStreaming rejects on an error frame instead of returning empty", async () => {
|
|
const upstream = sseStream(JSON.stringify({ error: { detail: "captcha expired" } }));
|
|
await assert.rejects(
|
|
() => collectZaiNonStreaming(upstream),
|
|
(err: Error) => {
|
|
assert.match(err.message, /captcha expired/);
|
|
return true;
|
|
}
|
|
);
|
|
});
|
|
|
|
test("collectZaiNonStreaming returns content when no error frame is present", async () => {
|
|
const upstream = sseStream(
|
|
JSON.stringify({ type: "chat:completion", data: { delta_content: "hello", phase: "answer" } }),
|
|
JSON.stringify({ type: "chat:completion", data: { phase: "done", done: true } })
|
|
);
|
|
const result = await collectZaiNonStreaming(upstream);
|
|
assert.equal(result.answer, "hello");
|
|
assert.equal(result.reasoning, "");
|
|
});
|