diff --git a/docs/public/core-concepts/models.mdx b/docs/public/core-concepts/models.mdx index 67ac32823..604cac913 100644 --- a/docs/public/core-concepts/models.mdx +++ b/docs/public/core-concepts/models.mdx @@ -20,6 +20,9 @@ No single model is best at everything. Fabro lets you assign the right model to | `claude-sonnet-4-6` | anthropic | `sonnet`, `claude-sonnet` | 200K | $3.00 / $15.00 | 50 tok/s | | `claude-sonnet-4-5` | anthropic | | 200K | $3.00 / $15.00 | 50 tok/s | | `claude-haiku-4-5` | anthropic | `haiku`, `claude-haiku` | 200K | $0.80 / $4.00 | 100 tok/s | +| `gpt-5.6-sol` | openai | `gpt-5.6`, `gpt56-sol` | 272K | $5.00 / $30.00 | n/a | +| `gpt-5.6-terra` | openai | `gpt56-terra` | 272K | $2.50 / $15.00 | n/a | +| `gpt-5.6-luna` | openai | `gpt56-luna` | 272K | $1.00 / $6.00 | n/a | | `gpt-5.4` | openai | `gpt54`, `gpt5`, `codex` | 272K | $2.50 / $15.00 | 70 tok/s | | `gpt-5.5` | openai | `gpt55` | 272K | $5.00 / $30.00 | 70 tok/s | | `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 | Provider | Default model | |---|---| | `anthropic` | `claude-sonnet-4-6` | -| `openai` | `gpt-5.5` | +| `openai` | `gpt-5.6-sol` | | `gemini` | `gemini-3.5-flash` | | `kimi` | `kimi-k2.5` | | `zai` | `glm-4.7` | diff --git a/lib/crates/fabro-cli/tests/it/cmd/attach.rs b/lib/crates/fabro-cli/tests/it/cmd/attach.rs index 327eab2d2..fff46c251 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/attach.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/attach.rs @@ -100,6 +100,13 @@ fn normalize_attach_json_progress_event(mut event: Value) -> Value { ); } } + if let Some(model_name) = event.pointer_mut("/properties/settings/run/model/name") { + assert!( + model_name.is_string(), + "default model should serialize as a string" + ); + *model_name = Value::String("[DEFAULT_MODEL]".to_string()); + } event } @@ -991,7 +998,7 @@ fn attach_json_errors_without_prompting_for_human_input() { "speed": null }, "fallbacks": [], - "name": "gpt-5.5", + "name": "[DEFAULT_MODEL]", "provider": "openai" }, "notifications": {}, diff --git a/lib/crates/fabro-llm/src/adapter_registry.rs b/lib/crates/fabro-llm/src/adapter_registry.rs index f37f80fa9..21ce3b69a 100644 --- a/lib/crates/fabro-llm/src/adapter_registry.rs +++ b/lib/crates/fabro-llm/src/adapter_registry.rs @@ -306,6 +306,9 @@ mod tests { ("gpt-5.4-pro", "gpt-5.4-pro", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi), ("gpt-5.5", "gpt-5.5", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi), ("gpt-5.5-pro", "gpt-5.5-pro", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi), + ("gpt-5.6-luna", "gpt-5.6-luna", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi), + ("gpt-5.6-sol", "gpt-5.6-sol", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi), + ("gpt-5.6-terra", "gpt-5.6-terra", T::OpenAi, C::OpenAiResponses, B::OpenAi, P::OpenAi), ("kimi-k2.5", "kimi-k2.5", T::OpenAiCompatible, C::OpenAiCompatible, B::OpenAi, P::OpenAi), ("mercury-2", "mercury-2", T::OpenAiCompatible, C::OpenAiCompatible, B::OpenAi, P::OpenAi), ("minimax-m2.5", "minimax-m2.5", T::OpenAiCompatible, C::OpenAiCompatible, B::OpenAi, P::OpenAi), diff --git a/lib/crates/fabro-model/src/catalog.rs b/lib/crates/fabro-model/src/catalog.rs index 2cd44e28e..5d3375f21 100644 --- a/lib/crates/fabro-model/src/catalog.rs +++ b/lib/crates/fabro-model/src/catalog.rs @@ -2137,7 +2137,8 @@ enabled = true let m = Catalog::builtin() .default_for_provider(&ProviderId::openai()) .unwrap(); - assert_eq!(m.id, "gpt-5.5"); + assert_eq!(m.provider, ProviderId::openai()); + assert!(m.default); let m = Catalog::builtin() .default_for_provider(&ProviderId::gemini()) @@ -2910,12 +2911,10 @@ probe = false )) .expect("sparse built-in model override should build"); + let openai = ProviderId::openai(); assert_eq!( - catalog - .probe_for_provider(&ProviderId::openai()) - .unwrap() - .id, - "gpt-5.5" + catalog.probe_for_provider(&openai).unwrap().id, + catalog.default_for_provider(&openai).unwrap().id ); } @@ -3319,12 +3318,10 @@ small_default = false )) .expect("sparse built-in model override should build"); + let openai = ProviderId::openai(); assert_eq!( - catalog - .small_default_for_provider(&ProviderId::openai()) - .unwrap() - .id, - "gpt-5.5" + catalog.small_default_for_provider(&openai).unwrap().id, + catalog.default_for_provider(&openai).unwrap().id ); } @@ -4447,6 +4444,9 @@ sampling_params = false "gpt-5.4", "gpt-5.4-mini", "gpt-5.5", + "gpt-5.6-luna", + "gpt-5.6-sol", + "gpt-5.6-terra", ] { assert_eq!( catalog.get(model).unwrap().context_window(), diff --git a/lib/crates/fabro-model/src/catalog/providers/openai.toml b/lib/crates/fabro-model/src/catalog/providers/openai.toml index 65bd2fdbc..56fe7a03c 100644 --- a/lib/crates/fabro-model/src/catalog/providers/openai.toml +++ b/lib/crates/fabro-model/src/catalog/providers/openai.toml @@ -8,6 +8,82 @@ priority = 90 [providers.openai.auth] credentials = ["env:OPENAI_API_KEY", "vault:OPENAI_API_KEY", "vault:OPENAI_CODEX"] +[models."gpt-5.6-sol"] +provider = "openai" +api_id = "gpt-5.6-sol" +display_name = "GPT-5.6 Sol" +family = "gpt-5" +training = "2026-02-16" +knowledge_cutoff = "February 16, 2026" +default = true +aliases = ["gpt56-sol", "gpt-56-sol", "gpt-5.6", "gpt56", "gpt-56"] + +[models."gpt-5.6-sol".limits] +context_window = 272000 +max_output = 128000 + +[models."gpt-5.6-sol".features] +tools = true +vision = true +reasoning = true +reasoning_effort = "levels" +prompt_cache = true + +[models."gpt-5.6-sol".costs] +input_cost_per_mtok = 5.0 +output_cost_per_mtok = 30.0 +cache_input_cost_per_mtok = 0.5 + +[models."gpt-5.6-terra"] +provider = "openai" +api_id = "gpt-5.6-terra" +display_name = "GPT-5.6 Terra" +family = "gpt-5" +training = "2026-02-16" +knowledge_cutoff = "February 16, 2026" +aliases = ["gpt56-terra", "gpt-56-terra"] + +[models."gpt-5.6-terra".limits] +context_window = 272000 +max_output = 128000 + +[models."gpt-5.6-terra".features] +tools = true +vision = true +reasoning = true +reasoning_effort = "levels" +prompt_cache = true + +[models."gpt-5.6-terra".costs] +input_cost_per_mtok = 2.5 +output_cost_per_mtok = 15.0 +cache_input_cost_per_mtok = 0.25 + +[models."gpt-5.6-luna"] +provider = "openai" +api_id = "gpt-5.6-luna" +display_name = "GPT-5.6 Luna" +family = "gpt-5" +training = "2026-02-16" +knowledge_cutoff = "February 16, 2026" +aliases = ["gpt56-luna", "gpt-56-luna"] + +[models."gpt-5.6-luna".limits] +context_window = 272000 +max_output = 128000 + +[models."gpt-5.6-luna".features] +tools = true +vision = true +reasoning = true +reasoning_effort = "levels" +prompt_cache = true + +[models."gpt-5.6-luna".costs] +input_cost_per_mtok = 1.0 +output_cost_per_mtok = 6.0 +cache_input_cost_per_mtok = 0.1 + [models."gpt-5.4"] provider = "openai" api_id = "gpt-5.4" @@ -40,7 +116,6 @@ display_name = "GPT-5.5" family = "gpt-5" training = "2025-12-01" knowledge_cutoff = "December 2025" -default = true estimated_output_tps = 70 aliases = ["gpt55", "gpt-55"] diff --git a/lib/crates/fabro-server/tests/it/api/runs.rs b/lib/crates/fabro-server/tests/it/api/runs.rs index 9e28d86ae..ecdf66ebc 100644 --- a/lib/crates/fabro-server/tests/it/api/runs.rs +++ b/lib/crates/fabro-server/tests/it/api/runs.rs @@ -1,5 +1,6 @@ use axum::body::Body; use axum::http::{Request, StatusCode}; +use fabro_model::{Catalog, ProviderId}; use fabro_types::settings::run::EnvironmentProvider; use tower::ServiceExt; @@ -160,7 +161,13 @@ _version = 1 created["ask_fabro"]["unavailable_reason"], "sandbox_not_ready" ); - assert_eq!(created["ask_fabro"]["default_model"], "gpt-5.5"); + let default_openai_model = Catalog::builtin() + .default_for_provider(&ProviderId::openai()) + .expect("the built-in OpenAI provider should have a default model"); + assert_eq!( + created["ask_fabro"]["default_model"].as_str(), + Some(default_openai_model.id()) + ); let get_request = Request::builder() .method("GET")