mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 14:22:09 +03:00
Three independent root causes in the TS 7 executor slice (#8484), each a declaration that had drifted behind the code using it. All type-only — no behavior change, verified by running the new tests against the parent commit's sources (7/7 pass there too). mimocode — AccountState was missing `proxy`, but syncAccountsFromCredentials() writes it on every account and getProxyDispatcher() reads it. The #3837/#5521 contract ("always present, null when unconfigured") lived only in a comment. Declared as AccountProxyConfig["proxy"] so the two stay in lockstep. (4) opencode — the tools-truncation block narrowed `modifiedBody` with `typeof === "object"` but, unlike the client_metadata block directly above it, omitted `!Array.isArray()` and the Record cast, so `.tools` was unreachable on `object`. Adopted the neighbouring block's idiom. Behavior is identical: the old code reached `.tools` on arrays too and relied on Array.isArray(undefined) short-circuiting. (4) zed-hosted — enqueueSseObject/finish/processLine were annotated ReadableStreamDefaultController but are driven from a TransformStream, whose controller has no close(). All three only ever enqueue, so they are now typed by that single capability (SseEnqueueTarget). (3) 310 -> 299 diagnostics, zero new, on a line-number-agnostic diff of the full tsc error set. Tests: new tests/unit/ts7-executor-shared-shapes.test.ts pins the tools truncation (previously uncovered) and the proxy-always-present contract. The zed-hosted transform is already covered end-to-end by zed-hosted-think-close-marker.test.ts. 300/300 across the 26 affected files. Co-authored-by: backryun <busan011@ormbiz.co.kr>
146 lines
5.1 KiB
TypeScript
146 lines
5.1 KiB
TypeScript
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
const { OpencodeExecutor } = await import("../../open-sse/executors/opencode.ts");
|
|
const { MimocodeExecutor } = await import("../../open-sse/executors/mimocode.ts");
|
|
|
|
/**
|
|
* Behavioral guards for the three type-only fixes in the TS 7 executor slice
|
|
* (see #8484). Each fix restored a type the code already depended on at runtime;
|
|
* these tests pin the runtime contracts so a future "simplification" of the
|
|
* annotations cannot silently change behavior.
|
|
*
|
|
* The zed-hosted `SseEnqueueTarget` fix is already covered end-to-end by
|
|
* `zed-hosted-think-close-marker.test.ts`, which drives the same TransformStream
|
|
* that failed to type-check — no duplicate added here.
|
|
*/
|
|
|
|
describe("OpencodeExecutor — tools truncation survives the narrowing fix", () => {
|
|
const executor = new OpencodeExecutor("opencode-go");
|
|
const CREDENTIALS = { apiKey: "k" } as Record<string, unknown>;
|
|
|
|
const tools = (n: number) =>
|
|
Array.from({ length: n }, (_, i) => ({
|
|
type: "function",
|
|
function: { name: `tool_${i}`, parameters: {} },
|
|
}));
|
|
|
|
function bodyWith(toolCount: number) {
|
|
return {
|
|
model: "oc/kimi-k2.6",
|
|
stream: true,
|
|
messages: [{ role: "user", content: "hi" }],
|
|
tools: tools(toolCount),
|
|
};
|
|
}
|
|
|
|
it("truncates an over-long tools array to 128 entries", () => {
|
|
const out = executor.transformRequest("oc/kimi-k2.6", bodyWith(200), true, CREDENTIALS) as {
|
|
tools: unknown[];
|
|
};
|
|
assert.equal(out.tools.length, 128, "upstream rejects more than 128 tools");
|
|
assert.deepEqual(
|
|
(out.tools[127] as { function: { name: string } }).function.name,
|
|
"tool_127",
|
|
"truncation keeps the first 128 in order, not an arbitrary slice"
|
|
);
|
|
});
|
|
|
|
it("leaves a within-limit tools array untouched", () => {
|
|
const out = executor.transformRequest("oc/kimi-k2.6", bodyWith(10), true, CREDENTIALS) as {
|
|
tools: unknown[];
|
|
};
|
|
assert.equal(out.tools.length, 10);
|
|
});
|
|
|
|
it("is a no-op when the body carries no tools", () => {
|
|
const body = {
|
|
model: "oc/kimi-k2.6",
|
|
stream: true,
|
|
messages: [{ role: "user", content: "hi" }],
|
|
};
|
|
const out = executor.transformRequest("oc/kimi-k2.6", body, true, CREDENTIALS) as Record<
|
|
string,
|
|
unknown
|
|
>;
|
|
assert.equal("tools" in out, false);
|
|
assert.ok(Array.isArray(out.messages), "messages preserved");
|
|
});
|
|
|
|
it("leaves an array-shaped body alone (pins the !Array.isArray guard)", () => {
|
|
// The pre-fix condition reached `.tools` on any object, arrays included, and
|
|
// relied on `Array.isArray(undefined)` short-circuiting. The explicit
|
|
// !Array.isArray() guard must keep that outcome identical.
|
|
const arrayBody = [{ role: "user", content: "hi" }] as unknown as Record<string, unknown>;
|
|
const out = executor.transformRequest("oc/kimi-k2.6", arrayBody, true, CREDENTIALS);
|
|
assert.ok(Array.isArray(out), "array body must pass through as an array");
|
|
assert.equal((out as unknown[]).length, 1);
|
|
});
|
|
});
|
|
|
|
describe("MimocodeExecutor — AccountState.proxy is always present (#3837/#5521)", () => {
|
|
const FP = "fingerprint-1";
|
|
|
|
function accountsOf(exec: unknown): Array<Record<string, unknown>> {
|
|
return (exec as { accounts: Array<Record<string, unknown>> }).accounts;
|
|
}
|
|
|
|
function sync(exec: unknown, credentials: unknown): void {
|
|
(exec as { syncAccountsFromCredentials(c: unknown): void }).syncAccountsFromCredentials(
|
|
credentials
|
|
);
|
|
}
|
|
|
|
it("defaults proxy to null — not undefined — when no accountProxies are configured", () => {
|
|
const exec = new MimocodeExecutor();
|
|
accountsOf(exec).length = 0;
|
|
accountsOf(exec).push({
|
|
fingerprint: FP,
|
|
jwt: "",
|
|
expiresAt: 0,
|
|
cooldownUntil: 0,
|
|
consecutiveFails: 0,
|
|
});
|
|
|
|
sync(exec, { providerSpecificData: {} });
|
|
|
|
const account = accountsOf(exec)[0];
|
|
assert.ok("proxy" in account, "every account must expose a proxy key");
|
|
assert.equal(account.proxy, null, "unconfigured proxy is null, never undefined");
|
|
});
|
|
|
|
it("resolves a configured proxy onto the matching account", () => {
|
|
const exec = new MimocodeExecutor();
|
|
accountsOf(exec).length = 0;
|
|
accountsOf(exec).push({
|
|
fingerprint: FP,
|
|
jwt: "",
|
|
expiresAt: 0,
|
|
cooldownUntil: 0,
|
|
consecutiveFails: 0,
|
|
});
|
|
|
|
const proxy = { type: "http", host: "p1.example.com", port: 1080 };
|
|
sync(exec, { providerSpecificData: { accountProxies: [{ fingerprint: FP, proxy }] } });
|
|
|
|
assert.deepEqual(accountsOf(exec)[0].proxy, proxy);
|
|
});
|
|
|
|
it("clears a previously-resolved proxy back to null when config drops it", () => {
|
|
const exec = new MimocodeExecutor();
|
|
accountsOf(exec).length = 0;
|
|
accountsOf(exec).push({
|
|
fingerprint: FP,
|
|
jwt: "",
|
|
expiresAt: 0,
|
|
cooldownUntil: 0,
|
|
consecutiveFails: 0,
|
|
proxy: { type: "http", host: "stale.example.com", port: 8080 },
|
|
});
|
|
|
|
sync(exec, { providerSpecificData: { accountProxies: [] } });
|
|
|
|
assert.equal(accountsOf(exec)[0].proxy, null, "a removed proxy must not linger on the account");
|
|
});
|
|
});
|