detached 6237ce5

feat: add Claude Sonnet 5.5 support for Anthropic

commit
6237ce556a337e278c8bf6043d47c91932598df9
author
patriceckhart · · 2026-09-29 20:26 UTC
parent
5ac0009
signature
unsigned
4 files · +93 -2
M README.md +2 -0
@@ -752,6 +752,8 @@ Reasoning levels (`--reasoning off|minimum|low|medium|high|xhigh|max`, also conf
752752
753753 Claude Opus 5.5 is available through Anthropic (`--provider anthropic --model claude-opus-5-5`) and GitHub Copilot (`--provider github-copilot --model claude-opus-5.5`, subject to account availability). Both entries support a 1,000,000-token context window and up to 128,000 output tokens. Adaptive thinking is always on, with `low`, `medium`, `high`, `xhigh`, and `max` effort levels. The default, `off`, and `minimum` requests use `low` instead. The reasoning selector omits unsupported levels. Catalog prices per million tokens are $4 input, $20 output, $0.20 cache reads, and $5 cache writes. Copilot billing remains governed by your plan.
754754
755+Claude Sonnet 5.5 is available through Anthropic (`--provider anthropic --model claude-sonnet-5-5`). It supports a 1,000,000-token context window and up to 128,000 output tokens. Adaptive thinking is always on with the same `low` through `max` effort levels as Opus 5.5, and `off` or `minimum` requests use `low`. Temperature is not sent. Catalog prices per million tokens are $2 input, $10 output, $0.20 cache reads, and $2.50 cache writes.
756+
755757 GPT-6 Sol and GPT-6 Luna are available through OpenAI API keys, OpenAI Codex subscriptions, and GitHub Copilot. Select `gpt-6-sol` or `gpt-6-luna` with `--provider openai`, `openai-responses`, `openai-codex`, or `github-copilot`. Both use the Responses API, support up to 128,000 output tokens, and expose `low` through `max` reasoning. OpenAI and Codex entries use a 272,000-token context window, while Copilot entries use 1,000,000 tokens. Access through subscription providers depends on account availability and organization policy.
756758
757759 You can add additional Gemini model IDs to `models.json` under the `google` provider.
@@ -752,6 +752,8 @@ Reasoning levels (`--reasoning off|minimum|low|medium|high|xhigh|max`, also conf
752 752
753 Claude Opus 5.5 is available through Anthropic (`--provider anthropic --model claude-opus-5-5`) and GitHub Copilot (`--provider github-copilot --model claude-opus-5.5`, subject to account availability). Both entries support a 1,000,000-token context window and up to 128,000 output tokens. Adaptive thinking is always on, with `low`, `medium`, `high`, `xhigh`, and `max` effort levels. The default, `off`, and `minimum` requests use `low` instead. The reasoning selector omits unsupported levels. Catalog prices per million tokens are $4 input, $20 output, $0.20 cache reads, and $5 cache writes. Copilot billing remains governed by your plan.753 Claude Opus 5.5 is available through Anthropic (`--provider anthropic --model claude-opus-5-5`) and GitHub Copilot (`--provider github-copilot --model claude-opus-5.5`, subject to account availability). Both entries support a 1,000,000-token context window and up to 128,000 output tokens. Adaptive thinking is always on, with `low`, `medium`, `high`, `xhigh`, and `max` effort levels. The default, `off`, and `minimum` requests use `low` instead. The reasoning selector omits unsupported levels. Catalog prices per million tokens are $4 input, $20 output, $0.20 cache reads, and $5 cache writes. Copilot billing remains governed by your plan.
754 754
755+Claude Sonnet 5.5 is available through Anthropic (`--provider anthropic --model claude-sonnet-5-5`). It supports a 1,000,000-token context window and up to 128,000 output tokens. Adaptive thinking is always on with the same `low` through `max` effort levels as Opus 5.5, and `off` or `minimum` requests use `low`. Temperature is not sent. Catalog prices per million tokens are $2 input, $10 output, $0.20 cache reads, and $2.50 cache writes.
756+
755 GPT-6 Sol and GPT-6 Luna are available through OpenAI API keys, OpenAI Codex subscriptions, and GitHub Copilot. Select `gpt-6-sol` or `gpt-6-luna` with `--provider openai`, `openai-responses`, `openai-codex`, or `github-copilot`. Both use the Responses API, support up to 128,000 output tokens, and expose `low` through `max` reasoning. OpenAI and Codex entries use a 272,000-token context window, while Copilot entries use 1,000,000 tokens. Access through subscription providers depends on account availability and organization policy.757 GPT-6 Sol and GPT-6 Luna are available through OpenAI API keys, OpenAI Codex subscriptions, and GitHub Copilot. Select `gpt-6-sol` or `gpt-6-luna` with `--provider openai`, `openai-responses`, `openai-codex`, or `github-copilot`. Both use the Responses API, support up to 128,000 output tokens, and expose `low` through `max` reasoning. OpenAI and Codex entries use a 272,000-token context window, while Copilot entries use 1,000,000 tokens. Access through subscription providers depends on account availability and organization policy.
756 758
757 You can add additional Gemini model IDs to `models.json` under the `google` provider.759 You can add additional Gemini model IDs to `models.json` under the `google` provider.
M packages/provider/models.go +5 -0
@@ -344,6 +344,11 @@ var Catalog = []Model{
344344 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
345345 PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5,
346346 },
347+ {
348+ Provider: "anthropic", ID: "claude-sonnet-5-5", DisplayName: "Claude Sonnet 5.5",
349+ ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
350+ PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5,
351+ },
347352 {
348353 Provider: "anthropic", ID: "claude-fable-5", DisplayName: "Claude Fable 5",
349354 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
@@ -344,6 +344,11 @@ var Catalog = []Model{
344 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,344 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
345 PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5,345 PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5,
346 },346 },
347+ {
348+ Provider: "anthropic", ID: "claude-sonnet-5-5", DisplayName: "Claude Sonnet 5.5",
349+ ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
350+ PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5,
351+ },
347 {352 {
348 Provider: "anthropic", ID: "claude-fable-5", DisplayName: "Claude Fable 5",353 Provider: "anthropic", ID: "claude-fable-5", DisplayName: "Claude Fable 5",
349 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,354 ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true,
M packages/provider/reasoning.go +14 -2
@@ -44,8 +44,9 @@ func defaultReasoningLevels(model Model) []string {
4444 }
4545
4646 id := strings.ToLower(model.ID)
47- // Opus 5.5 always uses adaptive thinking, including at its lowest effort.
48- if id == "claude-opus-5-5" || id == "claude-opus-5.5" {
47+ // Opus 5.5 and Sonnet 5.5 always use adaptive thinking, including at
48+ // their lowest effort.
49+ if alwaysOnAdaptiveThinking(id) {
4950 return []string{"low", "medium", "high", "xhigh", "max"}
5051 }
5152 if (model.Provider == "google" || model.Provider == "google-vertex") && strings.Contains(id, "gemini-3") {
@@ -86,6 +87,17 @@ func defaultReasoningLevels(model Model) []string {
8687 return []string{"", "low", "medium", "high"}
8788 }
8889
90+// alwaysOnAdaptiveThinking reports whether a Claude model rejects thinking
91+// being turned off. Both the hyphenated Anthropic ID and the dotted Copilot
92+// ID are recognised.
93+func alwaysOnAdaptiveThinking(id string) bool {
94+ switch id {
95+ case "claude-opus-5-5", "claude-opus-5.5", "claude-sonnet-5-5", "claude-sonnet-5.5":
96+ return true
97+ }
98+ return false
99+}
100+
89101 func containsReasoningLevel(levels []string, target string) bool {
90102 for _, level := range levels {
91103 if level == target {
@@ -44,8 +44,9 @@ func defaultReasoningLevels(model Model) []string {
44 }44 }
45 45
46 id := strings.ToLower(model.ID)46 id := strings.ToLower(model.ID)
47- // Opus 5.5 always uses adaptive thinking, including at its lowest effort.47+ // Opus 5.5 and Sonnet 5.5 always use adaptive thinking, including at
48- if id == "claude-opus-5-5" || id == "claude-opus-5.5" {48+ // their lowest effort.
49+ if alwaysOnAdaptiveThinking(id) {
49 return []string{"low", "medium", "high", "xhigh", "max"}50 return []string{"low", "medium", "high", "xhigh", "max"}
50 }51 }
51 if (model.Provider == "google" || model.Provider == "google-vertex") && strings.Contains(id, "gemini-3") {52 if (model.Provider == "google" || model.Provider == "google-vertex") && strings.Contains(id, "gemini-3") {
@@ -86,6 +87,17 @@ func defaultReasoningLevels(model Model) []string {
86 return []string{"", "low", "medium", "high"}87 return []string{"", "low", "medium", "high"}
87 }88 }
88 89
90+// alwaysOnAdaptiveThinking reports whether a Claude model rejects thinking
91+// being turned off. Both the hyphenated Anthropic ID and the dotted Copilot
92+// ID are recognised.
93+func alwaysOnAdaptiveThinking(id string) bool {
94+ switch id {
95+ case "claude-opus-5-5", "claude-opus-5.5", "claude-sonnet-5-5", "claude-sonnet-5.5":
96+ return true
97+ }
98+ return false
99+}
100+
89 func containsReasoningLevel(levels []string, target string) bool {101 func containsReasoningLevel(levels []string, target string) bool {
90 for _, level := range levels {102 for _, level := range levels {
91 if level == target {103 if level == target {
A packages/provider/sonnet55_test.go +72 -0
@@ -0,0 +1,72 @@
1+package provider
2+
3+import (
4+ "slices"
5+ "testing"
6+)
7+
8+func TestSonnet55CatalogAndThinking(t *testing.T) {
9+ for _, tc := range []struct {
10+ name string
11+ client *anthropicClient
12+ }{
13+ {"api-key", NewAnthropic("synthetic-key", "").(*anthropicClient)},
14+ {"oauth", NewAnthropicOAuth("synthetic-token", "").(*anthropicClient)},
15+ } {
16+ t.Run(tc.name, func(t *testing.T) {
17+ const id = "claude-sonnet-5-5"
18+ m, err := FindModel("anthropic", id)
19+ if err != nil {
20+ t.Fatal(err)
21+ }
22+ if m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking || m.Speculative {
23+ t.Fatalf("unexpected capabilities: %+v", m)
24+ }
25+ if m.PriceInput != 2 || m.PriceOutput != 10 || m.PriceCacheRead != 0.2 || m.PriceCacheWrite != 2.5 {
26+ t.Fatalf("unexpected pricing: %+v", m)
27+ }
28+ want := []string{"low", "medium", "high", "xhigh", "max"}
29+ if got := AvailableReasoningLevels(m); !slices.Equal(got, want) {
30+ t.Fatalf("levels = %q, want %q", got, want)
31+ }
32+ for _, level := range []string{"", "off", "minimum", "low", "medium", "high", "xhigh", "max"} {
33+ effort := level
34+ if level == "" || level == "off" || level == "minimum" {
35+ effort = "low"
36+ }
37+ if got := ClampReasoningForModel(m, level); got != effort {
38+ t.Errorf("clamp(%q) = %q, want %q", level, got, effort)
39+ }
40+ temperature := float32(0.5)
41+ wire, err := tc.client.buildRequest(Request{Model: id, Reasoning: level, Temperature: &temperature})
42+ if err != nil {
43+ t.Fatal(err)
44+ }
45+ if wire.Model != id || wire.MaxTokens != 128000 || wire.Temperature != nil {
46+ t.Fatalf("unexpected request: %+v", wire)
47+ }
48+ if wire.Thinking == nil || wire.Thinking.Type != "adaptive" || wire.Thinking.BudgetTokens != 0 {
49+ t.Fatalf("thinking = %+v", wire.Thinking)
50+ }
51+ if wire.OutputConfig == nil || wire.OutputConfig.Effort != effort {
52+ t.Fatalf("output config = %+v, want effort %q", wire.OutputConfig, effort)
53+ }
54+ }
55+ })
56+ }
57+}
58+
59+func TestSonnet55ReasoningOverrides(t *testing.T) {
60+ m, err := FindModel("anthropic", "claude-sonnet-5-5")
61+ if err != nil {
62+ t.Fatal(err)
63+ }
64+ m.ReasoningLevelMap = map[string]string{"max": "xhigh"}
65+ want := []string{"low", "medium", "high", "xhigh"}
66+ if got := AvailableReasoningLevels(m); !slices.Equal(got, want) {
67+ t.Fatalf("levels = %q, want %q", got, want)
68+ }
69+ if got := ClampReasoningForModel(m, "off"); got != "low" {
70+ t.Fatalf("off clamped to %q, want low", got)
71+ }
72+}
@@ -0,0 +1,72 @@
1+package provider
2+
3+import (
4+ "slices"
5+ "testing"
6+)
7+
8+func TestSonnet55CatalogAndThinking(t *testing.T) {
9+ for _, tc := range []struct {
10+ name string
11+ client *anthropicClient
12+ }{
13+ {"api-key", NewAnthropic("synthetic-key", "").(*anthropicClient)},
14+ {"oauth", NewAnthropicOAuth("synthetic-token", "").(*anthropicClient)},
15+ } {
16+ t.Run(tc.name, func(t *testing.T) {
17+ const id = "claude-sonnet-5-5"
18+ m, err := FindModel("anthropic", id)
19+ if err != nil {
20+ t.Fatal(err)
21+ }
22+ if m.ContextWindow != 1000000 || m.MaxOutput != 128000 || !m.Reasoning || !m.AdaptiveThinking || m.Speculative {
23+ t.Fatalf("unexpected capabilities: %+v", m)
24+ }
25+ if m.PriceInput != 2 || m.PriceOutput != 10 || m.PriceCacheRead != 0.2 || m.PriceCacheWrite != 2.5 {
26+ t.Fatalf("unexpected pricing: %+v", m)
27+ }
28+ want := []string{"low", "medium", "high", "xhigh", "max"}
29+ if got := AvailableReasoningLevels(m); !slices.Equal(got, want) {
30+ t.Fatalf("levels = %q, want %q", got, want)
31+ }
32+ for _, level := range []string{"", "off", "minimum", "low", "medium", "high", "xhigh", "max"} {
33+ effort := level
34+ if level == "" || level == "off" || level == "minimum" {
35+ effort = "low"
36+ }
37+ if got := ClampReasoningForModel(m, level); got != effort {
38+ t.Errorf("clamp(%q) = %q, want %q", level, got, effort)
39+ }
40+ temperature := float32(0.5)
41+ wire, err := tc.client.buildRequest(Request{Model: id, Reasoning: level, Temperature: &temperature})
42+ if err != nil {
43+ t.Fatal(err)
44+ }
45+ if wire.Model != id || wire.MaxTokens != 128000 || wire.Temperature != nil {
46+ t.Fatalf("unexpected request: %+v", wire)
47+ }
48+ if wire.Thinking == nil || wire.Thinking.Type != "adaptive" || wire.Thinking.BudgetTokens != 0 {
49+ t.Fatalf("thinking = %+v", wire.Thinking)
50+ }
51+ if wire.OutputConfig == nil || wire.OutputConfig.Effort != effort {
52+ t.Fatalf("output config = %+v, want effort %q", wire.OutputConfig, effort)
53+ }
54+ }
55+ })
56+ }
57+}
58+
59+func TestSonnet55ReasoningOverrides(t *testing.T) {
60+ m, err := FindModel("anthropic", "claude-sonnet-5-5")
61+ if err != nil {
62+ t.Fatal(err)
63+ }
64+ m.ReasoningLevelMap = map[string]string{"max": "xhigh"}
65+ want := []string{"low", "medium", "high", "xhigh"}
66+ if got := AvailableReasoningLevels(m); !slices.Equal(got, want) {
67+ t.Fatalf("levels = %q, want %q", got, want)
68+ }
69+ if got := ClampReasoningForModel(m, "off"); got != "low" {
70+ t.Fatalf("off clamped to %q, want low", got)
71+ }
72+}

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 477

↑↓ moveenter openesc close