From 8e8b674cf04ae66c94f70836cb29ac7824a77da2 Mon Sep 17 00:00:00 2001 From: Abhijay Jain Date: Fri, 21 Aug 2026 01:47:18 +0000 Subject: [PATCH] feat(providers): add Gondola as declarative OpenAI-compatible provider (#11421) Signed-off-by: Abhijay Jain --- crates/goose/src/providers/gondola.rs | 75 +++++++++++++++++++ crates/goose/src/providers/init.rs | 17 +++++ crates/goose/src/providers/mod.rs | 1 + .../docs/getting-started/providers.md | 1 + goose-self-test.yaml | 5 ++ 5 files changed, 99 insertions(+) create mode 100644 crates/goose/src/providers/gondola.rs diff --git a/crates/goose/src/providers/gondola.rs b/crates/goose/src/providers/gondola.rs new file mode 100644 index 000000000..db068b1d7 --- /dev/null +++ b/crates/goose/src/providers/gondola.rs @@ -0,0 +1,75 @@ +use super::api_client::{ApiClient, AuthMethod}; +use super::base::{ConfigKey, ProviderDef, ProviderMetadata}; +use super::openai_compatible::OpenAiCompatibleProvider; +use anyhow::Result; +use futures::future::BoxFuture; + +const GONDOLA_PROVIDER_NAME: &str = "gondola"; +pub const GONDOLA_API_HOST: &str = "https://api.gondola-ai.com/v1"; +pub const GONDOLA_DEFAULT_MODEL: &str = "deepseek-v4-flash"; +pub const GONDOLA_KNOWN_MODELS: &[&str] = &[ + "claude-opus-5", + "claude-sonnet-5", + "deepseek-v3.2", + "deepseek-v4-flash", + "deepseek-v4-pro", + "e2ee-deepseek-v4-flash", + "e2ee-qwen3-6-35b-a3b", + "kimi-k2-5", + "kimi-k3", + "llama-3.3-70b", + "qwen3-235b-a22b-instruct-2507", +]; +pub const GONDOLA_DOC_URL: &str = "https://gondola-ai.com/guides"; + +pub struct GondolaProvider; + +impl goose_providers::base::ProviderDescriptor for GondolaProvider { + fn metadata() -> ProviderMetadata { + ProviderMetadata::new( + GONDOLA_PROVIDER_NAME, + "Gondola", + "Pay-per-request inference via Venice AI, settled in USDC. Privacy-preserving with TEE model options.", + GONDOLA_DEFAULT_MODEL, + GONDOLA_KNOWN_MODELS.to_vec(), + GONDOLA_DOC_URL, + vec![ + ConfigKey::new("GONDOLA_API_KEY", true, true, None, true), + ConfigKey::new("GONDOLA_HOST", false, false, Some(GONDOLA_API_HOST), false), + ], + ) + .with_setup( + crate::providers::catalog::ProviderSetupMetadata::api_key( + crate::providers::catalog::ProviderSetupGroup::Default, + ) + .with_docs_url(GONDOLA_DOC_URL), + ) + } +} + +impl ProviderDef for GondolaProvider { + type Provider = OpenAiCompatibleProvider; + + fn from_env( + _extensions: Vec, + tls_config: Option, + ) -> BoxFuture<'static, Result> { + Box::pin(async move { + let config = crate::config::Config::global(); + let api_key: String = config.get_secret("GONDOLA_API_KEY")?; + let host: String = config + .get_param("GONDOLA_HOST") + .unwrap_or_else(|_| GONDOLA_API_HOST.to_string()); + + let api_client = + ApiClient::new_with_tls(host, AuthMethod::BearerToken(api_key), tls_config)? + .with_request_builder(crate::session_context::session_id_request_builder()); + + Ok(OpenAiCompatibleProvider::new( + GONDOLA_PROVIDER_NAME.to_string(), + api_client, + String::new(), + )) + }) + } +} diff --git a/crates/goose/src/providers/init.rs b/crates/goose/src/providers/init.rs index b195f9492..91899aef3 100644 --- a/crates/goose/src/providers/init.rs +++ b/crates/goose/src/providers/init.rs @@ -23,6 +23,7 @@ use super::{ gemini_cli::GeminiCliProvider, gemini_oauth::GeminiOAuthProvider, githubcopilot::GithubCopilotProvider, + gondola::GondolaProvider, huggingface::HuggingFaceProvider, kimicode::KimiCodeProvider, litellm::LiteLLMProvider, @@ -121,6 +122,7 @@ async fn init_registry() -> RwLock { false, Some(registrations::refresh_only()), ); + registry.register::(false); registry.register_with_inventory::( true, Some(registrations::google_inventory()), @@ -329,6 +331,21 @@ mod tests { .any(|key| key.name == "HF_TOKEN" && key.secret)); } + #[tokio::test] + async fn test_gondola_provider_registry_wiring() { + let gondola = get_from_registry("gondola") + .await + .expect("gondola provider should be registered"); + let meta = gondola.metadata(); + + assert_eq!(meta.name, "gondola"); + assert_eq!(meta.default_model, "deepseek-v4-flash"); + assert!(meta + .config_keys + .iter() + .any(|key| key.name == "GONDOLA_API_KEY" && key.secret)); + } + #[tokio::test] async fn test_openai_compatible_providers_config_keys() { let providers_list = providers().await; diff --git a/crates/goose/src/providers/mod.rs b/crates/goose/src/providers/mod.rs index f7e5669e7..f2ccd62b3 100644 --- a/crates/goose/src/providers/mod.rs +++ b/crates/goose/src/providers/mod.rs @@ -42,6 +42,7 @@ pub mod githubcopilot; pub mod google { pub use goose_providers::google::*; } +pub mod gondola; pub mod google_def; pub mod http_status { pub use goose_providers::http_status::*; diff --git a/documentation/docs/getting-started/providers.md b/documentation/docs/getting-started/providers.md index 6c9f8bf7d..40566b1f7 100644 --- a/documentation/docs/getting-started/providers.md +++ b/documentation/docs/getting-started/providers.md @@ -38,6 +38,7 @@ goose is compatible with a wide range of LLM providers, allowing you to choose a | [Gemini](https://ai.google.dev/gemini-api/docs) | Advanced LLMs by Google with multimodal capabilities (text, images). Gemini 3 models support configurable [thinking levels](#gemini-3-thinking-levels). | `GOOGLE_API_KEY`, `GEMINI3_THINKING_LEVEL` (optional) | | [GCP Vertex AI](https://cloud.google.com/vertex-ai) | Google Cloud's Vertex AI platform, supporting Gemini and Claude models. **Credentials must be [configured in advance](https://cloud.google.com/vertex-ai/docs/authentication).** Filters for allowed models by organization policy (if configured). | `GCP_PROJECT_ID`, `GCP_LOCATION` and optionally `GCP_MAX_RATE_LIMIT_RETRIES` (5), `GCP_MAX_OVERLOADED_RETRIES` (5), `GCP_INITIAL_RETRY_INTERVAL_MS` (5000), `GCP_BACKOFF_MULTIPLIER` (2.0), `GCP_MAX_RETRY_INTERVAL_MS` (320_000). | | [GitHub Copilot](https://docs.github.com/en/copilot/using-github-copilot/ai-models) | Access to AI models from OpenAI, Anthropic, Google, and other providers through GitHub's Copilot infrastructure. **GitHub account with Copilot access required.** | No manual key. Uses [device flow authentication](#github-copilot-authentication) for both CLI and Desktop. | +| [Gondola](https://gondola-ai.com/guides) | Pay-per-request inference via Venice AI, settled in USDC on Base. No subscription or minimum. Includes privacy-preserving TEE models (`e2ee-*`). OpenAI-compatible. | `GONDOLA_API_KEY`, `GONDOLA_HOST` (optional) | | [Groq](https://groq.com/) | High-performance inference hardware and tools for LLMs. | `GROQ_API_KEY` | | [iFlytek Spark](https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html) | iFlytek Spark (讯飞星火) models (4.0Ultra, generalv3.5, max-32k) via the OpenAI-compatible HTTP API. Best for chat: Spark needs `tool_calls_switch=true` (not injectable here) to return OpenAI-style tool calls. | `SPARK_API_PASSWORD` | | [iFlytek Astron MaaS](https://maas.xfyun.cn/) | iFlytek Astron MaaS (讯飞星辰) hosting Spark X2, DeepSeek, GLM, Kimi, MiniMax, Qwen, and Astron coding models via an OpenAI-compatible API. Set `ASTRON_BASE_URL` to switch between the Token Plan and Coding Plan endpoints. | `ASTRON_API_KEY`, `ASTRON_BASE_URL` (optional) | diff --git a/goose-self-test.yaml b/goose-self-test.yaml index 800328781..925e0aed1 100644 --- a/goose-self-test.yaml +++ b/goose-self-test.yaml @@ -169,6 +169,11 @@ prompt: | 4. Verify MaaS requires its bound model and uses /v1/chat/completions. If the source checkout or Rust toolchain is unavailable, mark this validation as skipped rather than failed. + ### Gondola Provider Validation + When running from a goose source checkout: + 1. Run `cargo test -p goose test_gondola_provider_registry_wiring` to verify Gondola is registered with the correct name, default model (`deepseek-v4-flash`), and a secret `GONDOLA_API_KEY` config key. + If the source checkout or Rust toolchain is unavailable, mark this validation as skipped rather than failed. + Log results to: {{ workspace_dir }}/phase1_basic_tools.md {% endif %}