diff --git a/crates/forge_app/src/dto/openai/transformers/pipeline.rs b/crates/forge_app/src/dto/openai/transformers/pipeline.rs index 7b1c309c0e..ad1693cbaa 100644 --- a/crates/forge_app/src/dto/openai/transformers/pipeline.rs +++ b/crates/forge_app/src/dto/openai/transformers/pipeline.rs @@ -72,7 +72,9 @@ impl Transformer for ProviderPipeline<'_> { let strict_schema = EnforceStrictToolSchema .pipe(EnforceStrictResponseFormatSchema) .when(move |_| { - provider.id == ProviderId::FIREWORKS_AI || provider.id == ProviderId::OPENCODE_ZEN + provider.id == ProviderId::FIREWORKS_AI + || provider.id == ProviderId::OPENCODE_ZEN + || provider.id == ProviderId::OPENCODE_GO }); let mut combined = zai_thinking diff --git a/crates/forge_domain/src/provider.rs b/crates/forge_domain/src/provider.rs index a65b43e416..371b74b31e 100644 --- a/crates/forge_domain/src/provider.rs +++ b/crates/forge_domain/src/provider.rs @@ -70,6 +70,7 @@ impl ProviderId { pub const MINIMAX: ProviderId = ProviderId(Cow::Borrowed("minimax")); pub const CODEX: ProviderId = ProviderId(Cow::Borrowed("codex")); pub const OPENCODE_ZEN: ProviderId = ProviderId(Cow::Borrowed("opencode_zen")); + pub const OPENCODE_GO: ProviderId = ProviderId(Cow::Borrowed("opencode_go")); pub const FIREWORKS_AI: ProviderId = ProviderId(Cow::Borrowed("fireworks-ai")); pub const NOVITA: ProviderId = ProviderId(Cow::Borrowed("novita")); pub const GOOGLE_AI_STUDIO: ProviderId = ProviderId(Cow::Borrowed("google_ai_studio")); @@ -103,6 +104,7 @@ impl ProviderId { ProviderId::MINIMAX, ProviderId::CODEX, ProviderId::OPENCODE_ZEN, + ProviderId::OPENCODE_GO, ProviderId::FIREWORKS_AI, ProviderId::NOVITA, ProviderId::GOOGLE_AI_STUDIO, @@ -129,6 +131,8 @@ impl ProviderId { "io_intelligence" => "IOIntelligence".to_string(), "minimax" => "MiniMax".to_string(), "codex" => "Codex".to_string(), + "opencode_zen" => "OpenCode Zen".to_string(), + "opencode_go" => "OpenCode Go".to_string(), "fireworks-ai" => "FireworksAI".to_string(), "novita" => "Novita".to_string(), "google_ai_studio" => "GoogleAIStudio".to_string(), @@ -174,6 +178,7 @@ impl std::str::FromStr for ProviderId { "io_intelligence" => ProviderId::IO_INTELLIGENCE, "minimax" => ProviderId::MINIMAX, "codex" => ProviderId::CODEX, + "opencode_go" => ProviderId::OPENCODE_GO, "fireworks-ai" => ProviderId::FIREWORKS_AI, "novita" => ProviderId::NOVITA, "google_ai_studio" => ProviderId::GOOGLE_AI_STUDIO, @@ -548,6 +553,8 @@ mod tests { assert_eq!(ProviderId::IO_INTELLIGENCE.to_string(), "IOIntelligence"); assert_eq!(ProviderId::CODEX.to_string(), "Codex"); assert_eq!(ProviderId::FIREWORKS_AI.to_string(), "FireworksAI"); + assert_eq!(ProviderId::OPENCODE_ZEN.to_string(), "OpenCode Zen"); + assert_eq!(ProviderId::OPENCODE_GO.to_string(), "OpenCode Go"); assert_eq!(ProviderId::GOOGLE_AI_STUDIO.to_string(), "GoogleAIStudio"); } @@ -565,12 +572,20 @@ mod tests { assert_eq!(actual, expected); } + #[test] + fn test_opencode_go_from_str() { + let actual = ProviderId::from_str("opencode_go").unwrap(); + let expected = ProviderId::OPENCODE_GO; + assert_eq!(actual, expected); + } + #[test] fn test_codex_in_built_in_providers() { let built_in = ProviderId::built_in_providers(); assert!(built_in.contains(&ProviderId::CODEX)); assert!(built_in.contains(&ProviderId::OPENAI_RESPONSES_COMPATIBLE)); assert!(built_in.contains(&ProviderId::FIREWORKS_AI)); + assert!(built_in.contains(&ProviderId::OPENCODE_GO)); assert!(built_in.contains(&ProviderId::GOOGLE_AI_STUDIO)); } diff --git a/crates/forge_repo/src/provider/provider.json b/crates/forge_repo/src/provider/provider.json index 16dc7899f6..af4d342058 100644 --- a/crates/forge_repo/src/provider/provider.json +++ b/crates/forge_repo/src/provider/provider.json @@ -2812,6 +2812,76 @@ ], "auth_methods": ["api_key"] }, + { + "id": "opencode_go", + "api_key_vars": "OPENCODE_API_KEY", + "url_param_vars": [], + "response_type": "OpenCode", + "url": "https://opencode.ai/zen/go/v1/chat/completions", + "models": [ + { + "id": "glm-5", + "name": "GLM 5", + "description": "Zhipu AI's flagship model with 204K context, reasoning, and tool calling capabilities", + "context_length": 204800, + "tools_supported": true, + "supports_parallel_tool_calls": true, + "supports_reasoning": true, + "input_modalities": ["text"] + }, + { + "id": "kimi-k2.5", + "name": "Kimi K2.5", + "description": "Moonshot AI's flagship model with 262K context, vision, and reasoning capabilities", + "context_length": 262144, + "tools_supported": true, + "supports_parallel_tool_calls": true, + "supports_reasoning": true, + "input_modalities": ["text", "image"] + }, + { + "id": "mimo-v2-pro", + "name": "MiMo V2 Pro", + "description": "Xiaomi's flagship foundation model with 1M context, reasoning, and tool calling capabilities", + "context_length": 1000000, + "tools_supported": true, + "supports_parallel_tool_calls": true, + "supports_reasoning": true, + "input_modalities": ["text"] + }, + { + "id": "mimo-v2-omni", + "name": "MiMo V2 Omni", + "description": "Xiaomi's omni-modal model that natively processes image, video, and audio inputs", + "context_length": 262100, + "tools_supported": true, + "supports_parallel_tool_calls": true, + "supports_reasoning": true, + "input_modalities": ["text", "image"] + }, + { + "id": "minimax-m2.7", + "name": "MiniMax M2.7", + "description": "MiniMax's latest model with enhanced reasoning and 204K context", + "context_length": 204800, + "tools_supported": true, + "supports_parallel_tool_calls": true, + "supports_reasoning": true, + "input_modalities": ["text"] + }, + { + "id": "minimax-m2.5", + "name": "MiniMax M2.5", + "description": "MiniMax's model with 204K context and reasoning capabilities", + "context_length": 204800, + "tools_supported": true, + "supports_parallel_tool_calls": true, + "supports_reasoning": true, + "input_modalities": ["text"] + } + ], + "auth_methods": ["api_key"] + }, { "id": "alibaba_coding", "provider_type": "llm", diff --git a/shell-plugin/lib/actions/auth.zsh b/shell-plugin/lib/actions/auth.zsh index 311c15dc17..751d8ede41 100644 --- a/shell-plugin/lib/actions/auth.zsh +++ b/shell-plugin/lib/actions/auth.zsh @@ -11,7 +11,7 @@ function _forge_action_login() { selected=$(_forge_select_provider "" "" "" "$input_text") if [[ -n "$selected" ]]; then # Extract the second field (provider ID) - local provider=$(echo "$selected" | awk '{print $2}') + local provider=$(echo "$selected" | awk -F ' +' '{print $2}') _forge_exec_interactive provider login "$provider" fi } @@ -25,7 +25,7 @@ function _forge_action_logout() { selected=$(_forge_select_provider "\[yes\]" "" "" "$input_text") if [[ -n "$selected" ]]; then # Extract the second field (provider ID) - local provider=$(echo "$selected" | awk '{print $2}') + local provider=$(echo "$selected" | awk -F ' +' '{print $2}') _forge_exec provider logout "$provider" fi }