diff --git a/.jp/config.toml b/.jp/config.toml index c4b572d33..5f10ad450 100644 --- a/.jp/config.toml +++ b/.jp/config.toml @@ -34,7 +34,7 @@ anthropic = "anthropic/claude-sonnet-5" claude = "anthropic/claude-sonnet-5" sonnet = "anthropic/claude-sonnet-5" opus = "anthropic/claude-opus-5" -fable = "anthropic/claude-fable-5" +fable = "anthropic/claude-fable-5-1" haiku = "anthropic/claude-haiku-4-5" zai = "cerebras/zai-glm-4.7" # OpenAI aliases diff --git a/crates/jp_llm/src/provider/anthropic.rs b/crates/jp_llm/src/provider/anthropic.rs index c55abbe5d..33d8e76fd 100644 --- a/crates/jp_llm/src/provider/anthropic.rs +++ b/crates/jp_llm/src/provider/anthropic.rs @@ -1811,6 +1811,11 @@ fn model_overrides(id: &str) -> Option { let cutoff = |year, month| NaiveDate::from_ymd_opt(year, month, 1); Some(match id { + "claude-fable-5-1" => ModelOverrides { + knowledge_cutoff: cutoff(2026, 6), + always_on: true, + ..Default::default() + }, "claude-fable-5" => ModelOverrides { knowledge_cutoff: cutoff(2026, 1), always_on: true, diff --git a/crates/jp_llm/src/provider/anthropic_tests.rs b/crates/jp_llm/src/provider/anthropic_tests.rs index a89f705f6..a044907c9 100644 --- a/crates/jp_llm/src/provider/anthropic_tests.rs +++ b/crates/jp_llm/src/provider/anthropic_tests.rs @@ -530,6 +530,40 @@ fn test_map_model_opus_5() { assert!(!details.supports_prefill()); } +/// Verify the `map_model` arm for Claude Fable 5.1 produces the expected +/// `ModelDetails`, including the `thinking-always-on` capability that stops JP +/// from sending `thinking: disabled` and from hard-forcing a `tool_choice`, +/// both of which Fable 5.1 rejects with a 400. +#[test] +fn test_map_model_fable_5_1() { + let model = adaptive_api_model("claude-fable-5-1", "Claude Fable 5.1", true, true); + + let details = map_model(model).unwrap(); + + assert_eq!( + details.id, + (PROVIDER, "claude-fable-5-1").try_into().unwrap() + ); + assert_eq!(details.display_name.as_deref(), Some("Claude Fable 5.1")); + assert_eq!(details.context_window, Some(1_000_000)); + assert_eq!(details.max_output_tokens, Some(128_000)); + assert_eq!( + details.knowledge_cutoff, + NaiveDate::from_ymd_opt(2026, 6, 1) + ); + assert_eq!( + details.reasoning, + Some(ReasoningDetails::adaptive(true, true).always_on()) + ); + assert_eq!(details.structured_output, Some(true)); + assert_eq!(details.deprecated, Some(ModelDeprecation::Active)); + assert!(details.features.contains(&"adaptive-thinking")); + assert!(details.features.contains(&"interleaved-thinking")); + assert!(details.features.contains(&"context-editing")); + assert!(!details.supports_disabling_thinking()); + assert!(!details.supports_prefill()); +} + /// Verify the `map_model` arm for Claude Fable 5 produces the expected /// `ModelDetails`, including the `thinking-always-on` capability that stops JP /// from sending `thinking: disabled` (which Fable rejects).