Skip to content

Commit 4de0fbf

Browse files
committed
refactor(models): use catalog capabilities for forced tool support
1 parent 5a8e54b commit 4de0fbf

3 files changed

Lines changed: 15 additions & 85 deletions

File tree

apps/sim/providers/anthropic/core.request.test.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -296,13 +296,8 @@ describe('executeAnthropicProviderRequest forced tool use', () => {
296296
expect(payload.tool_choice).toEqual({ type: 'tool', name: 'publish' })
297297
})
298298

299-
it.each([
300-
'claude-fable-5-1',
301-
'claude-fable-5-1-20260901',
302-
'azure-anthropic/claude-fable-5-1',
303-
'claude-mythos-5-1',
304-
])('drops forced tool_choice on %s because the API rejects it', async (model) => {
305-
const { payload, warn } = await runWithForcedTool(model)
299+
it('drops forced tool_choice when the catalog model disables Force', async () => {
300+
const { payload, warn } = await runWithForcedTool('claude-fable-5-1')
306301
expect(payload.tools?.map((tool) => tool.name)).toEqual(['publish'])
307302
expect(payload).not.toHaveProperty('tool_choice')
308303
expect(warn).toHaveBeenCalledWith(expect.stringContaining('rejects forced tool_choice'))

apps/sim/providers/models.test.ts

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,62 +16,18 @@ import {
1616
PROVIDER_DEFINITIONS,
1717
supportsForcedToolUse,
1818
updateFireworksModels,
19-
updateOpenRouterModels,
2019
} from '@/providers/models'
2120
import { supportsPromptCaching } from '@/providers/utils'
2221

2322
describe('forced tool use capability', () => {
24-
it('keeps Auto and None support when a model disables Force', () => {
25-
expect(getModelCapabilities('claude-fable-5-1')).toMatchObject({
26-
toolUsageControl: true,
27-
forcedToolUse: false,
28-
})
29-
})
30-
31-
it.each([
32-
'claude-fable-5-1',
33-
'CLAUDE-FABLE-5-1-20260901',
34-
'azure-anthropic/claude-fable-5-1',
35-
'azure-anthropic/claude-fable-5-1-20260901',
36-
'bedrock/anthropic.claude-fable-5-1-v1:0',
37-
'claude-mythos-5-1',
38-
'claude-mythos-5-1-20260901',
39-
])('disables forced tool use for %s', (model) => {
40-
expect(getModelCapabilities(model)).toMatchObject({
41-
toolUsageControl: true,
42-
forcedToolUse: false,
43-
})
44-
expect(supportsForcedToolUse(model)).toBe(false)
45-
})
46-
47-
it('adds only known alias capabilities to provider defaults', () => {
48-
expect(getModelCapabilities('claude-mythos-5-1')).toEqual({
49-
...PROVIDER_DEFINITIONS.anthropic.capabilities,
50-
forcedToolUse: false,
51-
})
52-
})
53-
54-
it('inherits alias capabilities for dynamic models and respects explicit overrides', () => {
55-
const originalModels = PROVIDER_DEFINITIONS.openrouter.models
56-
const modelId = 'anthropic/claude-fable-5-1'
57-
try {
58-
updateOpenRouterModels([modelId])
59-
expect(getModelCapabilities(modelId)?.forcedToolUse).toBe(false)
60-
expect(supportsForcedToolUse(modelId)).toBe(false)
61-
62-
PROVIDER_DEFINITIONS.openrouter.models[0].capabilities.forcedToolUse = true
63-
expect(getModelCapabilities(modelId)?.forcedToolUse).toBe(true)
64-
expect(supportsForcedToolUse(modelId)).toBe(true)
65-
} finally {
66-
PROVIDER_DEFINITIONS.openrouter.models = originalModels
67-
}
68-
})
69-
70-
it.each(['claude-fable-5-10', 'claude-mythos-5-10', 'claude-not-fable-5-1'])(
71-
'does not apply alias restrictions to unrelated model %s',
23+
it.each(['claude-fable-5-1', 'CLAUDE-FABLE-5-1'])(
24+
'disables Force while keeping Auto and None support for %s',
7225
(model) => {
73-
expect(getModelCapabilities(model)).toEqual(PROVIDER_DEFINITIONS.anthropic.capabilities)
74-
expect(supportsForcedToolUse(model)).toBe(true)
26+
expect(getModelCapabilities(model)).toMatchObject({
27+
toolUsageControl: true,
28+
forcedToolUse: false,
29+
})
30+
expect(supportsForcedToolUse(model)).toBe(false)
7531
}
7632
)
7733

apps/sim/providers/models.ts

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -164,16 +164,6 @@ export function getProviderFileAttachment(providerId: string): ProviderFileAttac
164164
return PROVIDER_DEFINITIONS[providerId]?.fileAttachment ?? DEFAULT_FILE_ATTACHMENT
165165
}
166166

167-
const CLAUDE_5_1_TOOL_CAPABILITIES = { forcedToolUse: false } as const satisfies ModelCapabilities
168-
169-
/** Known capabilities for model aliases; explicit catalog capabilities take precedence. */
170-
const MODEL_CAPABILITY_FALLBACKS = [
171-
{
172-
pattern: /(?:^|[/.])claude-(?:fable|mythos)-5-1(?:$|[-:])/,
173-
capabilities: CLAUDE_5_1_TOOL_CAPABILITIES,
174-
},
175-
] satisfies { pattern: RegExp; capabilities: ModelCapabilities }[]
176-
177167
export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
178168
fireworks: {
179169
id: 'fireworks',
@@ -846,7 +836,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
846836
updatedAt: '2026-09-01',
847837
},
848838
capabilities: {
849-
...CLAUDE_5_1_TOOL_CAPABILITIES,
839+
forcedToolUse: false,
850840
nativeStructuredOutputs: true,
851841
maxOutputTokens: 128000,
852842
promptCaching: { minimumCacheableTokens: 512 },
@@ -4307,36 +4297,25 @@ export function getModelPricing(modelId: string): ModelPricing | null {
43074297
}
43084298

43094299
export function getModelCapabilities(modelId: string): ModelCapabilities | null {
4310-
const normalizedModel = modelId.toLowerCase()
4311-
const fallbackCapabilities = MODEL_CAPABILITY_FALLBACKS.find(({ pattern }) =>
4312-
pattern.test(normalizedModel)
4313-
)?.capabilities
4314-
43154300
for (const provider of Object.values(PROVIDER_DEFINITIONS)) {
4316-
const model = provider.models.find((m) => m.id.toLowerCase() === normalizedModel)
4301+
const model = provider.models.find((m) => m.id.toLowerCase() === modelId.toLowerCase())
43174302
if (model) {
4318-
const capabilities: ModelCapabilities = {
4319-
...provider.capabilities,
4320-
...fallbackCapabilities,
4321-
...model.capabilities,
4322-
}
4303+
const capabilities: ModelCapabilities = { ...provider.capabilities, ...model.capabilities }
43234304
return capabilities
43244305
}
43254306
}
43264307

43274308
for (const provider of Object.values(PROVIDER_DEFINITIONS)) {
43284309
if (provider.modelPatterns) {
43294310
for (const pattern of provider.modelPatterns) {
4330-
if (pattern.test(normalizedModel)) {
4331-
return fallbackCapabilities
4332-
? { ...provider.capabilities, ...fallbackCapabilities }
4333-
: provider.capabilities || null
4311+
if (pattern.test(modelId.toLowerCase())) {
4312+
return provider.capabilities || null
43344313
}
43354314
}
43364315
}
43374316
}
43384317

4339-
return fallbackCapabilities || null
4318+
return null
43404319
}
43414320

43424321
export function getModelsWithTemperatureSupport(): string[] {

0 commit comments

Comments
 (0)