Files
OmniRoute/tests/unit/cc-discovery-aliases-append.test.ts
Will Gordon 4795825513 fix(sse): make Claude effort/no-think catalog variants dispatchable on every provider (#9006)
* 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.
2026-08-11 10:02:48 -03:00

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);
});