detached fdc6643

Add Claude Fable 5.1 support

commit
fdc66430918ef021cad5f4404e033bcd2460b373
author
patriceckhart · · 2026-09-02 06:47 UTC
parent
4441e91
signature
unsigned
refs
tag: v0.3.55
3 files · +67 -4
M packages/provider/anthropic.go +1 -1
@@ -21,7 +21,7 @@ const anthropicAPIVersion = "2023-06-01"
2121 // accepts the request; diverging from them causes 429 rate_limit_error
2222 // or 403 on the very first request.
2323 const (
24- claudeCodeVersion = "2.1.75"
24+ claudeCodeVersion = "2.1.258"
2525 claudeCodeIdentity = "You are Claude Code, Anthropic's official CLI for Claude."
2626 )
2727
@@ -21,7 +21,7 @@ const anthropicAPIVersion = "2023-06-01"
21 // accepts the request; diverging from them causes 429 rate_limit_error21 // accepts the request; diverging from them causes 429 rate_limit_error
22 // or 403 on the very first request.22 // or 403 on the very first request.
23 const (23 const (
24- claudeCodeVersion = "2.1.75"24+ claudeCodeVersion = "2.1.258"
25 claudeCodeIdentity = "You are Claude Code, Anthropic's official CLI for Claude."25 claudeCodeIdentity = "You are Claude Code, Anthropic's official CLI for Claude."
26 )26 )
27 27
M packages/provider/models.go +7 -3
@@ -272,7 +272,7 @@ var Catalog = []Model{
272272 BaseURL: gondolaDefaultBaseURL,
273273 },
274274
275- // ---- Speculative: Anthropic ----
275+ // ---- Anthropic / newer families ----
276276 {
277277 Provider: "anthropic", ID: "claude-opus-4-5", DisplayName: "Claude Opus 4.5 (latest)",
278278 ContextWindow: 200000, MaxOutput: 64000, Reasoning: true,
@@ -316,8 +316,12 @@ var Catalog = []Model{
316316 {
317317 Provider: "anthropic", ID: "claude-fable-5", DisplayName: "Claude Fable 5",
318318 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
319- PriceInput: 10, PriceOutput: 50, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,
320- Speculative: true,
319+ PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5,
320+ },
321+ {
322+ Provider: "anthropic", ID: "claude-fable-5-1", DisplayName: "Claude Fable 5.1",
323+ ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
324+ PriceInput: 10, PriceOutput: 50, PriceCacheRead: 0.25, PriceCacheWrite: 12.5,
321325 },
322326
323327 // ---- Speculative: OpenAI ----
@@ -272,7 +272,7 @@ var Catalog = []Model{
272 BaseURL: gondolaDefaultBaseURL,272 BaseURL: gondolaDefaultBaseURL,
273 },273 },
274 274
275- // ---- Speculative: Anthropic ----275+ // ---- Anthropic / newer families ----
276 {276 {
277 Provider: "anthropic", ID: "claude-opus-4-5", DisplayName: "Claude Opus 4.5 (latest)",277 Provider: "anthropic", ID: "claude-opus-4-5", DisplayName: "Claude Opus 4.5 (latest)",
278 ContextWindow: 200000, MaxOutput: 64000, Reasoning: true,278 ContextWindow: 200000, MaxOutput: 64000, Reasoning: true,
@@ -316,8 +316,12 @@ var Catalog = []Model{
316 {316 {
317 Provider: "anthropic", ID: "claude-fable-5", DisplayName: "Claude Fable 5",317 Provider: "anthropic", ID: "claude-fable-5", DisplayName: "Claude Fable 5",
318 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,318 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
319- PriceInput: 10, PriceOutput: 50, PriceCacheRead: 0.5, PriceCacheWrite: 6.25,319+ PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5,
320- Speculative: true,320+ },
321+ {
322+ Provider: "anthropic", ID: "claude-fable-5-1", DisplayName: "Claude Fable 5.1",
323+ ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
324+ PriceInput: 10, PriceOutput: 50, PriceCacheRead: 0.25, PriceCacheWrite: 12.5,
321 },325 },
322 326
323 // ---- Speculative: OpenAI ----327 // ---- Speculative: OpenAI ----
M packages/provider/provider_test.go +59 -0
@@ -96,6 +96,24 @@ func TestAnthropicErrorStatus(t *testing.T) {
9696 }
9797 }
9898
99+func TestAnthropicOAuthUsesCurrentClaudeCodeVersion(t *testing.T) {
100+ var userAgent string
101+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
102+ userAgent = r.Header.Get("user-agent")
103+ w.WriteHeader(http.StatusBadRequest)
104+ }))
105+ defer srv.Close()
106+
107+ c := NewAnthropicOAuth("test-token", srv.URL)
108+ _, err := c.Stream(context.Background(), Request{Model: "claude-fable-5-1"})
109+ if err == nil {
110+ t.Fatal("expected test server error")
111+ }
112+ if want := "claude-cli/2.1.258"; userAgent != want {
113+ t.Fatalf("user-agent = %q, want %q", userAgent, want)
114+ }
115+}
116+
99117 func TestOpenAIErrorStatus(t *testing.T) {
100118 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
101119 w.WriteHeader(http.StatusBadRequest)
@@ -382,6 +400,47 @@ func TestClaudeSonnet5Catalog(t *testing.T) {
382400 }
383401 }
384402
403+func TestClaudeFable5Catalog(t *testing.T) {
404+ m, err := FindModel("anthropic", "claude-fable-5")
405+ if err != nil {
406+ t.Fatal(err)
407+ }
408+ if m.DisplayName != "Claude Fable 5" || m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking || m.Speculative {
409+ t.Fatalf("unexpected Fable 5 model: %+v", m)
410+ }
411+ if m.PriceInput != 10 || m.PriceOutput != 50 || m.PriceCacheRead != 1 || m.PriceCacheWrite != 12.5 {
412+ t.Fatalf("unexpected Fable 5 pricing: %+v", m)
413+ }
414+}
415+
416+func TestClaudeFable51Catalog(t *testing.T) {
417+ m, err := FindModel("anthropic", "claude-fable-5-1")
418+ if err != nil {
419+ t.Fatal(err)
420+ }
421+ if m.DisplayName != "Claude Fable 5.1" || m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking || m.Speculative {
422+ t.Fatalf("unexpected Fable 5.1 model: %+v", m)
423+ }
424+ if m.PriceInput != 10 || m.PriceOutput != 50 || m.PriceCacheRead != 0.25 || m.PriceCacheWrite != 12.5 {
425+ t.Fatalf("unexpected Fable 5.1 pricing: %+v", m)
426+ }
427+
428+ wire, err := NewAnthropic("x", "").(*anthropicClient).buildRequest(Request{
429+ Model: m.ID,
430+ Reasoning: "max",
431+ Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}},
432+ })
433+ if err != nil {
434+ t.Fatal(err)
435+ }
436+ if wire.Thinking == nil || wire.Thinking.Type != "adaptive" || wire.Thinking.BudgetTokens != 0 {
437+ t.Fatalf("unexpected Fable 5.1 thinking config: %+v", wire.Thinking)
438+ }
439+ if wire.OutputConfig == nil || wire.OutputConfig.Effort != "max" {
440+ t.Fatalf("unexpected Fable 5.1 output config: %+v", wire.OutputConfig)
441+ }
442+}
443+
385444 func TestGondolaSeedCatalog(t *testing.T) {
386445 kimi, err := FindModel("gondola", "kimi-k3")
387446 if err != nil {
@@ -96,6 +96,24 @@ func TestAnthropicErrorStatus(t *testing.T) {
96 }96 }
97 }97 }
98 98
99+func TestAnthropicOAuthUsesCurrentClaudeCodeVersion(t *testing.T) {
100+ var userAgent string
101+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
102+ userAgent = r.Header.Get("user-agent")
103+ w.WriteHeader(http.StatusBadRequest)
104+ }))
105+ defer srv.Close()
106+
107+ c := NewAnthropicOAuth("test-token", srv.URL)
108+ _, err := c.Stream(context.Background(), Request{Model: "claude-fable-5-1"})
109+ if err == nil {
110+ t.Fatal("expected test server error")
111+ }
112+ if want := "claude-cli/2.1.258"; userAgent != want {
113+ t.Fatalf("user-agent = %q, want %q", userAgent, want)
114+ }
115+}
116+
99 func TestOpenAIErrorStatus(t *testing.T) {117 func TestOpenAIErrorStatus(t *testing.T) {
100 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {118 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
101 w.WriteHeader(http.StatusBadRequest)119 w.WriteHeader(http.StatusBadRequest)
@@ -382,6 +400,47 @@ func TestClaudeSonnet5Catalog(t *testing.T) {
382 }400 }
383 }401 }
384 402
403+func TestClaudeFable5Catalog(t *testing.T) {
404+ m, err := FindModel("anthropic", "claude-fable-5")
405+ if err != nil {
406+ t.Fatal(err)
407+ }
408+ if m.DisplayName != "Claude Fable 5" || m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking || m.Speculative {
409+ t.Fatalf("unexpected Fable 5 model: %+v", m)
410+ }
411+ if m.PriceInput != 10 || m.PriceOutput != 50 || m.PriceCacheRead != 1 || m.PriceCacheWrite != 12.5 {
412+ t.Fatalf("unexpected Fable 5 pricing: %+v", m)
413+ }
414+}
415+
416+func TestClaudeFable51Catalog(t *testing.T) {
417+ m, err := FindModel("anthropic", "claude-fable-5-1")
418+ if err != nil {
419+ t.Fatal(err)
420+ }
421+ if m.DisplayName != "Claude Fable 5.1" || m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking || m.Speculative {
422+ t.Fatalf("unexpected Fable 5.1 model: %+v", m)
423+ }
424+ if m.PriceInput != 10 || m.PriceOutput != 50 || m.PriceCacheRead != 0.25 || m.PriceCacheWrite != 12.5 {
425+ t.Fatalf("unexpected Fable 5.1 pricing: %+v", m)
426+ }
427+
428+ wire, err := NewAnthropic("x", "").(*anthropicClient).buildRequest(Request{
429+ Model: m.ID,
430+ Reasoning: "max",
431+ Messages: []Message{{Role: RoleUser, Content: []Content{TextBlock{Text: "hi"}}}},
432+ })
433+ if err != nil {
434+ t.Fatal(err)
435+ }
436+ if wire.Thinking == nil || wire.Thinking.Type != "adaptive" || wire.Thinking.BudgetTokens != 0 {
437+ t.Fatalf("unexpected Fable 5.1 thinking config: %+v", wire.Thinking)
438+ }
439+ if wire.OutputConfig == nil || wire.OutputConfig.Effort != "max" {
440+ t.Fatalf("unexpected Fable 5.1 output config: %+v", wire.OutputConfig)
441+ }
442+}
443+
385 func TestGondolaSeedCatalog(t *testing.T) {444 func TestGondolaSeedCatalog(t *testing.T) {
386 kimi, err := FindModel("gondola", "kimi-k3")445 kimi, err := FindModel("gondola", "kimi-k3")
387 if err != nil {446 if err != nil {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close