From d6853228e7502070ea2fb8509ec524748fdc5694 Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Fri, 18 Sep 2026 22:42:00 +0200 Subject: [PATCH] feat(llm, anthropic): Add `claude-fable-5-1` model support Users can now select Anthropic's `claude-fable-5-1`, the successor to Claude Fable 5 for demanding reasoning and long-horizon agentic work. It carries a 1M token context window, 128k max output tokens, always-on adaptive thinking with the full effort ladder up to `max`, and a June 2026 knowledge cutoff. This repository's `fable` alias resolves to it. Like Fable 5, the model rejects both `thinking: disabled` and a forced `tool_choice` with a 400 error. Marking it always-on in the override table routes it through the existing soft-force path, so `jp query -u run_tests` downgrades to `tool_choice: auto` plus a system prompt nudge backed by escalating-nudge retries, and JP never sends a disabled thinking config the API would refuse. The model also binds each thinking block to the conversation prefix that produced it, so a request that rebuilds the system prompt between turns is rejected once Anthropic enforces the check for an account. JP's thinking-rejection recovery catches that error and replays the reasoning as `` text, at the cost of one extra round trip. Opting out of the failure with `prefix_mismatch_behavior: "drop_block"`, along with per-message effort, turn-scoped system messages, and `thinking.display: "updates"`, needs request fields `async-anthropic` does not yet model. Signed-off-by: Jean Mertz --- .jp/config.toml | 2 +- crates/jp_llm/src/provider/anthropic.rs | 5 +++ crates/jp_llm/src/provider/anthropic_tests.rs | 34 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) 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).