mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(providers): correct Kiro model catalog to real upstream ids (#6170)
* fix(providers): correct Kiro model catalog to real upstream ids
Kiro's API (generateAssistantResponse) returns 400 "Invalid model. Please
select a different model" for any id it does not recognize. The registry
exposed fabricated ids (copied from OmniRoute's own Anthropic catalog) that
Kiro never serves, so every call to them 400'd. Live-verified on the VPS:
Removed (400 Invalid model):
- auto-kiro (no "auto" model id — was sent verbatim upstream)
- claude-fable-5 (Kiro offers no Fable)
- claude-opus-4.8/4.7/4.6 (Kiro offers no Opus)
Corrected:
- claude-sonnet-4.6 -> claude-sonnet-4.5 (Kiro's Sonnet is 4.5; 4.5 -> 200)
Kept:
- claude-sonnet-5 (real Kiro model, plan-gated per account)
- claude-haiku-4.5, deepseek-3.2, glm-5, minimax-m2.5/m2.1,
qwen3-coder-next (all proven 200 on the VPS)
Aligns the free-model catalog and drops the orphaned auto-kiro price key.
Regression guard: tests/unit/kiro-catalog-real-models.test.ts (3/3).
Kiro cluster #6112/#6113/#6099.
* test(providers): align stale Kiro-catalog tests to the corrected upstream ids
The fabricated Kiro ids removed in the parent commit (claude-fable-5,
claude-opus-4.8/4.7/4.6, claude-sonnet-4.6) were still asserted as present by
three pre-existing tests, which encoded the bug:
- catalog-updates-v3x: now asserts Kiro does NOT expose Fable 5 / Opus (kept the
legit cc exposure) and guards the real claude-sonnet-4.5 pricing.
- model-family-fallback-notation: the dot-notation example moves from kiro/ to
anthropic/ (which genuinely serves Opus/Fable in dot notation) — coverage kept.
- provider-models-route: the Kiro local-catalog assertion now expects the real
Sonnet 5 / Sonnet 4.5 set and negatively guards the fabricated ids.
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
This commit is contained in:
committed by
GitHub
parent
f2ad9b23bd
commit
b3a2cfe0ea
@@ -245,11 +245,7 @@ export const FREE_MODEL_BUDGETS: FreeModelBudget[] = [
|
||||
{ provider: "kilo-gateway", modelId: "nvidia/nemotron-3-ultra-550b-a55b:free", displayName: "NVIDIA Nemotron 3 Ultra (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "kilo-gateway-free", tos: "caution" },
|
||||
{ provider: "kilo-gateway", modelId: "nvidia/nemotron-3-super-120b-a12b:free", displayName: "NVIDIA Nemotron 3 Super (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "kilo-gateway-free", tos: "caution" },
|
||||
{ provider: "kilo-gateway", modelId: "nex-agi/nex-n2-pro:free", displayName: "Nex-N2-Pro (free)", monthlyTokens: 0, creditTokens: 0, freeType: "recurring-uncapped", poolKey: "kilo-gateway-free", tos: "caution" },
|
||||
{ provider: "kiro", modelId: "auto-kiro", displayName: "Auto (Kiro picks best model)", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
{ provider: "kiro", modelId: "claude-opus-4.8", displayName: "Claude Opus 4.8", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
{ provider: "kiro", modelId: "claude-opus-4.7", displayName: "Claude Opus 4.7", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
{ provider: "kiro", modelId: "claude-opus-4.6", displayName: "Claude Opus 4.6", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
{ provider: "kiro", modelId: "claude-sonnet-4.6", displayName: "Claude Sonnet 4.6", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
{ provider: "kiro", modelId: "claude-sonnet-4.5", displayName: "Claude Sonnet 4.5", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
{ provider: "kiro", modelId: "claude-haiku-4.5", displayName: "Claude Haiku 4.5", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
{ provider: "kiro", modelId: "deepseek-3.2", displayName: "DeepSeek V3.2", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
{ provider: "kiro", modelId: "minimax-m2.5", displayName: "MiniMax M2.5", monthlyTokens: 25000, creditTokens: 0, freeType: "recurring-monthly", poolKey: "kiro", tos: "avoid" },
|
||||
|
||||
@@ -15,32 +15,14 @@ export const kiroProvider: RegistryEntry = {
|
||||
tokenUrl: "https://prod.us-east-1.auth.desktop.kiro.dev/refreshToken",
|
||||
authUrl: "https://prod.us-east-1.auth.desktop.kiro.dev",
|
||||
},
|
||||
// Model IDs must match Kiro's real upstream catalog exactly — an unknown id
|
||||
// makes Kiro return `400 "Invalid model. Please select a different model"`.
|
||||
// Fabricated ids (auto-kiro, claude-opus-4.x, claude-fable-5, claude-sonnet-4.6)
|
||||
// were removed after live VPS validation: Kiro offers no Opus/Fable, its Sonnet
|
||||
// is 4.5 (not 4.6), and there is no "auto" model id (it was sent verbatim and
|
||||
// 400'd). claude-sonnet-5 is a real Kiro model but plan-gated per account —
|
||||
// kept so entitled accounts can use it. See kiro cluster #6112/#6113/#6099.
|
||||
models: [
|
||||
{ id: "auto-kiro", name: "Auto (Kiro picks best model)" },
|
||||
{
|
||||
id: "claude-fable-5",
|
||||
name: "Claude Fable 5",
|
||||
contextLength: 1000000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
id: "claude-opus-4.8",
|
||||
name: "Claude Opus 4.8",
|
||||
contextLength: 1000000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
id: "claude-opus-4.7",
|
||||
name: "Claude Opus 4.7",
|
||||
contextLength: 1000000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
id: "claude-opus-4.6",
|
||||
name: "Claude Opus 4.6",
|
||||
contextLength: 1000000,
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
id: "claude-sonnet-5",
|
||||
name: "Claude Sonnet 5",
|
||||
@@ -48,8 +30,8 @@ export const kiroProvider: RegistryEntry = {
|
||||
maxOutputTokens: 128000,
|
||||
},
|
||||
{
|
||||
id: "claude-sonnet-4.6",
|
||||
name: "Claude Sonnet 4.6",
|
||||
id: "claude-sonnet-4.5",
|
||||
name: "Claude Sonnet 4.5",
|
||||
contextLength: 200000,
|
||||
maxOutputTokens: 64000,
|
||||
},
|
||||
|
||||
@@ -57,9 +57,9 @@ function convertMessages(messages, tools, model) {
|
||||
let toolsAttached = false;
|
||||
|
||||
// Only Claude models support images in Kiro. Kiro also routes non-Claude
|
||||
// models (deepseek, minimax, glm, qwen3-coder-next, auto-kiro) that do not
|
||||
// accept image attachments — gate image extraction behind a Claude check so
|
||||
// we never attach images those models would reject.
|
||||
// models (deepseek, minimax, glm, qwen3-coder-next) that do not accept image
|
||||
// attachments — gate image extraction behind a Claude check so we never
|
||||
// attach images those models would reject.
|
||||
const supportsImages = typeof model === "string" && model.toLowerCase().includes("claude");
|
||||
|
||||
const flushPending = () => {
|
||||
|
||||
@@ -584,7 +584,10 @@ export const DEFAULT_PRICING_OAUTH = {
|
||||
reasoning: 8.0,
|
||||
cache_creation: 2.0,
|
||||
},
|
||||
// Kiro "Auto" model — routes to best available
|
||||
// Kiro "Auto" pricing — retained as a fallback price for any legacy "auto"
|
||||
// reference. The "auto-kiro" registry model was removed (Kiro's API has no
|
||||
// "auto" model id — it 400'd "Invalid model"), so its dedicated price key
|
||||
// was dropped with it. See kiro cluster #6112/#6113/#6099.
|
||||
auto: {
|
||||
input: 3.0,
|
||||
output: 15.0,
|
||||
@@ -592,13 +595,5 @@ export const DEFAULT_PRICING_OAUTH = {
|
||||
reasoning: 15.0,
|
||||
cache_creation: 3.0,
|
||||
},
|
||||
// Registry exposes the Auto model as id "auto-kiro" — keep both keys priced.
|
||||
"auto-kiro": {
|
||||
input: 3.0,
|
||||
output: 15.0,
|
||||
cached: 1.5,
|
||||
reasoning: 15.0,
|
||||
cache_creation: 3.0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -47,23 +47,19 @@ test("NVIDIA catalog includes the verified 2026 additions and GPT OSS 20B alias
|
||||
});
|
||||
});
|
||||
|
||||
test("Fable 5 catalog exposes claude-fable-5 in cc and kiro providers with matching pricing", () => {
|
||||
test("Fable 5 catalog exposes claude-fable-5 in cc — but NOT via Kiro (fabricated)", () => {
|
||||
// claude-fable-5 is a real Claude flagship served by the Claude Code (cc) channel,
|
||||
// but Kiro's upstream never served it — it had been copied verbatim into the Kiro
|
||||
// registry from OmniRoute's own Anthropic catalog and returned upstream 400
|
||||
// "Invalid model". #6170 removed the fabricated Kiro copy.
|
||||
const ccIds = new Set(getModelsByProviderId("cc").map((m) => m.id));
|
||||
assert.ok(ccIds.has("claude-fable-5"), "cc must expose claude-fable-5");
|
||||
|
||||
const kiroModels = getModelsByProviderId("kiro");
|
||||
const kiroIds = new Set(kiroModels.map((m) => m.id));
|
||||
assert.ok(kiroIds.has("claude-fable-5"), "kiro must expose claude-fable-5");
|
||||
|
||||
const fable = kiroModels.find((m) => m.id === "claude-fable-5");
|
||||
assert.equal(fable?.contextLength, 1000000);
|
||||
assert.equal(fable?.maxOutputTokens, 128000);
|
||||
|
||||
const ccPricing = (DEFAULT_PRICING as Record<string, Record<string, unknown>>).cc;
|
||||
assert.ok(ccPricing["claude-fable-5"], "cc pricing must include claude-fable-5");
|
||||
|
||||
const kiroPricing = (DEFAULT_PRICING as Record<string, Record<string, unknown>>).kiro;
|
||||
assert.ok(kiroPricing["claude-fable-5"], "kiro pricing must include claude-fable-5");
|
||||
const kiroIds = new Set(getModelsByProviderId("kiro").map((m) => m.id));
|
||||
assert.equal(kiroIds.has("claude-fable-5"), false, "kiro must NOT expose claude-fable-5 (fabricated)");
|
||||
});
|
||||
|
||||
test("Sonnet 5 catalog exposes claude-sonnet-5 across cc/kiro/anthropic/blackbox with Sonnet-tier pricing", () => {
|
||||
@@ -89,20 +85,15 @@ test("Sonnet 5 catalog exposes claude-sonnet-5 across cc/kiro/anthropic/blackbox
|
||||
assert.equal(kiroSonnet5Price.output, 15.0);
|
||||
});
|
||||
|
||||
test("Kiro catalog exposes Claude Opus 4.8 alongside 4.7 with matching pricing", () => {
|
||||
const models = getModelsByProviderId("kiro");
|
||||
const ids = new Set(models.map((model) => model.id));
|
||||
test("Kiro catalog does NOT expose Claude Opus (fabricated — Kiro upstream has no Opus)", () => {
|
||||
// Kiro's real upstream never served any Opus model; the Opus 4.8/4.7/4.6 ids had been
|
||||
// copied into the Kiro registry from OmniRoute's Anthropic catalog and returned upstream
|
||||
// 400 "Invalid model. Please select a different model". #6170 removed them.
|
||||
const ids = new Set(getModelsByProviderId("kiro").map((model) => model.id));
|
||||
|
||||
assert.ok(ids.has("claude-opus-4.8"), "kiro must expose claude-opus-4.8");
|
||||
assert.ok(ids.has("claude-opus-4.7"), "kiro must still expose claude-opus-4.7");
|
||||
|
||||
const opus48 = models.find((model) => model.id === "claude-opus-4.8");
|
||||
assert.equal(opus48?.contextLength, 1000000);
|
||||
assert.equal(opus48?.maxOutputTokens, 128000);
|
||||
|
||||
// Pricing for the Kiro channel must cover the new model so usage cost is non-zero.
|
||||
const kiroPricing = (DEFAULT_PRICING as Record<string, Record<string, unknown>>).kiro;
|
||||
assert.ok(kiroPricing["claude-opus-4.8"], "kiro pricing must include claude-opus-4.8");
|
||||
assert.equal(ids.has("claude-opus-4.8"), false, "kiro must NOT expose claude-opus-4.8 (fabricated)");
|
||||
assert.equal(ids.has("claude-opus-4.7"), false, "kiro must NOT expose claude-opus-4.7 (fabricated)");
|
||||
assert.equal(ids.has("claude-opus-4.6"), false, "kiro must NOT expose claude-opus-4.6 (fabricated)");
|
||||
});
|
||||
|
||||
test("Every Kiro registry model resolves a non-zero pricing row (no $0.00 usage)", async () => {
|
||||
@@ -124,14 +115,15 @@ test("Every Kiro registry model resolves a non-zero pricing row (no $0.00 usage)
|
||||
);
|
||||
}
|
||||
|
||||
// Regression guard for the reported issue: Sonnet 4.6 must be priced like Sonnet 4.5.
|
||||
const sonnet46 = getPricingForModel("kiro", "claude-sonnet-4.6") as {
|
||||
// Regression guard: Kiro's real Sonnet is 4.5 (the "4.6" id was fabricated — #6170) and
|
||||
// must carry Sonnet-tier pricing ($3/$15).
|
||||
const sonnet45 = getPricingForModel("kiro", "claude-sonnet-4.5") as {
|
||||
input: number;
|
||||
output: number;
|
||||
} | null;
|
||||
assert.ok(sonnet46, "kiro pricing must include claude-sonnet-4.6");
|
||||
assert.equal(sonnet46?.input, 3.0);
|
||||
assert.equal(sonnet46?.output, 15.0);
|
||||
assert.ok(sonnet45, "kiro pricing must include claude-sonnet-4.5");
|
||||
assert.equal(sonnet45?.input, 3.0);
|
||||
assert.equal(sonnet45?.output, 15.0);
|
||||
});
|
||||
|
||||
test("Every OpenAI registry model resolves a non-zero pricing row (alias: openai)", async () => {
|
||||
|
||||
58
tests/unit/kiro-catalog-real-models.test.ts
Normal file
58
tests/unit/kiro-catalog-real-models.test.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { REGISTRY } from "@omniroute/open-sse/config/providers/index.ts";
|
||||
import { FREE_MODEL_BUDGETS } from "@omniroute/open-sse/config/freeModelCatalog.data.ts";
|
||||
|
||||
// Kiro's upstream (`generateAssistantResponse`) returns 400 "Invalid model.
|
||||
// Please select a different model" for any model id it does not recognize. The
|
||||
// registry must therefore expose ONLY ids Kiro actually serves. These ids were
|
||||
// fabricated (copied from OmniRoute's own Anthropic catalog) and live-verified
|
||||
// to 400 on the VPS — they must never reappear. Regression guard for the kiro
|
||||
// cluster (#6112/#6113/#6099).
|
||||
const FABRICATED_KIRO_IDS = [
|
||||
"auto-kiro", // no "auto" model id on Kiro — was sent verbatim and 400'd
|
||||
"claude-fable-5", // Kiro offers no Fable
|
||||
"claude-opus-4.8", // Kiro offers no Opus
|
||||
"claude-opus-4.7",
|
||||
"claude-opus-4.6",
|
||||
"claude-sonnet-4.6", // Kiro's Sonnet is 4.5, not 4.6
|
||||
];
|
||||
|
||||
// Ids proven to return 200 on the VPS (or a real, plan-gated Kiro model).
|
||||
const REAL_KIRO_IDS = [
|
||||
"claude-sonnet-5", // real model, plan-gated per account (kept)
|
||||
"claude-sonnet-4.5", // proven 200 (replaces the fabricated 4.6)
|
||||
"claude-haiku-4.5", // proven 200
|
||||
"deepseek-3.2", // proven 200
|
||||
"glm-5", // proven 200
|
||||
"minimax-m2.5", // proven 200
|
||||
"minimax-m2.1", // proven 200
|
||||
"qwen3-coder-next", // proven 200
|
||||
];
|
||||
|
||||
test("kiro registry exposes no fabricated model ids", () => {
|
||||
const ids = new Set((REGISTRY.kiro?.models || []).map((m) => m.id));
|
||||
for (const bad of FABRICATED_KIRO_IDS) {
|
||||
assert.ok(!ids.has(bad), `kiro registry must not expose fabricated id "${bad}"`);
|
||||
}
|
||||
});
|
||||
|
||||
test("kiro registry exposes exactly the real Kiro model ids", () => {
|
||||
const ids = (REGISTRY.kiro?.models || []).map((m) => m.id).sort();
|
||||
assert.deepEqual(ids, [...REAL_KIRO_IDS].sort());
|
||||
});
|
||||
|
||||
test("kiro free-model catalog carries no fabricated ids", () => {
|
||||
const kiroCatalogIds = new Set(
|
||||
FREE_MODEL_BUDGETS.filter((e) => e.provider === "kiro").map((e) => e.modelId)
|
||||
);
|
||||
for (const bad of FABRICATED_KIRO_IDS) {
|
||||
assert.ok(!kiroCatalogIds.has(bad), `free catalog must not list fabricated kiro id "${bad}"`);
|
||||
}
|
||||
// Every kiro free-catalog entry must exist in the registry (no orphans).
|
||||
const registryIds = new Set((REGISTRY.kiro?.models || []).map((m) => m.id));
|
||||
for (const id of kiroCatalogIds) {
|
||||
assert.ok(registryIds.has(id), `free catalog kiro id "${id}" is not in the registry`);
|
||||
}
|
||||
});
|
||||
@@ -4,8 +4,10 @@ import assert from "node:assert/strict";
|
||||
// Covers getNextFamilyFallback's dot-vs-hyphen notation resolution (the hunk
|
||||
// added alongside Claude Fable 5 in #3524 that affects ALL families): the
|
||||
// lookup normalizes dots→hyphens, and each candidate is resolved to the
|
||||
// notation the provider's registry actually exposes (kiro uses dot notation
|
||||
// `claude-opus-4.8`, cc uses hyphen `claude-opus-4-8`).
|
||||
// notation the provider's registry actually exposes (anthropic uses dot notation
|
||||
// `claude-opus-4.8`, cc uses hyphen `claude-opus-4-8`). Kiro is NOT used as the
|
||||
// dot-notation example any more — its upstream never served Opus/Fable and #6170
|
||||
// removed the fabricated ids; `anthropic` genuinely serves them in dot notation.
|
||||
const { getNextFamilyFallback } = await import("../../open-sse/services/modelFamilyFallback.ts");
|
||||
|
||||
test("Fable 5 falls back to the next-best Opus tier first (not Sonnet) — cc→claude", () => {
|
||||
@@ -14,16 +16,16 @@ test("Fable 5 falls back to the next-best Opus tier first (not Sonnet) — cc→
|
||||
assert.equal(next, "claude/claude-opus-4-8");
|
||||
});
|
||||
|
||||
test("Fable 5 fallback resolves to kiro's dot-notation model id", () => {
|
||||
// kiro registry exposes `claude-opus-4.8` (dot), not `claude-opus-4-8`.
|
||||
const next = getNextFamilyFallback("kiro/claude-fable-5", new Set(["kiro/claude-fable-5"]));
|
||||
assert.equal(next, "kiro/claude-opus-4.8");
|
||||
test("Fable 5 fallback resolves to anthropic's dot-notation model id", () => {
|
||||
// anthropic registry exposes `claude-opus-4.8` (dot), not `claude-opus-4-8`.
|
||||
const next = getNextFamilyFallback("anthropic/claude-fable-5", new Set(["anthropic/claude-fable-5"]));
|
||||
assert.equal(next, "anthropic/claude-opus-4.8");
|
||||
});
|
||||
|
||||
test("dot-notation current model is normalized for the family lookup", () => {
|
||||
// kiro/claude-opus-4.8 must find the claude-opus-4-8 family entry.
|
||||
const next = getNextFamilyFallback("kiro/claude-opus-4.8", new Set(["kiro/claude-opus-4.8"]));
|
||||
assert.equal(next, "kiro/claude-opus-4.7");
|
||||
// anthropic/claude-opus-4.8 must find the claude-opus-4-8 family entry.
|
||||
const next = getNextFamilyFallback("anthropic/claude-opus-4.8", new Set(["anthropic/claude-opus-4.8"]));
|
||||
assert.equal(next, "anthropic/claude-opus-4.7");
|
||||
});
|
||||
|
||||
test("skips already-tried candidates and advances down the Fable chain", () => {
|
||||
|
||||
@@ -105,11 +105,15 @@ test("Kiro registry exposes the current CLI model lineup with context windows",
|
||||
const kiroModels = getProviderModels("kr");
|
||||
const byId = new Map(kiroModels.map((model) => [model.id, model]));
|
||||
|
||||
assert.ok(byId.has("claude-opus-4.7"));
|
||||
assert.equal(byId.get("claude-opus-4.7")?.contextLength, 1000000);
|
||||
assert.ok(byId.has("claude-sonnet-4.6"));
|
||||
// Kiro's real upstream Claude lineup (#6170): Sonnet 5 / Sonnet 4.5 / Haiku 4.5.
|
||||
// The Opus 4.x and Sonnet 4.6 ids were fabricated (copied from the Anthropic
|
||||
// catalog) and returned upstream 400 "Invalid model" — removed.
|
||||
assert.ok(byId.has("claude-sonnet-5"));
|
||||
assert.equal(byId.get("claude-sonnet-5")?.contextLength, 1000000);
|
||||
assert.ok(byId.has("claude-sonnet-4.5"));
|
||||
assert.ok(byId.has("claude-haiku-4.5"));
|
||||
assert.equal(byId.has("claude-opus-4-7"), false);
|
||||
assert.equal(byId.has("claude-opus-4.7"), false);
|
||||
assert.equal(byId.has("claude-sonnet-4.6"), false);
|
||||
assert.equal(byId.has("claude-sonnet-4-6"), false);
|
||||
assert.equal(byId.has("claude-haiku-4-5"), false);
|
||||
});
|
||||
|
||||
@@ -734,9 +734,9 @@ test("provider models route returns the expanded local catalog for Kiro", async
|
||||
assert.equal(response.status, 200);
|
||||
assert.equal(body.provider, "kiro");
|
||||
assert.equal(body.source, "local_catalog");
|
||||
assert.ok(body.models.some((model) => model.id === "claude-haiku-4.5"));
|
||||
assert.ok(body.models.some((model) => model.id === "claude-opus-4.7"));
|
||||
assert.ok(body.models.some((model) => model.id === "claude-sonnet-4.6"));
|
||||
const kiroIds = new Set(body.models.map((model) => model.id)); // #6170: real upstream lineup
|
||||
assert.ok(kiroIds.has("claude-sonnet-5") && kiroIds.has("claude-sonnet-4.5") && kiroIds.has("claude-haiku-4.5"));
|
||||
assert.equal(kiroIds.has("claude-opus-4.7") || kiroIds.has("claude-sonnet-4.6"), false); // fabricated ids removed
|
||||
});
|
||||
|
||||
test("provider models route returns the local catalog for new built-in chat-openai-compat providers", async () => {
|
||||
|
||||
Reference in New Issue
Block a user