mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-15 19:32:20 +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 (7163081f5 and 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);
|
|
});
|