diff --git a/crates/arc-agent/src/cli.rs b/crates/arc-agent/src/cli.rs index 3758e8fb8..27076f279 100644 --- a/crates/arc-agent/src/cli.rs +++ b/crates/arc-agent/src/cli.rs @@ -95,18 +95,6 @@ impl AgentArgs { } } -pub fn default_model(provider: Provider) -> &'static str { - match provider { - Provider::OpenAi => "gpt-5.2-codex", - Provider::Gemini => "gemini-3.1-pro-preview", - Provider::Anthropic => "claude-opus-4-6", - Provider::Kimi => "kimi-k2.5", - Provider::Zai => "glm-4.7", - Provider::Minimax => "minimax-m2.5", - Provider::Inception => "mercury", - } -} - fn tool_category(name: &str) -> &'static str { match name { "read_file" | "read_many_files" | "grep" | "glob" | "list_dir" => "read", @@ -392,12 +380,13 @@ pub async fn run_with_args(args: AgentArgs) -> anyhow::Result<()> { } // Resolve model and build profile - let model = args - .model - .as_deref() - .unwrap_or_else(|| default_model(provider)); + let model = args.model.unwrap_or_else(|| { + arc_llm::catalog::default_model_for_provider(provider.as_str()) + .map(|m| m.id) + .unwrap_or_else(|| provider.as_str().to_string()) + }); eprintln!("{}", styles.dim.apply_to(format!("Using model: {model}"))); - let mut profile = build_profile(provider, model, Some(client.clone())); + let mut profile = build_profile(provider, &model, Some(client.clone())); // Build sandbox let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); @@ -692,43 +681,6 @@ mod tests { assert!(is_auto_approved(PermissionLevel::Full, "shell")); } - // default_model tests - - #[test] - fn default_model_anthropic() { - assert_eq!(default_model(Provider::Anthropic), "claude-opus-4-6"); - } - - #[test] - fn default_model_openai() { - assert_eq!(default_model(Provider::OpenAi), "gpt-5.2-codex"); - } - - #[test] - fn default_model_gemini() { - assert_eq!(default_model(Provider::Gemini), "gemini-3.1-pro-preview"); - } - - #[test] - fn default_model_kimi() { - assert_eq!(default_model(Provider::Kimi), "kimi-k2.5"); - } - - #[test] - fn default_model_zai() { - assert_eq!(default_model(Provider::Zai), "glm-4.7"); - } - - #[test] - fn default_model_minimax() { - assert_eq!(default_model(Provider::Minimax), "minimax-m2.5"); - } - - #[test] - fn default_model_inception() { - assert_eq!(default_model(Provider::Inception), "mercury"); - } - // build_tool_approval non-interactive tests #[test] diff --git a/crates/arc-agent/tests/guardrails.rs b/crates/arc-agent/tests/guardrails.rs index c011aca33..718b39ef3 100644 --- a/crates/arc-agent/tests/guardrails.rs +++ b/crates/arc-agent/tests/guardrails.rs @@ -1,31 +1,15 @@ -use arc_agent::cli::default_model; use arc_agent::{AnthropicProfile, GeminiProfile, OpenAiProfile, ProviderProfile}; use arc_llm::catalog; use arc_llm::provider::Provider; -#[test] -fn every_default_model_exists_in_catalog() { - for &provider in Provider::ALL { - let model = default_model(provider); - assert!( - catalog::get_model_info(model).is_some(), - "default_model for {:?} is '{}' but it is not in the catalog", - provider, - model - ); - } -} - #[test] fn profile_context_window_matches_catalog_for_default_models() { for &provider in Provider::ALL { - let model = default_model(provider); - let catalog_info = catalog::get_model_info(model).unwrap_or_else(|| { - panic!( - "default_model '{}' for {:?} not in catalog", - model, provider - ) - }); + let catalog_info = catalog::default_model_for_provider(provider.as_str()) + .unwrap_or_else(|| { + panic!("no default model for {:?} in catalog", provider) + }); + let model = &catalog_info.id; let profile: Box = match provider { Provider::OpenAi => Box::new(OpenAiProfile::new(model)), diff --git a/crates/arc-llm/src/catalog.json b/crates/arc-llm/src/catalog.json index 39e78d81a..a6ed1ad64 100644 --- a/crates/arc-llm/src/catalog.json +++ b/crates/arc-llm/src/catalog.json @@ -26,6 +26,20 @@ "input_cost_per_million": 3.0, "output_cost_per_million": 15.0, "estimated_output_tps": 50, + "aliases": [] + }, + { + "id": "claude-sonnet-4-6", + "provider": "anthropic", + "display_name": "Claude Sonnet 4.6", + "context_window": 200000, + "max_output": 64000, + "supports_tools": true, + "supports_vision": true, + "supports_reasoning": true, + "input_cost_per_million": 3.0, + "output_cost_per_million": 15.0, + "estimated_output_tps": 50, "aliases": ["sonnet", "claude-sonnet"] }, { @@ -54,8 +68,7 @@ "input_cost_per_million": 1.75, "output_cost_per_million": 14.0, "estimated_output_tps": 65, - "aliases": ["gpt5"], - "default": true + "aliases": ["gpt5"] }, { "id": "gpt-5-mini", @@ -111,7 +124,8 @@ "input_cost_per_million": 2.5, "output_cost_per_million": 15.0, "estimated_output_tps": 70, - "aliases": ["gpt54"] + "aliases": ["gpt54"], + "default": true }, { "id": "gpt-5.4-pro", diff --git a/crates/arc-llm/src/catalog.rs b/crates/arc-llm/src/catalog.rs index 0ecf1ef78..fb57debf2 100644 --- a/crates/arc-llm/src/catalog.rs +++ b/crates/arc-llm/src/catalog.rs @@ -169,7 +169,7 @@ mod tests { assert!(m.default); let m = default_model_for_provider("openai").unwrap(); - assert_eq!(m.id, "gpt-5.2"); + assert_eq!(m.id, "gpt-5.4"); let m = default_model_for_provider("gemini").unwrap(); assert_eq!(m.id, "gemini-3.1-pro-preview"); @@ -221,7 +221,7 @@ mod tests { assert_eq!(info.id, "claude-opus-4-6"); let info = get_model_info("sonnet").unwrap(); - assert_eq!(info.id, "claude-sonnet-4-5"); + assert_eq!(info.id, "claude-sonnet-4-6"); let info = get_model_info("codex").unwrap(); assert_eq!(info.id, "gpt-5.3-codex"); @@ -235,7 +235,7 @@ mod tests { #[test] fn list_models_by_provider() { let anthropic = list_models(Some("anthropic")); - assert_eq!(anthropic.len(), 3); + assert_eq!(anthropic.len(), 4); assert!(anthropic.iter().all(|m| m.provider == "anthropic")); let openai = list_models(Some("openai")); @@ -323,7 +323,7 @@ mod tests { assert!(m.supports_reasoning); assert_eq!(m.input_cost_per_million, Some(2.5)); assert_eq!(m.output_cost_per_million, Some(15.0)); - assert!(!m.default); + assert!(m.default); } #[test] diff --git a/crates/arc-workflows/src/cli/run.rs b/crates/arc-workflows/src/cli/run.rs index c4124b289..bbc065927 100644 --- a/crates/arc-workflows/src/cli/run.rs +++ b/crates/arc-workflows/src/cli/run.rs @@ -35,19 +35,6 @@ use super::{ RunArgs, SandboxProvider, }; -/// Return the default model string for a given provider. -fn default_model_for_provider(provider: Provider) -> &'static str { - match provider { - Provider::Anthropic => "claude-opus-4-6", - Provider::OpenAi => "gpt-5.2", - Provider::Gemini => "gemini-3.1-pro-preview", - Provider::Kimi => "kimi-k2.5", - Provider::Zai => "glm-4.7", - Provider::Minimax => "minimax-m2.5", - Provider::Inception => "mercury", - } -} - /// Resolve model and provider through the full precedence chain: /// CLI flag > TOML config > run defaults > DOT graph attrs > provider-specific defaults. /// Then resolve through the catalog for alias expansion. @@ -87,7 +74,9 @@ fn resolve_model_provider( .as_deref() .and_then(|s| s.parse::().ok()) .unwrap_or(Provider::Anthropic); - default_model_for_provider(provider_enum).to_string() + arc_llm::catalog::default_model_for_provider(provider_enum.as_str()) + .map(|m| m.id) + .unwrap_or_else(|| provider_enum.as_str().to_string()) }); // Resolve model alias through catalog @@ -1502,50 +1491,6 @@ async fn generate_retro( mod tests { use super::*; - #[test] - fn default_model_for_anthropic() { - assert_eq!( - default_model_for_provider(Provider::Anthropic), - "claude-opus-4-6" - ); - } - - #[test] - fn default_model_for_openai() { - assert_eq!(default_model_for_provider(Provider::OpenAi), "gpt-5.2"); - } - - #[test] - fn default_model_for_gemini() { - assert_eq!( - default_model_for_provider(Provider::Gemini), - "gemini-3.1-pro-preview" - ); - } - - #[test] - fn default_model_for_kimi() { - assert_eq!(default_model_for_provider(Provider::Kimi), "kimi-k2.5"); - } - - #[test] - fn default_model_for_zai() { - assert_eq!(default_model_for_provider(Provider::Zai), "glm-4.7"); - } - - #[test] - fn default_model_for_minimax() { - assert_eq!( - default_model_for_provider(Provider::Minimax), - "minimax-m2.5" - ); - } - - #[test] - fn default_model_for_inception() { - assert_eq!(default_model_for_provider(Provider::Inception), "mercury"); - } - #[test] fn resolve_model_provider_defaults() { let graph = crate::graph::types::Graph::new("test");