Skip to content

Commit b2b4d0e

Browse files
authored
feat(models): add Claude Fable 5.1 and align Anthropic prompt-cache minimums (#7380)
1 parent 150bff4 commit b2b4d0e

6 files changed

Lines changed: 110 additions & 8 deletions

File tree

apps/docs/content/docs/workflows/blocks/agent.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Live tool-call chips stream for **OpenAI, Anthropic, Azure Anthropic, Google, Ve
109109
| Provider | Streamed thinking | Models |
110110
|----------|-------------------|--------|
111111
| OpenAI | Summaries only — Requires OpenAI organization verification; falls back to no summaries. | `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5-pro`, `gpt-5.5`, `gpt-5.4-pro`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, `gpt-5.2-pro`, `gpt-5.2`, `gpt-5.1`, `gpt-5-pro`, `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `o4-mini`, `o3`, `o3-mini`, `o1` |
112-
| Anthropic | Summaries only — These generations omit full thinking; Sim requests summarized thinking on streaming runs. | `claude-fable-5`, `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-opus-4-5`, `claude-opus-4-1`, `claude-sonnet-4-5`, `claude-haiku-4-5` |
112+
| Anthropic | Summaries only — These generations omit full thinking; Sim requests summarized thinking on streaming runs. | `claude-fable-5-1`, `claude-fable-5`, `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-opus-4-5`, `claude-opus-4-1`, `claude-sonnet-4-5`, `claude-haiku-4-5` |
113113
| Azure OpenAI | Summaries only — Requires OpenAI organization verification; falls back to no summaries. | `azure/gpt-5.4`, `azure/gpt-5.4-mini`, `azure/gpt-5.4-nano`, `azure/gpt-5.2`, `azure/gpt-5.1`, `azure/gpt-5.1-codex`, `azure/gpt-5`, `azure/gpt-5-mini`, `azure/gpt-5-nano`, `azure/o3`, `azure/o4-mini` |
114114
| Azure Anthropic | Summaries only — These generations omit full thinking; Sim requests summarized thinking on streaming runs. | `azure-anthropic/claude-opus-4-6`, `azure-anthropic/claude-opus-4-5`, `azure-anthropic/claude-sonnet-4-5`, `azure-anthropic/claude-opus-4-1`, `azure-anthropic/claude-haiku-4-5` |
115115
| Google | Summaries only | `gemini-3.6-flash`, `gemini-3.5-flash-lite`, `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3.1-flash-lite`, `gemini-3-flash-preview`, `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.5-flash-lite` |

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,58 @@ describe('executeAnthropicProviderRequest prompt caching', () => {
248248
expect(payload.system).toBeUndefined()
249249
})
250250
})
251+
252+
describe('executeAnthropicProviderRequest forced tool use', () => {
253+
const forcedTool = {
254+
id: 'publish',
255+
name: 'publish',
256+
description: 'Publish a post',
257+
params: {},
258+
parameters: { type: 'object', properties: {}, required: [] },
259+
usageControl: 'force' as const,
260+
}
261+
262+
const textReply = {
263+
id: 'msg-answer',
264+
type: 'message',
265+
role: 'assistant',
266+
model: 'claude-fable-5-1',
267+
content: [{ type: 'text', text: 'Done' }],
268+
stop_reason: 'end_turn',
269+
stop_sequence: null,
270+
usage: { input_tokens: 2, output_tokens: 2 },
271+
}
272+
273+
async function runWithForcedTool(model: string) {
274+
const create = vi.fn().mockResolvedValue({ ...textReply, model })
275+
const warn = vi.fn()
276+
await executeAnthropicProviderRequest(
277+
{
278+
model,
279+
apiKey: 'test-key',
280+
maxTokens: 1024,
281+
messages: [{ role: 'user', content: 'Publish this' }],
282+
tools: [forcedTool],
283+
},
284+
{
285+
providerId: 'anthropic',
286+
providerLabel: 'Anthropic',
287+
createClient: () => ({ messages: { create } }) as never,
288+
logger: { info: vi.fn(), warn, error: vi.fn(), debug: vi.fn() },
289+
}
290+
)
291+
return { payload: create.mock.calls[0][0] as Anthropic.Messages.MessageCreateParams, warn }
292+
}
293+
294+
it('forces the tool on models that accept forced tool_choice', async () => {
295+
const { payload } = await runWithForcedTool('claude-sonnet-4-5')
296+
expect(payload.tool_choice).toEqual({ type: 'tool', name: 'publish' })
297+
})
298+
299+
it('drops forced tool_choice on Claude Fable 5.1 because the API rejects it', async () => {
300+
const { payload, warn } = await runWithForcedTool('claude-fable-5-1')
301+
expect(payload.tools?.map((tool) => tool.name)).toEqual(['publish'])
302+
expect(payload).not.toHaveProperty('tool_choice')
303+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('rejects forced tool_choice'))
304+
})
305+
})

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { describeModelLevel } from '@/providers/utils'
1313
describe('buildThinkingConfig', () => {
1414
it('requests summarized display for omitted-display models on agent-events runs', () => {
1515
for (const model of [
16+
'claude-fable-5-1',
1617
'claude-fable-5',
1718
'claude-sonnet-5',
1819
'claude-opus-5',
@@ -27,6 +28,7 @@ describe('buildThinkingConfig', () => {
2728

2829
it('never adds display on legacy runs (no agent events)', () => {
2930
for (const model of [
31+
'claude-fable-5-1',
3032
'claude-fable-5',
3133
'claude-sonnet-5',
3234
'claude-opus-5',

apps/sim/providers/anthropic/core.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ const ANTHROPIC_THINKING_OUTPUT_HEADROOM = 4096
132132

133133
/**
134134
* Checks if a model supports adaptive thinking (thinking.type: "adaptive").
135-
* Fable 5 supports ONLY adaptive thinking (always on; type: "disabled" is rejected).
135+
* Fable 5 and Fable 5.1 support ONLY adaptive thinking (always on; type: "disabled" is rejected).
136136
* Sonnet 5 supports ONLY adaptive thinking (manual budget_tokens returns a 400 error).
137137
* Opus 5, Opus 4.8, and Opus 4.7 support ONLY adaptive thinking (no extended thinking / budget_tokens).
138138
* Opus 4.6 and Sonnet 4.6 support both extended and adaptive thinking — use adaptive.
@@ -155,10 +155,21 @@ function supportsAdaptiveThinking(modelId: string): boolean {
155155
)
156156
}
157157

158+
/**
159+
* Claude Fable 5.1 and Claude Mythos 5.1 reject forced tool use: a `tool_choice` of
160+
* type `tool` or `any` returns a 400 (`tool_choice: type "tool" and "any" are not
161+
* supported for this model.`). Thinking is always on for these models, so a forced
162+
* call would skip it. The request is sent with the default `auto` instead.
163+
*/
164+
function rejectsForcedToolChoice(modelId: string): boolean {
165+
const normalizedModel = modelId.toLowerCase()
166+
return normalizedModel.includes('fable-5-1') || normalizedModel.includes('mythos-5-1')
167+
}
168+
158169
/**
159170
* Builds the thinking configuration for the Anthropic API based on model capabilities and level.
160171
*
161-
* - Fable 5, Sonnet 5, Opus 5, Opus 4.8, Opus 4.7: Uses adaptive thinking only (no extended thinking support)
172+
* - Fable 5.1, Fable 5, Sonnet 5, Opus 5, Opus 4.8, Opus 4.7: Uses adaptive thinking only (no extended thinking support)
162173
* - Opus 4.6, Sonnet 4.6: Uses adaptive thinking with effort parameter
163174
* - Other models: Uses budget_tokens-based extended thinking
164175
*
@@ -417,7 +428,13 @@ export async function executeAnthropicProviderRequest(
417428
} else if (toolChoice === 'none') {
418429
payload.tool_choice = { type: 'none' }
419430
} else if (toolChoice !== 'auto') {
420-
payload.tool_choice = toolChoice
431+
if (rejectsForcedToolChoice(request.model)) {
432+
logger.warn(
433+
`Model ${modelId} rejects forced tool_choice; sending tool "${toolChoice.name}" with tool_choice auto`
434+
)
435+
} else {
436+
payload.tool_choice = toolChoice
437+
}
421438
}
422439
}
423440

apps/sim/providers/models.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ describe('prompt caching capability', () => {
6161

6262
it('reports the vendor minimum prefix, raised for Haiku', () => {
6363
expect(getPromptCachingMinimumTokens('claude-sonnet-5')).toBe(1024)
64-
expect(getPromptCachingMinimumTokens('claude-haiku-4-5')).toBe(2048)
65-
expect(getPromptCachingMinimumTokens('azure-anthropic/claude-haiku-4-5')).toBe(2048)
64+
expect(getPromptCachingMinimumTokens('claude-haiku-4-5')).toBe(4096)
65+
expect(getPromptCachingMinimumTokens('azure-anthropic/claude-haiku-4-5')).toBe(4096)
6666
expect(getPromptCachingMinimumTokens('gpt-5.5')).toBeNull()
6767
})
6868
})

apps/sim/providers/models.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,27 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
825825
promptCaching: { minimumCacheableTokens: 1024 },
826826
},
827827
models: [
828+
{
829+
id: 'claude-fable-5-1',
830+
pricing: {
831+
input: 10.0,
832+
cachedInput: 0.25,
833+
output: 50.0,
834+
updatedAt: '2026-09-01',
835+
},
836+
capabilities: {
837+
nativeStructuredOutputs: true,
838+
maxOutputTokens: 128000,
839+
promptCaching: { minimumCacheableTokens: 512 },
840+
thinking: {
841+
levels: ['low', 'medium', 'high', 'xhigh', 'max'],
842+
default: 'high',
843+
streamed: 'summary',
844+
},
845+
},
846+
contextWindow: 1000000,
847+
releaseDate: '2026-09-01',
848+
},
828849
{
829850
id: 'claude-fable-5',
830851
pricing: {
@@ -836,6 +857,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
836857
capabilities: {
837858
nativeStructuredOutputs: true,
838859
maxOutputTokens: 128000,
860+
promptCaching: { minimumCacheableTokens: 512 },
839861
thinking: {
840862
levels: ['low', 'medium', 'high', 'xhigh', 'max'],
841863
default: 'high',
@@ -877,6 +899,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
877899
capabilities: {
878900
nativeStructuredOutputs: true,
879901
maxOutputTokens: 128000,
902+
promptCaching: { minimumCacheableTokens: 512 },
880903
thinking: {
881904
levels: ['low', 'medium', 'high', 'xhigh', 'max'],
882905
default: 'high',
@@ -918,6 +941,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
918941
capabilities: {
919942
nativeStructuredOutputs: true,
920943
maxOutputTokens: 128000,
944+
promptCaching: { minimumCacheableTokens: 2048 },
921945
thinking: {
922946
levels: ['low', 'medium', 'high', 'xhigh', 'max'],
923947
default: 'high',
@@ -939,6 +963,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
939963
temperature: { min: 0, max: 1 },
940964
nativeStructuredOutputs: true,
941965
maxOutputTokens: 128000,
966+
promptCaching: { minimumCacheableTokens: 4096 },
942967
thinking: {
943968
levels: ['low', 'medium', 'high', 'max'],
944969
default: 'high',
@@ -981,6 +1006,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
9811006
temperature: { min: 0, max: 1 },
9821007
nativeStructuredOutputs: true,
9831008
maxOutputTokens: 64000,
1009+
promptCaching: { minimumCacheableTokens: 4096 },
9841010
thinking: {
9851011
levels: ['low', 'medium', 'high'],
9861012
default: 'high',
@@ -1086,7 +1112,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
10861112
temperature: { min: 0, max: 1 },
10871113
nativeStructuredOutputs: true,
10881114
maxOutputTokens: 64000,
1089-
promptCaching: { minimumCacheableTokens: 2048 },
1115+
promptCaching: { minimumCacheableTokens: 4096 },
10901116
thinking: {
10911117
levels: ['low', 'medium', 'high'],
10921118
default: 'high',
@@ -1457,6 +1483,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
14571483
temperature: { min: 0, max: 1 },
14581484
nativeStructuredOutputs: true,
14591485
maxOutputTokens: 128000,
1486+
promptCaching: { minimumCacheableTokens: 4096 },
14601487
thinking: {
14611488
levels: ['low', 'medium', 'high', 'max'],
14621489
default: 'high',
@@ -1478,6 +1505,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
14781505
temperature: { min: 0, max: 1 },
14791506
nativeStructuredOutputs: true,
14801507
maxOutputTokens: 64000,
1508+
promptCaching: { minimumCacheableTokens: 4096 },
14811509
thinking: {
14821510
levels: ['low', 'medium', 'high'],
14831511
default: 'high',
@@ -1541,7 +1569,7 @@ export const PROVIDER_DEFINITIONS: Record<string, ProviderDefinition> = {
15411569
temperature: { min: 0, max: 1 },
15421570
nativeStructuredOutputs: true,
15431571
maxOutputTokens: 64000,
1544-
promptCaching: { minimumCacheableTokens: 2048 },
1572+
promptCaching: { minimumCacheableTokens: 4096 },
15451573
thinking: {
15461574
levels: ['low', 'medium', 'high'],
15471575
default: 'high',

0 commit comments

Comments
 (0)