feat(model-capabilities): add maintenance guardrails
This commit is contained in:
@@ -2,8 +2,10 @@ import { describe, expect, test } from "bun:test"
|
||||
|
||||
import {
|
||||
getModelCapabilities,
|
||||
getBundledModelCapabilitiesSnapshot,
|
||||
type ModelCapabilitiesSnapshot,
|
||||
} from "./model-capabilities"
|
||||
import { AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS } from "./model-requirements"
|
||||
|
||||
describe("getModelCapabilities", () => {
|
||||
const bundledSnapshot: ModelCapabilitiesSnapshot = {
|
||||
@@ -79,6 +81,12 @@ describe("getModelCapabilities", () => {
|
||||
maxOutputTokens: 128_000,
|
||||
toolCall: true,
|
||||
})
|
||||
expect(result.diagnostics).toMatchObject({
|
||||
resolutionMode: "snapshot-backed",
|
||||
canonicalization: { source: "canonical" },
|
||||
snapshot: { source: "bundled-snapshot" },
|
||||
variants: { source: "runtime" },
|
||||
})
|
||||
})
|
||||
|
||||
test("reads structured runtime capabilities from the SDK v2 shape", () => {
|
||||
@@ -113,6 +121,12 @@ describe("getModelCapabilities", () => {
|
||||
output: ["text"],
|
||||
},
|
||||
})
|
||||
expect(result.diagnostics).toMatchObject({
|
||||
resolutionMode: "snapshot-backed",
|
||||
reasoning: { source: "runtime" },
|
||||
supportsThinking: { source: "runtime" },
|
||||
toolCall: { source: "runtime" },
|
||||
})
|
||||
})
|
||||
|
||||
test("respects root-level thinking flags when providers do not nest them under capabilities", () => {
|
||||
@@ -129,6 +143,9 @@ describe("getModelCapabilities", () => {
|
||||
canonicalModelID: "gpt-5.4",
|
||||
supportsThinking: true,
|
||||
})
|
||||
expect(result.diagnostics).toMatchObject({
|
||||
supportsThinking: { source: "runtime" },
|
||||
})
|
||||
})
|
||||
|
||||
test("accepts runtime variant arrays without corrupting them into numeric keys", () => {
|
||||
@@ -158,6 +175,14 @@ describe("getModelCapabilities", () => {
|
||||
supportsTemperature: true,
|
||||
maxOutputTokens: 128_000,
|
||||
})
|
||||
expect(result.diagnostics).toMatchObject({
|
||||
resolutionMode: "alias-backed",
|
||||
canonicalization: {
|
||||
source: "pattern-alias",
|
||||
ruleID: "anthropic-thinking-suffix",
|
||||
},
|
||||
snapshot: { source: "bundled-snapshot" },
|
||||
})
|
||||
})
|
||||
|
||||
test("maps local gemini aliases to canonical models.dev entries", () => {
|
||||
@@ -174,6 +199,14 @@ describe("getModelCapabilities", () => {
|
||||
supportsTemperature: true,
|
||||
maxOutputTokens: 65_000,
|
||||
})
|
||||
expect(result.diagnostics).toMatchObject({
|
||||
resolutionMode: "alias-backed",
|
||||
canonicalization: {
|
||||
source: "exact-alias",
|
||||
ruleID: "gemini-3.1-pro-tier-alias",
|
||||
},
|
||||
snapshot: { source: "bundled-snapshot" },
|
||||
})
|
||||
})
|
||||
|
||||
test("prefers runtime models.dev cache over bundled snapshot", () => {
|
||||
@@ -203,6 +236,11 @@ describe("getModelCapabilities", () => {
|
||||
maxOutputTokens: 64_000,
|
||||
supportsTemperature: false,
|
||||
})
|
||||
expect(result.diagnostics).toMatchObject({
|
||||
snapshot: { source: "runtime-snapshot" },
|
||||
maxOutputTokens: { source: "runtime-snapshot" },
|
||||
supportsTemperature: { source: "runtime-snapshot" },
|
||||
})
|
||||
})
|
||||
|
||||
test("falls back to heuristic family rules when no snapshot entry exists", () => {
|
||||
@@ -218,6 +256,12 @@ describe("getModelCapabilities", () => {
|
||||
variants: ["low", "medium", "high"],
|
||||
reasoningEfforts: ["none", "minimal", "low", "medium", "high"],
|
||||
})
|
||||
expect(result.diagnostics).toMatchObject({
|
||||
resolutionMode: "heuristic-backed",
|
||||
snapshot: { source: "none" },
|
||||
family: { source: "heuristic" },
|
||||
reasoningEfforts: { source: "heuristic" },
|
||||
})
|
||||
})
|
||||
|
||||
test("detects prefixed o-series model IDs through the heuristic fallback", () => {
|
||||
@@ -233,5 +277,34 @@ describe("getModelCapabilities", () => {
|
||||
variants: ["low", "medium", "high"],
|
||||
reasoningEfforts: ["none", "minimal", "low", "medium", "high"],
|
||||
})
|
||||
expect(result.diagnostics).toMatchObject({
|
||||
resolutionMode: "heuristic-backed",
|
||||
snapshot: { source: "none" },
|
||||
family: { source: "heuristic" },
|
||||
})
|
||||
})
|
||||
|
||||
test("keeps every built-in OmO requirement model snapshot-backed", () => {
|
||||
const bundledSnapshot = getBundledModelCapabilitiesSnapshot()
|
||||
const requirementModels = new Set<string>()
|
||||
|
||||
for (const requirement of Object.values(AGENT_MODEL_REQUIREMENTS)) {
|
||||
for (const entry of requirement.fallbackChain) requirementModels.add(entry.model)
|
||||
}
|
||||
|
||||
for (const requirement of Object.values(CATEGORY_MODEL_REQUIREMENTS)) {
|
||||
for (const entry of requirement.fallbackChain) requirementModels.add(entry.model)
|
||||
}
|
||||
|
||||
for (const modelID of requirementModels) {
|
||||
const result = getModelCapabilities({
|
||||
providerID: "test-provider",
|
||||
modelID,
|
||||
bundledSnapshot,
|
||||
})
|
||||
|
||||
expect(result.diagnostics.resolutionMode).toBe("snapshot-backed")
|
||||
expect(result.diagnostics.snapshot.source).toBe("bundled-snapshot")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user