Skip to content

Commit 18d4628

Browse files
authored
Add OpenAI GPT-5.6 models (#566)
## Summary - add GPT-5.6 Sol, Terra, and Luna to the built-in OpenAI model catalog with current capabilities, limits, aliases, and pricing - route all three models through the OpenAI Responses API and keep the Codex-safe 272K context policy - make GPT-5.6 Sol the OpenAI default and update the model docs and Ask Fabro API contract ## Testing - cargo nextest run -p fabro-model - cargo nextest run -p fabro-llm builtin_catalog_route_equivalence_table - cargo nextest run -p fabro-server list_models - cargo nextest run -p fabro-server --features test-support run_responses_include_ask_fabro_affordance - cargo +nightly-2026-04-14 fmt --check --all - cargo +nightly-2026-04-14 clippy -p fabro-model -p fabro-llm --all-targets -- -D warnings - git diff --check
1 parent 96372c5 commit 18d4628

6 files changed

Lines changed: 110 additions & 15 deletions

File tree

docs/public/core-concepts/models.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ No single model is best at everything. Fabro lets you assign the right model to
2020
| `claude-sonnet-4-6` | anthropic | `sonnet`, `claude-sonnet` | 200K | $3.00 / $15.00 | 50 tok/s |
2121
| `claude-sonnet-4-5` | anthropic | | 200K | $3.00 / $15.00 | 50 tok/s |
2222
| `claude-haiku-4-5` | anthropic | `haiku`, `claude-haiku` | 200K | $0.80 / $4.00 | 100 tok/s |
23+
| `gpt-5.6-sol` | openai | `gpt-5.6`, `gpt56-sol` | 272K | $5.00 / $30.00 | n/a |
24+
| `gpt-5.6-terra` | openai | `gpt56-terra` | 272K | $2.50 / $15.00 | n/a |
25+
| `gpt-5.6-luna` | openai | `gpt56-luna` | 272K | $1.00 / $6.00 | n/a |
2326
| `gpt-5.4` | openai | `gpt54`, `gpt5`, `codex` | 272K | $2.50 / $15.00 | 70 tok/s |
2427
| `gpt-5.5` | openai | `gpt55` | 272K | $5.00 / $30.00 | 70 tok/s |
2528
| `gpt-5.5-pro` | openai | `gpt55-pro` | 1M | $30.00 / $180.00 | 20 tok/s |
@@ -169,7 +172,7 @@ When no model or provider is specified, Fabro checks configured provider credent
169172
| Provider | Default model |
170173
|---|---|
171174
| `anthropic` | `claude-sonnet-4-6` |
172-
| `openai` | `gpt-5.5` |
175+
| `openai` | `gpt-5.6-sol` |
173176
| `gemini` | `gemini-3.5-flash` |
174177
| `kimi` | `kimi-k2.5` |
175178
| `zai` | `glm-4.7` |

lib/crates/fabro-cli/tests/it/cmd/attach.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ fn normalize_attach_json_progress_event(mut event: Value) -> Value {
100100
);
101101
}
102102
}
103+
if let Some(model_name) = event.pointer_mut("/properties/settings/run/model/name") {
104+
assert!(
105+
model_name.is_string(),
106+
"default model should serialize as a string"
107+
);
108+
*model_name = Value::String("[DEFAULT_MODEL]".to_string());
109+
}
103110
event
104111
}
105112

@@ -991,7 +998,7 @@ fn attach_json_errors_without_prompting_for_human_input() {
991998
"speed": null
992999
},
9931000
"fallbacks": [],
994-
"name": "gpt-5.5",
1001+
"name": "[DEFAULT_MODEL]",
9951002
"provider": "openai"
9961003
},
9971004
"notifications": {},

lib/crates/fabro-llm/src/adapter_registry.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,9 @@ mod tests {
306306
("gpt-5.4-pro", "gpt-5.4-pro", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi),
307307
("gpt-5.5", "gpt-5.5", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi),
308308
("gpt-5.5-pro", "gpt-5.5-pro", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi),
309+
("gpt-5.6-luna", "gpt-5.6-luna", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi),
310+
("gpt-5.6-sol", "gpt-5.6-sol", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi),
311+
("gpt-5.6-terra", "gpt-5.6-terra", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi),
309312
("kimi-k2.5", "kimi-k2.5", T::OpenAiCompatible, C::OpenAiCompatible, B::OpenAi, P::OpenAi),
310313
("mercury-2", "mercury-2", T::OpenAiCompatible, C::OpenAiCompatible, B::OpenAi, P::OpenAi),
311314
("minimax-m2.5", "minimax-m2.5", T::OpenAiCompatible, C::OpenAiCompatible, B::OpenAi, P::OpenAi),

lib/crates/fabro-model/src/catalog.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,7 +2137,8 @@ enabled = true
21372137
let m = Catalog::builtin()
21382138
.default_for_provider(&ProviderId::openai())
21392139
.unwrap();
2140-
assert_eq!(m.id, "gpt-5.5");
2140+
assert_eq!(m.provider, ProviderId::openai());
2141+
assert!(m.default);
21412142

21422143
let m = Catalog::builtin()
21432144
.default_for_provider(&ProviderId::gemini())
@@ -2910,12 +2911,10 @@ probe = false
29102911
))
29112912
.expect("sparse built-in model override should build");
29122913

2914+
let openai = ProviderId::openai();
29132915
assert_eq!(
2914-
catalog
2915-
.probe_for_provider(&ProviderId::openai())
2916-
.unwrap()
2917-
.id,
2918-
"gpt-5.5"
2916+
catalog.probe_for_provider(&openai).unwrap().id,
2917+
catalog.default_for_provider(&openai).unwrap().id
29192918
);
29202919
}
29212920

@@ -3319,12 +3318,10 @@ small_default = false
33193318
))
33203319
.expect("sparse built-in model override should build");
33213320

3321+
let openai = ProviderId::openai();
33223322
assert_eq!(
3323-
catalog
3324-
.small_default_for_provider(&ProviderId::openai())
3325-
.unwrap()
3326-
.id,
3327-
"gpt-5.5"
3323+
catalog.small_default_for_provider(&openai).unwrap().id,
3324+
catalog.default_for_provider(&openai).unwrap().id
33283325
);
33293326
}
33303327

@@ -4447,6 +4444,9 @@ sampling_params = false
44474444
"gpt-5.4",
44484445
"gpt-5.4-mini",
44494446
"gpt-5.5",
4447+
"gpt-5.6-luna",
4448+
"gpt-5.6-sol",
4449+
"gpt-5.6-terra",
44504450
] {
44514451
assert_eq!(
44524452
catalog.get(model).unwrap().context_window(),

lib/crates/fabro-model/src/catalog/providers/openai.toml

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,82 @@ priority = 90
88
[providers.openai.auth]
99
credentials = ["env:OPENAI_API_KEY", "vault:OPENAI_API_KEY", "vault:OPENAI_CODEX"]
1010

11+
[models."gpt-5.6-sol"]
12+
provider = "openai"
13+
api_id = "gpt-5.6-sol"
14+
display_name = "GPT-5.6 Sol"
15+
family = "gpt-5"
16+
training = "2026-02-16"
17+
knowledge_cutoff = "February 16, 2026"
18+
default = true
19+
aliases = ["gpt56-sol", "gpt-56-sol", "gpt-5.6", "gpt56", "gpt-56"]
20+
21+
[models."gpt-5.6-sol".limits]
22+
context_window = 272000
23+
max_output = 128000
24+
25+
[models."gpt-5.6-sol".features]
26+
tools = true
27+
vision = true
28+
reasoning = true
29+
reasoning_effort = "levels"
30+
prompt_cache = true
31+
32+
[models."gpt-5.6-sol".costs]
33+
input_cost_per_mtok = 5.0
34+
output_cost_per_mtok = 30.0
35+
cache_input_cost_per_mtok = 0.5
36+
37+
[models."gpt-5.6-terra"]
38+
provider = "openai"
39+
api_id = "gpt-5.6-terra"
40+
display_name = "GPT-5.6 Terra"
41+
family = "gpt-5"
42+
training = "2026-02-16"
43+
knowledge_cutoff = "February 16, 2026"
44+
aliases = ["gpt56-terra", "gpt-56-terra"]
45+
46+
[models."gpt-5.6-terra".limits]
47+
context_window = 272000
48+
max_output = 128000
49+
50+
[models."gpt-5.6-terra".features]
51+
tools = true
52+
vision = true
53+
reasoning = true
54+
reasoning_effort = "levels"
55+
prompt_cache = true
56+
57+
[models."gpt-5.6-terra".costs]
58+
input_cost_per_mtok = 2.5
59+
output_cost_per_mtok = 15.0
60+
cache_input_cost_per_mtok = 0.25
61+
62+
[models."gpt-5.6-luna"]
63+
provider = "openai"
64+
api_id = "gpt-5.6-luna"
65+
display_name = "GPT-5.6 Luna"
66+
family = "gpt-5"
67+
training = "2026-02-16"
68+
knowledge_cutoff = "February 16, 2026"
69+
aliases = ["gpt56-luna", "gpt-56-luna"]
70+
71+
[models."gpt-5.6-luna".limits]
72+
context_window = 272000
73+
max_output = 128000
74+
75+
[models."gpt-5.6-luna".features]
76+
tools = true
77+
vision = true
78+
reasoning = true
79+
reasoning_effort = "levels"
80+
prompt_cache = true
81+
82+
[models."gpt-5.6-luna".costs]
83+
input_cost_per_mtok = 1.0
84+
output_cost_per_mtok = 6.0
85+
cache_input_cost_per_mtok = 0.1
86+
1187
[models."gpt-5.4"]
1288
provider = "openai"
1389
api_id = "gpt-5.4"
@@ -40,7 +116,6 @@ display_name = "GPT-5.5"
40116
family = "gpt-5"
41117
training = "2025-12-01"
42118
knowledge_cutoff = "December 2025"
43-
default = true
44119
estimated_output_tps = 70
45120
aliases = ["gpt55", "gpt-55"]
46121

lib/crates/fabro-server/tests/it/api/runs.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use axum::body::Body;
22
use axum::http::{Request, StatusCode};
3+
use fabro_model::{Catalog, ProviderId};
34
use fabro_types::settings::run::EnvironmentProvider;
45
use tower::ServiceExt;
56

@@ -160,7 +161,13 @@ _version = 1
160161
created["ask_fabro"]["unavailable_reason"],
161162
"sandbox_not_ready"
162163
);
163-
assert_eq!(created["ask_fabro"]["default_model"], "gpt-5.5");
164+
let default_openai_model = Catalog::builtin()
165+
.default_for_provider(&ProviderId::openai())
166+
.expect("the built-in OpenAI provider should have a default model");
167+
assert_eq!(
168+
created["ask_fabro"]["default_model"].as_str(),
169+
Some(default_openai_model.id())
170+
);
164171

165172
let get_request = Request::builder()
166173
.method("GET")

0 commit comments

Comments
 (0)