diff --git a/.env.example b/.env.example index ea69ab76b..1093d69ee 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ DAYTONA_API_KEY= FIREWORKS_API_KEY= GEMINI_API_KEY= INCEPTION_API_KEY= -KIMI_API_KEY= +MOONSHOT_API_KEY= MINIMAX_API_KEY= MODAL_KIMI_K3_BASE_URL= MODAL_TOKEN_ID= diff --git a/docs/public/administration/server-configuration.mdx b/docs/public/administration/server-configuration.mdx index 23013c749..2b526ada9 100644 --- a/docs/public/administration/server-configuration.mdx +++ b/docs/public/administration/server-configuration.mdx @@ -383,7 +383,7 @@ fabro secret set GEMINI_API_KEY AI... | `ANTHROPIC_API_KEY` | Anthropic (Claude) | | `OPENAI_API_KEY` | OpenAI (GPT) | | `GEMINI_API_KEY` | Google (Gemini) | -| `KIMI_API_KEY` | Moonshot AI | +| `MOONSHOT_API_KEY` or `KIMI_API_KEY` | Moonshot AI; `MOONSHOT_API_KEY` takes precedence | | `ZAI_API_KEY` | Zai (GLM) | | `MINIMAX_API_KEY` | Minimax | | `INCEPTION_API_KEY` | Inception (Mercury) | diff --git a/docs/public/reference/sdk.mdx b/docs/public/reference/sdk.mdx index 929840cd5..402953e5c 100644 --- a/docs/public/reference/sdk.mdx +++ b/docs/public/reference/sdk.mdx @@ -366,7 +366,7 @@ For env-backed usage, `EnvCredentialSource` checks for API key environment varia | `ANTHROPIC_API_KEY` | Anthropic | | `OPENAI_API_KEY` | OpenAI | | `GEMINI_API_KEY` or `GOOGLE_API_KEY` | Gemini | -| `KIMI_API_KEY` | Moonshot AI | +| `MOONSHOT_API_KEY` or `KIMI_API_KEY` | Moonshot AI; `MOONSHOT_API_KEY` takes precedence | | `ZAI_API_KEY` | ZAI | | `MINIMAX_API_KEY` | Minimax | | `INCEPTION_API_KEY` | Inception | diff --git a/lib/apps/fabro-cli/tests/it/cmd/model_test.rs b/lib/apps/fabro-cli/tests/it/cmd/model_test.rs index acaae9851..53fd86fb2 100644 --- a/lib/apps/fabro-cli/tests/it/cmd/model_test.rs +++ b/lib/apps/fabro-cli/tests/it/cmd/model_test.rs @@ -18,6 +18,7 @@ fn remove_provider_env(cmd: &mut Command) -> &mut Command { .env_remove("OPENAI_API_KEY") .env_remove("GEMINI_API_KEY") .env_remove("GOOGLE_API_KEY") + .env_remove("MOONSHOT_API_KEY") .env_remove("KIMI_API_KEY") .env_remove("ZAI_API_KEY") .env_remove("MINIMAX_API_KEY") diff --git a/lib/foundation/fabro-auth/src/resolve.rs b/lib/foundation/fabro-auth/src/resolve.rs index 4f6ada34b..dd12d1a1e 100644 --- a/lib/foundation/fabro-auth/src/resolve.rs +++ b/lib/foundation/fabro-auth/src/resolve.rs @@ -630,6 +630,60 @@ base_url = "https://example--kimi-k3.modal.run/v1" ); } + #[tokio::test] + async fn resolve_moonshot_api_request_prefers_moonshot_env_key() { + let dir = tempfile::tempdir().unwrap(); + let vault = Vault::load(dir.path().join("secrets.json")).unwrap(); + let resolver = test_resolver( + vault, + Arc::new(|name| match name { + EnvVars::MOONSHOT_API_KEY => Some("moonshot-key".to_string()), + EnvVars::KIMI_API_KEY => Some("kimi-key".to_string()), + _ => None, + }), + ); + + let resolved = resolver + .resolve( + ProviderId::new("moonshot"), + CredentialUsage::ApiRequest, + &default_catalog(), + ) + .await + .unwrap(); + + let ResolvedCredential::Api(api) = resolved; + assert_eq!( + api.auth_header, + Some(ApiKeyHeader::Bearer("moonshot-key".to_string())) + ); + } + + #[tokio::test] + async fn resolve_moonshot_api_request_falls_back_to_kimi_env_key() { + let dir = tempfile::tempdir().unwrap(); + let vault = Vault::load(dir.path().join("secrets.json")).unwrap(); + let resolver = test_resolver( + vault, + Arc::new(|name| (name == EnvVars::KIMI_API_KEY).then(|| "kimi-key".to_string())), + ); + + let resolved = resolver + .resolve( + ProviderId::new("moonshot"), + CredentialUsage::ApiRequest, + &default_catalog(), + ) + .await + .unwrap(); + + let ResolvedCredential::Api(api) = resolved; + assert_eq!( + api.auth_header, + Some(ApiKeyHeader::Bearer("kimi-key".to_string())) + ); + } + #[tokio::test] async fn resolve_openai_api_request_falls_back_to_codex_oauth_credential() { let dir = tempfile::tempdir().unwrap(); diff --git a/lib/foundation/fabro-config/src/layers/llm.rs b/lib/foundation/fabro-config/src/layers/llm.rs index d232c40b7..298968a62 100644 --- a/lib/foundation/fabro-config/src/layers/llm.rs +++ b/lib/foundation/fabro-config/src/layers/llm.rs @@ -7,11 +7,18 @@ //! display_name = "Moonshot AI" //! adapter = "openai_compatible" //! base_url = "https://api.moonshot.ai/v1" -//! auth = { credentials = ["env:KIMI_API_KEY", "vault:KIMI_API_KEY"] } //! priority = 60 //! enabled = true //! aliases = ["moonshot-ai"] //! +//! [llm.providers.moonshot.auth] +//! credentials = [ +//! "env:MOONSHOT_API_KEY", +//! "env:KIMI_API_KEY", +//! "vault:MOONSHOT_API_KEY", +//! "vault:KIMI_API_KEY", +//! ] +//! //! [llm.providers.moonshot.models."kimi-k2.5"] //! ... //! ``` @@ -499,7 +506,12 @@ enabled = true aliases = ["moonshot-ai"] [providers.moonshot.auth] -credentials = ["env:KIMI_API_KEY", "vault:KIMI_API_KEY"] +credentials = [ + "env:MOONSHOT_API_KEY", + "env:KIMI_API_KEY", + "vault:MOONSHOT_API_KEY", + "vault:KIMI_API_KEY", +] "#; let layer: LlmLayer = toml::from_str(toml).unwrap(); let moonshot = layer.providers.get("moonshot").unwrap(); @@ -509,7 +521,9 @@ credentials = ["env:KIMI_API_KEY", "vault:KIMI_API_KEY"] let auth = moonshot.auth.as_ref().expect("expected api_key auth"); assert_eq!(auth.header, ApiKeyHeaderPolicy::Bearer); assert_eq!(auth.credentials, vec![ + CredentialRef::Env("MOONSHOT_API_KEY".to_string()), CredentialRef::Env("KIMI_API_KEY".to_string()), + CredentialRef::Vault("MOONSHOT_API_KEY".to_string()), CredentialRef::Vault("KIMI_API_KEY".to_string()), ]); assert_eq!( diff --git a/lib/foundation/fabro-model/src/catalog.rs b/lib/foundation/fabro-model/src/catalog.rs index f0d8719f4..5c4bfaa10 100644 --- a/lib/foundation/fabro-model/src/catalog.rs +++ b/lib/foundation/fabro-model/src/catalog.rs @@ -3694,6 +3694,26 @@ enabled = true ]); } + #[test] + fn builtin_moonshot_provider_prefers_moonshot_api_key() { + let moonshot = ProviderId::new("moonshot"); + let catalog = Catalog::builtin(); + let provider = catalog + .provider(&moonshot) + .expect("Moonshot provider should be present"); + + assert_eq!(provider.auth.as_ref().unwrap().credentials, vec![ + CredentialRef::Env("MOONSHOT_API_KEY".to_string()), + CredentialRef::Env("KIMI_API_KEY".to_string()), + CredentialRef::Vault("MOONSHOT_API_KEY".to_string()), + CredentialRef::Vault("KIMI_API_KEY".to_string()), + ]); + assert_eq!( + catalog.provider_vault_secret_name(&moonshot), + Some("MOONSHOT_API_KEY") + ); + } + #[test] fn builtin_kimi_k3_selection_prefers_modal_then_moonshot_over_openrouter() { let moonshot = ProviderId::new("moonshot"); diff --git a/lib/foundation/fabro-model/src/catalog/providers/moonshot.toml b/lib/foundation/fabro-model/src/catalog/providers/moonshot.toml index ddc1633e4..aaa4d7ec4 100644 --- a/lib/foundation/fabro-model/src/catalog/providers/moonshot.toml +++ b/lib/foundation/fabro-model/src/catalog/providers/moonshot.toml @@ -7,7 +7,12 @@ base_url = "https://api.moonshot.ai/v1" priority = 70 [providers.moonshot.auth] -credentials = ["env:KIMI_API_KEY", "vault:KIMI_API_KEY"] +credentials = [ + "env:MOONSHOT_API_KEY", + "env:KIMI_API_KEY", + "vault:MOONSHOT_API_KEY", + "vault:KIMI_API_KEY", +] [providers.moonshot.models."kimi-k2.5"] display_name = "Kimi K2.5" diff --git a/lib/foundation/fabro-static/src/env_vars.rs b/lib/foundation/fabro-static/src/env_vars.rs index 849ec6842..e6f00e3b9 100644 --- a/lib/foundation/fabro-static/src/env_vars.rs +++ b/lib/foundation/fabro-static/src/env_vars.rs @@ -56,6 +56,7 @@ impl EnvVars { pub const INCEPTION_API_KEY: &'static str = "INCEPTION_API_KEY"; pub const KIMI_API_KEY: &'static str = "KIMI_API_KEY"; pub const MINIMAX_API_KEY: &'static str = "MINIMAX_API_KEY"; + pub const MOONSHOT_API_KEY: &'static str = "MOONSHOT_API_KEY"; pub const MODAL_TOKEN_ID: &'static str = "MODAL_TOKEN_ID"; pub const MODAL_TOKEN_SECRET: &'static str = "MODAL_TOKEN_SECRET"; pub const OPENAI_API_KEY: &'static str = "OPENAI_API_KEY"; @@ -206,6 +207,7 @@ mod tests { EnvVars::INCEPTION_API_KEY, EnvVars::KIMI_API_KEY, EnvVars::MINIMAX_API_KEY, + EnvVars::MOONSHOT_API_KEY, EnvVars::OPENAI_API_KEY, EnvVars::OPENAI_BASE_URL, EnvVars::OPENAI_ORGANIZATION, diff --git a/lib/foundation/fabro-static/src/secret_registry.rs b/lib/foundation/fabro-static/src/secret_registry.rs index 03dec8e12..8c823b112 100644 --- a/lib/foundation/fabro-static/src/secret_registry.rs +++ b/lib/foundation/fabro-static/src/secret_registry.rs @@ -30,6 +30,7 @@ const OPTIONAL_VAULT_SECRETS: &[&str] = &[ EnvVars::INCEPTION_API_KEY, EnvVars::KIMI_API_KEY, EnvVars::MINIMAX_API_KEY, + EnvVars::MOONSHOT_API_KEY, EnvVars::OPENAI_API_KEY, EnvVars::OPENROUTER_API_KEY, EnvVars::POOLSIDE_API_KEY, @@ -97,6 +98,7 @@ mod tests { EnvVars::INCEPTION_API_KEY, EnvVars::KIMI_API_KEY, EnvVars::MINIMAX_API_KEY, + EnvVars::MOONSHOT_API_KEY, EnvVars::OPENAI_API_KEY, EnvVars::OPENROUTER_API_KEY, EnvVars::POOLSIDE_API_KEY,