fix(models): stop inventing chat capabilities for specialty surfaces (#8016) (#8022)

Co-authored-by: RaviTharuma <ravitharuma@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
Ravi Tharuma
2026-07-22 15:37:57 +02:00
committed by GitHub
parent 7c08c0afea
commit 295189d4a8
3 changed files with 123 additions and 4 deletions

View File

@@ -16,7 +16,24 @@ import { getModelContextOverride } from "@/lib/db/modelContextOverrides";
import { getModelCapabilityOverride } from "@/lib/db/modelCapabilityOverrides";
import { isVisionModelId } from "@/shared/constants/visionModels";
const TOOL_CALLING_UNSUPPORTED_PATTERNS: string[] = [];
const TOOL_CALLING_UNSUPPORTED_PATTERNS: string[] = [
// Specialty / non-chat surfaces must never inherit optimistic tool defaults (#8016)
"whisper",
"tts-1",
"gpt-4o-mini-tts",
"omni-moderation",
"moderation",
"eleven_multilingual",
"eleven_turbo",
"seedance",
"/veo",
"veo-",
"rerank",
"embedding",
"dall-e",
"flux-",
"stable-diffusion",
];
const REASONING_UNSUPPORTED_PATTERNS = [
"antigravity/claude-sonnet-4-6",
"antigravity/claude-sonnet-4-5",
@@ -26,8 +43,39 @@ const REASONING_UNSUPPORTED_PATTERNS = [
"antigravity/gpt-oss-",
"antigravity/gemini-3",
"antigravity/tab_",
// Specialty / non-chat surfaces (#8016)
"whisper",
"tts-1",
"gpt-4o-mini-tts",
"omni-moderation",
"moderation",
"eleven_multilingual",
"eleven_turbo",
"seedance",
"/veo",
"veo-",
"rerank",
"embedding",
"dall-e",
"flux-",
"stable-diffusion",
];
/** Catalog/API surface types that are not chat completions. */
const NON_CHAT_SURFACE_TYPES = new Set([
"audio",
"video",
"image",
"moderation",
"rerank",
"embedding",
"music",
]);
export function isNonChatCatalogSurface(type: unknown): boolean {
return typeof type === "string" && NON_CHAT_SURFACE_TYPES.has(type);
}
const MAX_TOKENS_UNSUPPORTED_PATTERNS = [
"o1-preview",
"o1-mini",

View File

@@ -2,7 +2,10 @@ import { randomUUID } from "node:crypto";
import { parseModel } from "@omniroute/open-sse/services/model.ts";
import { getModelInfo } from "@/sse/services/model";
import { getModelAliases } from "@/lib/db/models";
import { getResolvedModelCapabilities } from "@/lib/modelCapabilities";
import {
getResolvedModelCapabilities,
isNonChatCatalogSurface,
} from "@/lib/modelCapabilities";
import {
getAuthoritativeContextWindow,
getAuthoritativeProviderContextWindow,
@@ -351,12 +354,24 @@ export function enrichCatalogModelEntry<T extends JsonRecord>(
getAuthoritativeProviderContextWindow(provider, model) ??
getAuthoritativeContextWindow(metadata.model) ??
getAuthoritativeContextWindow(model);
const specialtySurface = isNonChatCatalogSurface(entry.type);
const capabilityFields = {
...(typeof metadata.capabilities.vision === "boolean"
? { vision: metadata.capabilities.vision }
: {}),
tool_calling: metadata.capabilities.toolCalling,
reasoning: metadata.capabilities.reasoning,
// #8016: never invent chat tool/reasoning defaults onto specialty surfaces.
// Only copy boolean true when authoritative metadata says so; specialty rows
// default to false instead of optimistic chat heuristics.
tool_calling: specialtySurface
? metadata.capabilities.supportsTools === true || metadata.capabilities.toolCalling === true
? true
: false
: metadata.capabilities.toolCalling,
reasoning: specialtySurface
? metadata.capabilities.supportsThinking === true || metadata.capabilities.reasoning === true
? true
: false
: metadata.capabilities.reasoning,
// #6241: surface thinking support + the canonical effort tiers so the frontend can
// render the effort/thinking toggles. `thinking` is kept for back-compat; `supportsThinking`
// is the explicit flag and `effort_tiers` lists the selectable reasoning levels
@@ -403,10 +418,22 @@ export function enrichCatalogModelEntry<T extends JsonRecord>(
}
if (
!specialtySurface &&
(typeof nextEntry.context_length !== "number" || authoritativeContextWindow !== null) &&
typeof metadata.limits.contextWindow === "number"
) {
nextEntry.context_length = metadata.limits.contextWindow;
} else if (
specialtySurface &&
authoritativeContextWindow !== null &&
typeof authoritativeContextWindow === "number"
) {
// Only authoritative static windows may decorate specialty rows.
nextEntry.context_length = authoritativeContextWindow;
} else if (specialtySurface && typeof nextEntry.context_length === "number") {
// Keep an explicit source-provided context if the emitter already set one.
} else if (specialtySurface) {
delete nextEntry.context_length;
}
if (typeof metadata.limits.maxOutputTokens === "number" && metadata.limits.maxOutputTokens > 0) {

View File

@@ -0,0 +1,44 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
getResolvedModelCapabilities,
isNonChatCatalogSurface,
} from "../../src/lib/modelCapabilities.ts";
import { enrichCatalogModelEntry } from "../../src/lib/modelMetadataRegistry.ts";
describe("specialty catalog surfaces (#8016)", () => {
it("recognizes non-chat catalog types", () => {
assert.equal(isNonChatCatalogSurface("audio"), true);
assert.equal(isNonChatCatalogSurface("video"), true);
assert.equal(isNonChatCatalogSurface("moderation"), true);
assert.equal(isNonChatCatalogSurface("chat"), false);
assert.equal(isNonChatCatalogSurface(undefined), false);
});
it("does not optimistically enable tools/reasoning for specialty model ids", () => {
const whisper = getResolvedModelCapabilities({ provider: "openai", model: "whisper-1" });
assert.equal(whisper.toolCalling, false);
assert.equal(whisper.reasoning, false);
const tts = getResolvedModelCapabilities({ provider: "openai", model: "tts-1" });
assert.equal(tts.toolCalling, false);
assert.equal(tts.reasoning, false);
const veo = getResolvedModelCapabilities({ provider: "veo-free", model: "veo" });
assert.equal(veo.toolCalling, false);
assert.equal(veo.reasoning, false);
});
it("enrichment does not invent chat tool/reasoning on typed specialty rows", () => {
const enriched = enrichCatalogModelEntry({
id: "openai/whisper-1",
owned_by: "openai",
root: "whisper-1",
type: "audio",
subtype: "transcription",
});
const caps = (enriched.capabilities || {}) as Record<string, unknown>;
assert.equal(caps.tool_calling, false);
assert.equal(caps.reasoning, false);
});
});