mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-17 20:52:15 +03:00
* fix(executors): route Claude-via-Vertex through native rawPredict with real streaming Claude models on Vertex AI were being sent through the generic OpenAI- compatible partner endpoint, which 404s/errors for Claude on at least some projects. Route them through Vertex's native Anthropic Messages API (publishers/anthropic/.../rawPredict) instead, stripping the body-level model field rawPredict rejects and injecting the required anthropic_version field. rawPredict only ever returns a complete JSON body, never real SSE framing, so streaming requests now get a genuine Anthropic-format SSE stream synthesized from that JSON (message_start/content_block_*/ message_delta/message_stop), which the existing claude-to-openai response translator already knows how to parse. Also fixes two response-format resolution bugs that silently dropped a custom model's DB-stored targetFormat override whenever the model id also existed in the static provider registry (as claude-sonnet-4-6 and claude-opus-4-7 do under vertex): resolveModelOrError had its own ad-hoc resolution that never consulted the override, and even once fixed, executeChatWithBreaker discarded the correctly-resolved format before handleChatCore's own resolution ran a second time. * docs: add changelog fragment for #8909 * refactor(sse): extract shared Claude effort-model predicate * fix(sse): strip Claude effort-suffix ids for any provider serving a real Claude model * fix(sse): keep no-think and CC-discovery catalog variant roots unprefixed * fix(dashboard): re-qualify no-think playground model ids correctly * fix(sse): scope Vertex 404s to a per-model lockout via passthroughModels * docs: add changelog fragment for the Claude catalog/dispatch fix * fix(sse): align regex naming and changelog formatting * fix(sse): clarify effort-variant strip comment and add cross-module drift guard * fix(sse): disambiguate Vertex connection-wide vs per-model 403s * docs: document Vertex 403 disambiguation in changelog fragment * fix(sse): correlate reason and resource within the same ErrorInfo detail * fix(sse): extract Vertex error classifier and rebaseline frozen file sizes * test: register vertex-passthrough-model-lockout in stryker tap.testFiles * fix(sse): reconciles rebase-onto-tip drift for 9006 Two categories of inherited base-branch breakage surfaced when rebasing onto release/v3.8.50's latest tip, both confirmed unrelated to this PR's own diff: - check:file-size: base.ts and chat.ts drifted further past their frozen caps via already-merged commits (7163081f5and others) that didn't rebaseline after growing them. Documented and bumped in file-size-baseline.json. - chat-helpers.test.ts: two gpt-5.5 routing assertions predate #9275 (fix(routing): bare model ids route to codex first), which deliberately made gpt-5.5 route to codex unconditionally, regardless of which other providers are active. Confirmed via #9275's own commit message and code comments this is intentional, not a regression; verified reproducible on the raw base tip alone, with no changes from this PR involved. Updated both assertions and their names to match the new, intentional default. * ci: re-trigger checks after GitHub Actions incident (2026-08-07, resolved) * ci: re-trigger checks (previous push event was dropped) * fix(quality): rebaseline combo-routing-engine.test.ts own-comment growth The ALL_ACCOUNTS_INACTIVE->ALL_TARGETS_SKIPPED fix (a32aed738) added explanatory comments (+7 lines), pushing the file past its frozen 3457 cap. CI's PR-mode check:file-size caught it; local check-file-size.mjs was not re-run after that specific commit.
169 lines
7.1 KiB
TypeScript
169 lines
7.1 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
CC_DISCOVERY_PREFIX,
|
|
CC_DISCOVERY_COMBO_PREFIX,
|
|
appendCcDiscoveryAliases,
|
|
} from "../../open-sse/utils/ccDiscoveryAliases.ts";
|
|
|
|
interface CatalogEntry {
|
|
id: string;
|
|
owned_by?: string;
|
|
name?: string;
|
|
root?: string;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
const alwaysEnabled = (): boolean => true;
|
|
|
|
test("constants match the discovery prefixes", () => {
|
|
assert.equal(CC_DISCOVERY_PREFIX, "claude/");
|
|
assert.equal(CC_DISCOVERY_COMBO_PREFIX, "claude/combo/");
|
|
});
|
|
|
|
test("adds a claude/ mirror with display_name and root for an eligible model", () => {
|
|
const models: CatalogEntry[] = [{ id: "kimi/kimi-k2.6", owned_by: "kimi", name: "Kimi K2.6" }];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
|
|
assert.equal(out.length, 2);
|
|
assert.deepEqual(out[0], models[0]);
|
|
const alias = out[1];
|
|
assert.equal(alias.id, "claude/kimi/kimi-k2.6");
|
|
assert.equal(alias.root, "kimi-k2.6");
|
|
assert.equal(alias.display_name, "Kimi K2.6 (OmniRoute)");
|
|
assert.equal(alias.owned_by, "kimi");
|
|
});
|
|
|
|
test("keeps root bare even when the original id carries a provider prefix", () => {
|
|
const models: CatalogEntry[] = [
|
|
{ id: "vertex/claude-sonnet-5", owned_by: "vertex", name: "Claude Sonnet 5 (Vertex)" },
|
|
];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
const alias = out.find((m) => m.id === "claude/vertex/claude-sonnet-5");
|
|
assert.ok(alias, "mirror entry with the fully-qualified id must exist");
|
|
assert.equal(
|
|
alias!.root,
|
|
"claude-sonnet-5",
|
|
"root must be bare, matching the no-think/effort-variant convention"
|
|
);
|
|
});
|
|
|
|
test("falls back to the id for display_name when name is missing", () => {
|
|
const models: CatalogEntry[] = [{ id: "kimi/kimi-k2.6" }];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
assert.equal(out[1].display_name, "kimi/kimi-k2.6 (OmniRoute)");
|
|
});
|
|
|
|
test("never re-mirrors ids that already start with claude or anthropic", () => {
|
|
const models: CatalogEntry[] = [
|
|
{ id: "claude/claude-fable-5", owned_by: "claude" },
|
|
{ id: "anthropic/claude-opus-4-8", owned_by: "anthropic" },
|
|
{ id: "claudeish/not-actually-claude", owned_by: "claudeish" },
|
|
];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
// "claudeish/..." does not match the anchored (claude|anthropic)(/|$) pattern,
|
|
// so it is still eligible for mirroring — only exact claude/anthropic prefixes are excluded.
|
|
assert.equal(out.length, models.length + 1);
|
|
assert.equal(out[out.length - 1].id, "claude/claudeish/not-actually-claude");
|
|
});
|
|
|
|
test("never aliases no-think/ ids or effort-suffixed ids", () => {
|
|
const models: CatalogEntry[] = [
|
|
{ id: "no-think/claude/claude-fable-5", owned_by: "claude" },
|
|
{ id: "claude/claude-fable-5-high", owned_by: "claude" },
|
|
{ id: "claude/claude-fable-5-xhigh", owned_by: "claude" },
|
|
{ id: "kimi/kimi-k2.6-medium", owned_by: "kimi" },
|
|
];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
assert.equal(out, models);
|
|
});
|
|
|
|
test("mirrors combo entries under claude/combo/", () => {
|
|
const models: CatalogEntry[] = [
|
|
{ id: "custo-otimizado", owned_by: "combo", name: "Custo Otimizado" },
|
|
];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
assert.equal(out.length, 2);
|
|
assert.equal(out[1].id, "claude/combo/custo-otimizado");
|
|
assert.equal(out[1].root, "custo-otimizado");
|
|
assert.equal(out[1].display_name, "Custo Otimizado (OmniRoute)");
|
|
});
|
|
|
|
test("mirrors combo names containing spaces (comboNameSchema allows them)", () => {
|
|
const models: CatalogEntry[] = [
|
|
{ id: "Custo Otimizado BR", owned_by: "combo", name: "Custo Otimizado BR" },
|
|
];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
assert.equal(out.length, 2);
|
|
assert.equal(out[1].id, "claude/combo/Custo Otimizado BR");
|
|
assert.equal(out[1].root, "Custo Otimizado BR");
|
|
});
|
|
|
|
test("keeps a combo's root the full name verbatim when the combo name contains a slash", () => {
|
|
// comboNameSchema (src/shared/validation/schemas/combo.ts) explicitly allows "/" in
|
|
// combo names, so bareModelName must NOT be applied to combo entries — only to real
|
|
// provider-qualified model ids.
|
|
const models: CatalogEntry[] = [{ id: "Team/Alpha", owned_by: "combo", name: "Team/Alpha" }];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
assert.equal(out.length, 2);
|
|
assert.equal(out[1].id, "claude/combo/Team/Alpha");
|
|
assert.equal(out[1].root, "Team/Alpha", "root must be the full combo name, not truncated");
|
|
});
|
|
|
|
test("skips disabled entries and returns the same array reference when nothing is eligible", () => {
|
|
const models: CatalogEntry[] = [{ id: "kimi/kimi-k2.6", owned_by: "kimi" }];
|
|
const out = appendCcDiscoveryAliases(models, () => false);
|
|
assert.equal(out, models);
|
|
});
|
|
|
|
test("does NOT mirror built-in auto/* combos (request path can't resolve them)", () => {
|
|
// Built-in auto combos are synthesized by createBuiltinAutoCombo, not stored in
|
|
// the DB combos table, so getComboByName() misses them at request time — mirroring
|
|
// them would advertise an id the request path rejects. See ccDiscoveryAliasResolve.
|
|
const models: CatalogEntry[] = [
|
|
{ id: "auto", owned_by: "combo", name: "Auto" },
|
|
{ id: "auto/glm", owned_by: "combo", name: "Auto GLM" },
|
|
{ id: "auto/pro:pro", owned_by: "combo", name: "Auto Pro" },
|
|
{ id: "real-combo", owned_by: "combo", name: "Real Combo" },
|
|
];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
const aliasIds = out.filter((m) => String(m.id).startsWith("claude/")).map((m) => m.id);
|
|
assert.deepEqual(aliasIds, ["claude/combo/real-combo"]);
|
|
});
|
|
|
|
test("returns the same array reference when the input is empty", () => {
|
|
const models: CatalogEntry[] = [];
|
|
const out = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
assert.equal(out, models);
|
|
});
|
|
|
|
test("idempotent: running twice never double-prefixes (alias entries are skipped as already-claude)", () => {
|
|
const models: CatalogEntry[] = [
|
|
{ id: "kimi/kimi-k2.6", owned_by: "kimi", name: "Kimi K2.6" },
|
|
{ id: "custo-otimizado", owned_by: "combo", name: "Custo Otimizado" },
|
|
];
|
|
const once = appendCcDiscoveryAliases(models, alwaysEnabled);
|
|
const twice = appendCcDiscoveryAliases(once, alwaysEnabled);
|
|
|
|
// The alias entries synthesized in `once` already start with "claude/", so the
|
|
// second pass's ALREADY_CLAUDE_RE guard skips them — only the still-present
|
|
// original entries are (re-)mirrored. Critically, no id is ever double-prefixed.
|
|
const doublePrefixed = twice.filter(
|
|
(m) => m.id.startsWith("claude/claude/") || m.id.startsWith("claude/combo/combo/")
|
|
);
|
|
assert.equal(doublePrefixed.length, 0);
|
|
|
|
// Every alias id that resulted from re-processing `once` is a stable mirror of an
|
|
// original entry, never a mirror of an already-mirrored one.
|
|
const aliasIds = twice.filter((m) => m.id.startsWith(CC_DISCOVERY_PREFIX)).map((m) => m.id);
|
|
for (const id of aliasIds) {
|
|
assert.equal(id.startsWith("claude/claude/"), false);
|
|
}
|
|
});
|
|
|
|
test("non-array input is returned unchanged", () => {
|
|
const notAnArray = null as unknown as CatalogEntry[];
|
|
assert.equal(appendCcDiscoveryAliases(notAnArray, alwaysEnabled), notAnArray);
|
|
});
|