From 1a7001751dc5d36eade13578ff20b199c83604d9 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Thu, 14 May 2026 22:36:41 +0800 Subject: [PATCH 1/4] refactor: rename provider 'gemini' to 'google' to match models.dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align the data-plane provider ID with models.dev's canonical provider identifier. The upstream API endpoint and behavior are unchanged — only the wire-level provider string changes from "gemini" to "google". - Rename Provider::Gemini → Provider::Google - Rename crate aisix-provider-gemini → aisix-provider-google - Update all schema enums, OpenAPI specs, test fixtures, and docs Ref: api7/AISIX-Cloud#290 --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- crates/aisix-admin/src/openapi.rs | 2 +- crates/aisix-core/src/models/model.rs | 8 ++++---- crates/aisix-core/src/models/schema.rs | 2 +- .../Cargo.toml | 2 +- .../src/lib.rs | 18 +++++++++--------- crates/aisix-provider-openai/src/bridge.rs | 14 +++++++------- crates/aisix-proxy/Cargo.toml | 2 +- crates/aisix-proxy/src/lib.rs | 14 +++++++------- crates/aisix-proxy/src/messages.rs | 16 ++++++++-------- crates/aisix-proxy/src/passthrough.rs | 2 +- crates/aisix-proxy/src/rerank.rs | 2 +- crates/aisix-server/Cargo.toml | 2 +- crates/aisix-server/src/main.rs | 4 ++-- docs/configuration/models.md | 2 +- docs/configuration/provider-keys.md | 4 ++-- docs/integration/anthropic-messages.md | 2 +- docs/overview/core-concepts.md | 2 +- docs/overview/what-is-aisix-ai-gateway.md | 2 +- .../first-model-first-key-first-request.md | 2 +- docs/reference/provider-compatibility.md | 2 +- .../openai-client-to-anthropic-upstream.md | 2 +- .../cases/cross-provider-matrix-e2e.test.ts | 4 ++-- .../error-envelope-normalization-e2e.test.ts | 4 ++-- .../src/cases/responses-endpoint-e2e.test.ts | 2 +- 26 files changed, 62 insertions(+), 62 deletions(-) rename crates/{aisix-provider-gemini => aisix-provider-google}/Cargo.toml (94%) rename crates/{aisix-provider-gemini => aisix-provider-google}/src/lib.rs (85%) diff --git a/Cargo.lock b/Cargo.lock index 083a969b..af8924af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,7 +230,7 @@ dependencies = [ ] [[package]] -name = "aisix-provider-gemini" +name = "aisix-provider-google" version = "0.1.0" dependencies = [ "aisix-core", @@ -273,7 +273,7 @@ dependencies = [ "aisix-obs", "aisix-provider-anthropic", "aisix-provider-deepseek", - "aisix-provider-gemini", + "aisix-provider-google", "aisix-provider-openai", "aisix-ratelimit", "async-stream", @@ -329,7 +329,7 @@ dependencies = [ "aisix-obs", "aisix-provider-anthropic", "aisix-provider-deepseek", - "aisix-provider-gemini", + "aisix-provider-google", "aisix-provider-openai", "aisix-proxy", "aisix-ratelimit", diff --git a/Cargo.toml b/Cargo.toml index 85bb0d0e..9eb316ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ "crates/aisix-gateway", "crates/aisix-provider-openai", "crates/aisix-provider-anthropic", - "crates/aisix-provider-gemini", + "crates/aisix-provider-google", "crates/aisix-provider-deepseek", "crates/aisix-proxy", "crates/aisix-admin", diff --git a/crates/aisix-admin/src/openapi.rs b/crates/aisix-admin/src/openapi.rs index 866740ac..cf137b6d 100644 --- a/crates/aisix-admin/src/openapi.rs +++ b/crates/aisix-admin/src/openapi.rs @@ -271,7 +271,7 @@ const OPENAPI_JSON: &str = r##"{ "required": ["display_name"], "properties": { "display_name": {"type": "string", "example": "my-gpt4"}, - "provider": {"type": "string", "enum": ["openai","anthropic","gemini","deepseek"]}, + "provider": {"type": "string", "enum": ["openai","anthropic","google","deepseek"]}, "model_name": {"type": "string", "example": "gpt-4o"}, "provider_key_id": {"type": "string", "example": "11111111-1111-1111-1111-111111111111"}, "timeout": {"type": "integer", "minimum": 0, "description": "Request timeout in milliseconds. Absent or 0 = no timeout."}, diff --git a/crates/aisix-core/src/models/model.rs b/crates/aisix-core/src/models/model.rs index e1e7793c..28498d34 100644 --- a/crates/aisix-core/src/models/model.rs +++ b/crates/aisix-core/src/models/model.rs @@ -23,7 +23,7 @@ use crate::resource::Resource; pub enum Provider { Openai, Anthropic, - Gemini, + Google, Deepseek, /// Cohere — currently exposed for `/v1/rerank` only (#213 Phase 1). /// Cohere's chat / generate APIs are not OpenAI-compatible; a @@ -43,7 +43,7 @@ impl Provider { match self { Self::Openai => "https://api.openai.com", Self::Anthropic => "https://api.anthropic.com", - Self::Gemini => "https://generativelanguage.googleapis.com/v1beta/openai", + Self::Google => "https://generativelanguage.googleapis.com/v1beta/openai", Self::Deepseek => "https://api.deepseek.com", Self::Cohere => "https://api.cohere.com", Self::Jina => "https://api.jina.ai", @@ -54,7 +54,7 @@ impl Provider { match self { Self::Openai => "openai", Self::Anthropic => "anthropic", - Self::Gemini => "gemini", + Self::Google => "google", Self::Deepseek => "deepseek", Self::Cohere => "cohere", Self::Jina => "jina", @@ -447,7 +447,7 @@ mod tests { "https://api.anthropic.com" ); assert_eq!( - Provider::Gemini.default_base_url(), + Provider::Google.default_base_url(), "https://generativelanguage.googleapis.com/v1beta/openai" ); assert_eq!( diff --git a/crates/aisix-core/src/models/schema.rs b/crates/aisix-core/src/models/schema.rs index 818e7f75..81e1d073 100644 --- a/crates/aisix-core/src/models/schema.rs +++ b/crates/aisix-core/src/models/schema.rs @@ -109,7 +109,7 @@ fn model_schema() -> Value { "additionalProperties": false, "properties": { "display_name": { "type": "string", "minLength": 1 }, - "provider": { "type": "string", "enum": ["openai","anthropic","gemini","deepseek","cohere","jina"] }, + "provider": { "type": "string", "enum": ["openai","anthropic","google","deepseek","cohere","jina"] }, "model_name": { "type": "string", "minLength": 1 }, "provider_key_id": { "type": "string", "minLength": 1 }, "timeout": { "type": "integer", "minimum": 0 }, diff --git a/crates/aisix-provider-gemini/Cargo.toml b/crates/aisix-provider-google/Cargo.toml similarity index 94% rename from crates/aisix-provider-gemini/Cargo.toml rename to crates/aisix-provider-google/Cargo.toml index 114d4ea3..f33dfc00 100644 --- a/crates/aisix-provider-gemini/Cargo.toml +++ b/crates/aisix-provider-google/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "aisix-provider-gemini" +name = "aisix-provider-google" version.workspace = true edition.workspace = true rust-version.workspace = true diff --git a/crates/aisix-provider-gemini/src/lib.rs b/crates/aisix-provider-google/src/lib.rs similarity index 85% rename from crates/aisix-provider-gemini/src/lib.rs rename to crates/aisix-provider-google/src/lib.rs index c8f8cc33..20333b5d 100644 --- a/crates/aisix-provider-gemini/src/lib.rs +++ b/crates/aisix-provider-google/src/lib.rs @@ -1,10 +1,10 @@ -//! aisix-provider-gemini — Google Gemini via its OpenAI-compatible endpoint. +//! aisix-provider-google — Google Gemini via its OpenAI-compatible endpoint. //! //! Google exposes an OpenAI-shaped `/chat/completions` surface at //! `generativelanguage.googleapis.com/v1beta/openai`. The wire format is //! close enough to plain OpenAI that the upstream `OpenAiBridge` covers //! every field we care about — this crate only relabels the bridge -//! (`name() == "gemini"`) so metrics and logs can distinguish traffic. +//! (`name() == "google"`) so metrics and logs can distinguish traffic. //! //! Operators configure Gemini access by setting on the Model: //! @@ -23,14 +23,14 @@ use aisix_provider_openai::OpenAiBridge; -/// Default base for Gemini's OpenAI-compat endpoint. Only used when the +/// Default base for Google's OpenAI-compat endpoint. Only used when the /// Model doesn't carry an explicit `api_base` — production configs should /// set one. -pub const GEMINI_DEFAULT_BASE: &str = "https://generativelanguage.googleapis.com/v1beta/openai"; +pub const GOOGLE_DEFAULT_BASE: &str = "https://generativelanguage.googleapis.com/v1beta/openai"; -/// Build a Bridge that speaks Gemini's OpenAI-compatible chat API. +/// Build a Bridge that speaks Google's OpenAI-compatible chat API. pub fn gemini_bridge() -> OpenAiBridge { - OpenAiBridge::new().with_name("gemini") + OpenAiBridge::new().with_name("google") } #[cfg(test)] @@ -43,12 +43,12 @@ mod tests { #[test] fn bridge_reports_gemini_name() { - assert_eq!(gemini_bridge().name(), "gemini"); + assert_eq!(gemini_bridge().name(), "google"); } #[test] fn default_base_targets_v1beta_openai_shim() { - assert!(GEMINI_DEFAULT_BASE.contains("/v1beta/openai")); + assert!(GOOGLE_DEFAULT_BASE.contains("/v1beta/openai")); } #[tokio::test] @@ -73,7 +73,7 @@ mod tests { let model: aisix_core::Model = serde_json::from_str( r#"{ "display_name": "my-gemini", - "provider": "gemini", + "provider": "google", "model_name": "gemini-2.5-flash", "provider_key_id": "11111111-1111-1111-1111-111111111111" }"#, diff --git a/crates/aisix-provider-openai/src/bridge.rs b/crates/aisix-provider-openai/src/bridge.rs index 8bac31aa..cb9ca55c 100644 --- a/crates/aisix-provider-openai/src/bridge.rs +++ b/crates/aisix-provider-openai/src/bridge.rs @@ -47,10 +47,10 @@ pub const OPENAI_DEFAULT_BASE: &str = "https://api.openai.com/v1"; const DEEPSEEK_DEFAULT_BASE: &str = "https://api.deepseek.com"; /// Fallback host for the `gemini`-named variant of this bridge. -/// Mirrors `aisix_provider_gemini::GEMINI_DEFAULT_BASE` so that a -/// `with_name("gemini")` instance without an explicit `api_base` +/// Mirrors `aisix_provider_google::GOOGLE_DEFAULT_BASE` so that a +/// `with_name("google")` instance without an explicit `api_base` /// dispatches to Google's OpenAI-compatible Gemini endpoint. -const GEMINI_DEFAULT_BASE: &str = "https://generativelanguage.googleapis.com/v1beta/openai"; +const GOOGLE_DEFAULT_BASE: &str = "https://generativelanguage.googleapis.com/v1beta/openai"; /// Path suffixes the bridge appends to `api_base` when building upstream /// URLs. If an operator accidentally pastes the full upstream URL into @@ -91,14 +91,14 @@ impl OpenAiBridge { } /// Default upstream base for this bridge variant. The bridge factory - /// wraps with `with_name("deepseek")` / `with_name("gemini")` to + /// wraps with `with_name("deepseek")` / `with_name("google")` to /// retarget; the default base follows the same retargeting so a /// degenerate config (no `api_base` on the Model) still reaches the /// right host. fn default_base(&self) -> &'static str { match self.name { "deepseek" => DEEPSEEK_DEFAULT_BASE, - "gemini" => GEMINI_DEFAULT_BASE, + "google" => GOOGLE_DEFAULT_BASE, _ => OPENAI_DEFAULT_BASE, } } @@ -852,7 +852,7 @@ data: [DONE]\n\n"; /// path rather than falling through to the OpenAI host. #[test] fn gemini_default_base_targets_gemini_v1beta_openai() { - let bridge = OpenAiBridge::new().with_name("gemini"); + let bridge = OpenAiBridge::new().with_name("google"); let pk: ProviderKey = serde_json::from_str(r#"{"display_name":"x","secret":"k"}"#).unwrap(); let ctx = BridgeContext::new("rid", sample_model(), Arc::new(pk)); assert_eq!( @@ -866,7 +866,7 @@ data: [DONE]\n\n"; /// still strips an accidentally-pasted endpoint suffix. #[test] fn gemini_api_base_strips_endpoint_suffix_but_does_not_synthesize_prefix() { - let bridge = OpenAiBridge::new().with_name("gemini"); + let bridge = OpenAiBridge::new().with_name("google"); // Canonical form passes through. let canonical = "https://generativelanguage.googleapis.com/v1beta/openai"; diff --git a/crates/aisix-proxy/Cargo.toml b/crates/aisix-proxy/Cargo.toml index 65e779f9..e6153de9 100644 --- a/crates/aisix-proxy/Cargo.toml +++ b/crates/aisix-proxy/Cargo.toml @@ -56,5 +56,5 @@ aisix-provider-openai = { path = "../aisix-provider-openai" } # aisix-provider-anthropic moved to [dependencies] so the /v1/messages # handler can use its inbound wire helpers in non-test builds too. aisix-provider-deepseek = { path = "../aisix-provider-deepseek" } -aisix-provider-gemini = { path = "../aisix-provider-gemini" } +aisix-provider-google = { path = "../aisix-provider-google" } wiremock.workspace = true diff --git a/crates/aisix-proxy/src/lib.rs b/crates/aisix-proxy/src/lib.rs index df5f0e5f..440b00d2 100644 --- a/crates/aisix-proxy/src/lib.rs +++ b/crates/aisix-proxy/src/lib.rs @@ -3115,7 +3115,7 @@ data: [DONE]\n\n"; // → upstream → Bridge response decoder → renderer → wire bytes. const MATRIX_ANTHROPIC_PK_ID: &str = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"; - const MATRIX_GEMINI_PK_ID: &str = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"; + const MATRIX_GOOGLE_PK_ID: &str = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"; const MATRIX_DEEPSEEK_PK_ID: &str = "cccccccc-cccc-cccc-cccc-cccccccccccc"; fn anthropic_model_entry(name: &str) -> ResourceEntry { @@ -3134,9 +3134,9 @@ data: [DONE]\n\n"; let cfg = format!( r#"{{ "display_name": "{name}", - "provider": "gemini", + "provider": "google", "model_name": "gemini-2.0-flash", - "provider_key_id": "{MATRIX_GEMINI_PK_ID}" + "provider_key_id": "{MATRIX_GOOGLE_PK_ID}" }}"# ); ResourceEntry::new("model-gemini-1", serde_json::from_str(&cfg).unwrap(), 1) @@ -3171,7 +3171,7 @@ data: [DONE]\n\n"; } fn matrix_gemini_pk(api_base: &str) -> ResourceEntry { - matrix_pk_entry(MATRIX_GEMINI_PK_ID, "ya29-test", api_base) + matrix_pk_entry(MATRIX_GOOGLE_PK_ID, "ya29-test", api_base) } fn matrix_deepseek_pk(api_base: &str) -> ResourceEntry { @@ -3308,12 +3308,12 @@ event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"; /// (OpenAI inbound) × (Gemini upstream). Gemini's bridge is a /// thin wrapper around the OpenAi-compat `/chat/completions` /// endpoint, so the upstream wire is OpenAI-shape — but the - /// `Hub.get(Provider::Gemini)` lookup must still resolve to the + /// `Hub.get(Provider::Google)` lookup must still resolve to the /// Gemini-specific bridge instance (different metrics label, /// different default base URL behavior). #[tokio::test] async fn matrix_openai_in_gemini_upstream_non_streaming() { - use aisix_provider_gemini::gemini_bridge; + use aisix_provider_google::gemini_bridge; let upstream = MockServer::start().await; Mock::given(method("POST")) @@ -3337,7 +3337,7 @@ event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"; snap.apikeys .insert(apikey_entry("sk-caller", &["my-gemini"])); let hub = Arc::new(Hub::new()); - hub.register(Provider::Gemini, Arc::new(gemini_bridge())); + hub.register(Provider::Google, Arc::new(gemini_bridge())); let app = build_router(build_state(snap, hub)); let body = serde_json::json!({ diff --git a/crates/aisix-proxy/src/messages.rs b/crates/aisix-proxy/src/messages.rs index a0ab22ec..be97ea26 100644 --- a/crates/aisix-proxy/src/messages.rs +++ b/crates/aisix-proxy/src/messages.rs @@ -691,7 +691,7 @@ mod tests { const ANTHROPIC_PK_ID: &str = "11111111-1111-1111-1111-111111111111"; const OPENAI_PK_ID: &str = "22222222-2222-2222-2222-222222222222"; - const GEMINI_PK_ID: &str = "33333333-3333-3333-3333-333333333333"; + const GOOGLE_PK_ID: &str = "33333333-3333-3333-3333-333333333333"; const DEEPSEEK_PK_ID: &str = "44444444-4444-4444-4444-444444444444"; fn anthropic_model(name: &str) -> ResourceEntry { @@ -1072,9 +1072,9 @@ data: [DONE]\n\n"; let cfg = format!( r#"{{ "display_name": "{name}", - "provider": "gemini", + "provider": "google", "model_name": "gemini-2.0-flash", - "provider_key_id": "{GEMINI_PK_ID}" + "provider_key_id": "{GOOGLE_PK_ID}" }}"# ); ResourceEntry::new("m-3", serde_json::from_str(&cfg).unwrap(), 1) @@ -1097,7 +1097,7 @@ data: [DONE]\n\n"; r#"{{"display_name":"gemini-up","secret":"ya29-test","api_base":"{api_base}"}}"# ); let pk: aisix_core::ProviderKey = serde_json::from_str(&json).unwrap(); - ResourceEntry::new(GEMINI_PK_ID, pk, 1) + ResourceEntry::new(GOOGLE_PK_ID, pk, 1) } fn deepseek_pk(api_base: &str) -> ResourceEntry { @@ -1128,7 +1128,7 @@ data: [DONE]\n\n"; /// non-Anthropic Bridge in the workspace. #[tokio::test] async fn matrix_anthropic_in_gemini_upstream_non_streaming() { - use aisix_provider_gemini::gemini_bridge; + use aisix_provider_google::gemini_bridge; let upstream = MockServer::start().await; Mock::given(method("POST")) @@ -1154,7 +1154,7 @@ data: [DONE]\n\n"; let hub = Arc::new(Hub::new()); hub.register(Provider::Anthropic, Arc::new(AnthropicBridge::new())); - hub.register(Provider::Gemini, Arc::new(gemini_bridge())); + hub.register(Provider::Google, Arc::new(gemini_bridge())); let handle = SnapshotHandle::new(snap); let app = crate::build_router(crate::ProxyState::new(handle, hub, &cfg()).without_cache()); @@ -1343,9 +1343,9 @@ data: [DONE]\n\n"; #[tokio::test] async fn matrix_anthropic_in_gemini_upstream_streaming() { - use aisix_provider_gemini::gemini_bridge; + use aisix_provider_google::gemini_bridge; assert_anthropic_streams_through_openai_compat_upstream( - Provider::Gemini, + Provider::Google, Arc::new(gemini_bridge()), // Placeholder; helper rebuilds with the wiremock uri. gemini_model("my-claude-via-gemini"), diff --git a/crates/aisix-proxy/src/passthrough.rs b/crates/aisix-proxy/src/passthrough.rs index 4dbcccaf..b59da817 100644 --- a/crates/aisix-proxy/src/passthrough.rs +++ b/crates/aisix-proxy/src/passthrough.rs @@ -49,7 +49,7 @@ fn default_base(provider_prefix: &str) -> Option<&'static str> { match provider_prefix { "openai" => Some("https://api.openai.com"), "anthropic" => Some("https://api.anthropic.com"), - "gemini" => Some("https://generativelanguage.googleapis.com"), + "google" => Some("https://generativelanguage.googleapis.com"), "deepseek" => Some("https://api.deepseek.com"), _ => None, } diff --git a/crates/aisix-proxy/src/rerank.rs b/crates/aisix-proxy/src/rerank.rs index daee03bb..9e3528b4 100644 --- a/crates/aisix-proxy/src/rerank.rs +++ b/crates/aisix-proxy/src/rerank.rs @@ -259,7 +259,7 @@ fn default_base_for_provider(provider: aisix_core::models::Provider) -> Option Some("https://api.jina.ai".to_string()), Provider::Anthropic => None, // Anthropic doesn't expose a rerank API - Provider::Gemini => None, // Gemini doesn't expose a rerank API + Provider::Google => None, // Gemini doesn't expose a rerank API Provider::Deepseek => None, } } diff --git a/crates/aisix-server/Cargo.toml b/crates/aisix-server/Cargo.toml index 1e41245a..3fd0fdea 100644 --- a/crates/aisix-server/Cargo.toml +++ b/crates/aisix-server/Cargo.toml @@ -22,7 +22,7 @@ aisix-guardrails = { path = "../aisix-guardrails" } aisix-admin = { path = "../aisix-admin" } aisix-provider-openai = { path = "../aisix-provider-openai" } aisix-provider-anthropic = { path = "../aisix-provider-anthropic" } -aisix-provider-gemini = { path = "../aisix-provider-gemini" } +aisix-provider-google = { path = "../aisix-provider-google" } aisix-provider-deepseek = { path = "../aisix-provider-deepseek" } aisix-ratelimit = { path = "../aisix-ratelimit" } aisix-cache = { path = "../aisix-cache", features = ["redis"] } diff --git a/crates/aisix-server/src/main.rs b/crates/aisix-server/src/main.rs index 4040640e..9d618c41 100644 --- a/crates/aisix-server/src/main.rs +++ b/crates/aisix-server/src/main.rs @@ -30,7 +30,7 @@ use aisix_gateway::Hub; use aisix_obs::{init_tracing, install_otlp_tracer, Metrics}; use aisix_provider_anthropic::AnthropicBridge; use aisix_provider_deepseek::deepseek_bridge; -use aisix_provider_gemini::gemini_bridge; +use aisix_provider_google::gemini_bridge; use aisix_provider_openai::OpenAiBridge; use aisix_proxy::background::run_background_model_check_once; use aisix_proxy::budget::BudgetClient; @@ -841,7 +841,7 @@ fn build_hub() -> Hub { let hub = Hub::new(); hub.register(Provider::Openai, Arc::new(OpenAiBridge::new())); hub.register(Provider::Anthropic, Arc::new(AnthropicBridge::new())); - hub.register(Provider::Gemini, Arc::new(gemini_bridge())); + hub.register(Provider::Google, Arc::new(gemini_bridge())); hub.register(Provider::Deepseek, Arc::new(deepseek_bridge())); hub } diff --git a/docs/configuration/models.md b/docs/configuration/models.md index 47b6ee0d..cf5a78c6 100644 --- a/docs/configuration/models.md +++ b/docs/configuration/models.md @@ -117,7 +117,7 @@ curl -sS -X POST http://127.0.0.1:3001/admin/v1/models \ ## Field Notes - `display_name` is the alias clients send in proxy requests. -- `provider` currently supports `openai`, `anthropic`, `gemini`, and `deepseek`. +- `provider` currently supports `openai`, `anthropic`, `google`, and `deepseek`. - `provider_key_id` must reference an existing `ProviderKey` resource. - `timeout` is in milliseconds. `0` or omission means no timeout. - `cost` stores pricing metadata used by budget and usage accounting paths. diff --git a/docs/configuration/provider-keys.md b/docs/configuration/provider-keys.md index 832df665..12639507 100644 --- a/docs/configuration/provider-keys.md +++ b/docs/configuration/provider-keys.md @@ -53,7 +53,7 @@ Each provider has its own convention — the four current bridges do **not** sha |---|---|---|---| | `openai` | include `/v1` | `/chat/completions`, `/embeddings`, `/completions`, `/images/generations`, `/audio/*` | `https://api.openai.com/v1` | | `deepseek` | bare host (DeepSeek serves OpenAI-compatible paths at the host root) | `/chat/completions` | `https://api.deepseek.com` | -| `gemini` | host plus the OpenAI-compat prefix `/v1beta/openai` | `/chat/completions` | `https://generativelanguage.googleapis.com/v1beta/openai` | +| `google` | host plus the OpenAI-compat prefix `/v1beta/openai` | `/chat/completions` | `https://generativelanguage.googleapis.com/v1beta/openai` | | `anthropic` | bare host | `/v1/messages` | `https://api.anthropic.com` | The OpenAI and Anthropic conventions match each upstream's official SDK — `openai-python` initialises `base_url = "https://api.openai.com/v1"`, while `anthropic-sdk-python` initialises `base_url = "https://api.anthropic.com"` and appends `/v1/messages` itself. DeepSeek is OpenAI-compatible but exposes `/chat/completions` directly at the host root, and Gemini's OpenAI-compatible surface lives under a fixed `/v1beta/openai` prefix that the bridge does not synthesize. @@ -80,7 +80,7 @@ For `anthropic` (always tolerated): - the full upstream URL `…/v1/messages` or the `/v1` prefix is stripped — `api_base: "https://api.anthropic.com/v1/messages"`, `…/v1`, and bare host all converge to the bare host at dispatch time. -For `gemini` and any other variant: only suffix stripping; the bridge does not synthesize the `/v1beta/openai` prefix. Operators should paste the full canonical form. +For `google` and any other variant: only suffix stripping; the bridge does not synthesize the `/v1beta/openai` prefix. Operators should paste the full canonical form. ### Outside the canonical hosts diff --git a/docs/integration/anthropic-messages.md b/docs/integration/anthropic-messages.md index 88c0247f..89f06990 100644 --- a/docs/integration/anthropic-messages.md +++ b/docs/integration/anthropic-messages.md @@ -27,7 +27,7 @@ If you rely on Anthropic-specific semantics, this is the safest path. ### Non-Anthropic Upstream -When the resolved model provider is `openai`, `gemini`, or `deepseek`, the gateway translates the Anthropic-style request into the internal chat format, dispatches through the provider bridge, and then re-encodes the response as Anthropic-style JSON or SSE. +When the resolved model provider is `openai`, `google`, or `deepseek`, the gateway translates the Anthropic-style request into the internal chat format, dispatches through the provider bridge, and then re-encodes the response as Anthropic-style JSON or SSE. This path is useful for keeping a stable Anthropic-style client edge, but it should not be treated as feature-identical to native Anthropic behavior. diff --git a/docs/overview/core-concepts.md b/docs/overview/core-concepts.md index 913cb306..886952bc 100644 --- a/docs/overview/core-concepts.md +++ b/docs/overview/core-concepts.md @@ -22,7 +22,7 @@ The current provider enum includes: - `openai` - `anthropic` -- `gemini` +- `google` - `deepseek` ## Provider Key diff --git a/docs/overview/what-is-aisix-ai-gateway.md b/docs/overview/what-is-aisix-ai-gateway.md index 9f0b6e14..7a69d076 100644 --- a/docs/overview/what-is-aisix-ai-gateway.md +++ b/docs/overview/what-is-aisix-ai-gateway.md @@ -78,7 +78,7 @@ The current provider enum includes: - `openai` - `anthropic` -- `gemini` +- `google` - `deepseek` Provider support is not identical across every endpoint. The current high-level support summary is captured in the [Feature Matrix](feature-matrix.md), and the current provider-oriented reference lives in [Provider Compatibility](../reference/provider-compatibility.md). diff --git a/docs/quickstart/first-model-first-key-first-request.md b/docs/quickstart/first-model-first-key-first-request.md index 29548c32..46a72332 100644 --- a/docs/quickstart/first-model-first-key-first-request.md +++ b/docs/quickstart/first-model-first-key-first-request.md @@ -46,7 +46,7 @@ curl -sS -X POST http://127.0.0.1:3001/admin/v1/provider_keys \ ``` :::caution `api_base` convention differs per provider -Each provider has its own canonical form — do not generalize from this OpenAI example. `openai` expects `api_base` to include `/v1`; `deepseek` wants the bare host (`https://api.deepseek.com`); `gemini` wants the OpenAI-compat prefix (`https://generativelanguage.googleapis.com/v1beta/openai`); `anthropic` wants the bare host (the bridge appends `/v1/messages` itself). The gateway tolerates common paste-mistakes such as trailing slashes, full endpoint URLs, and (for the canonical OpenAI/DeepSeek hosts) the missing or extra `/v1` segment. See [Provider Keys § `api_base` Behavior](../configuration/provider-keys.md#api_base-behavior) for the full truth table and the tolerated forms. +Each provider has its own canonical form — do not generalize from this OpenAI example. `openai` expects `api_base` to include `/v1`; `deepseek` wants the bare host (`https://api.deepseek.com`); `google` wants the OpenAI-compat prefix (`https://generativelanguage.googleapis.com/v1beta/openai`); `anthropic` wants the bare host (the bridge appends `/v1/messages` itself). The gateway tolerates common paste-mistakes such as trailing slashes, full endpoint URLs, and (for the canonical OpenAI/DeepSeek hosts) the missing or extra `/v1` segment. See [Provider Keys § `api_base` Behavior](../configuration/provider-keys.md#api_base-behavior) for the full truth table and the tolerated forms. ::: The admin envelope returns a `ResourceEntry` shape: diff --git a/docs/reference/provider-compatibility.md b/docs/reference/provider-compatibility.md index 990690a1..86827d1b 100644 --- a/docs/reference/provider-compatibility.md +++ b/docs/reference/provider-compatibility.md @@ -10,7 +10,7 @@ The current provider set is: - `openai` - `anthropic` -- `gemini` +- `google` - `deepseek` ## Compatibility Boundary diff --git a/docs/tutorials/openai-client-to-anthropic-upstream.md b/docs/tutorials/openai-client-to-anthropic-upstream.md index 12a7ac4c..864cfdaf 100644 --- a/docs/tutorials/openai-client-to-anthropic-upstream.md +++ b/docs/tutorials/openai-client-to-anthropic-upstream.md @@ -152,7 +152,7 @@ curl -sS -X DELETE http://127.0.0.1:3001/admin/v1/provider_keys/ANTHROPIC_PK_ID - **Add a routing model on top** — combine an OpenAI-backed direct model and `claude-prod` under one virtual alias and let the gateway pick between them. See [Build A Virtual Model With Failover](build-a-virtual-model-with-failover.md). - **Use the Anthropic-style endpoint directly** — `POST /v1/messages` exposes the Anthropic shape end-to-end without translation. See [Anthropic SDK Quickstart](../quickstart/anthropic-sdk.md). -- **Cover other providers the same way** — `provider: "gemini"` and `provider: "deepseek"` use the same pattern. The bridges handle their own wire translation; the caller stays on OpenAI Chat Completions. +- **Cover other providers the same way** — `provider: "google"` and `provider: "deepseek"` use the same pattern. The bridges handle their own wire translation; the caller stays on OpenAI Chat Completions. ## Related Pages diff --git a/tests/e2e/src/cases/cross-provider-matrix-e2e.test.ts b/tests/e2e/src/cases/cross-provider-matrix-e2e.test.ts index 13cf65dc..be52cfb4 100644 --- a/tests/e2e/src/cases/cross-provider-matrix-e2e.test.ts +++ b/tests/e2e/src/cases/cross-provider-matrix-e2e.test.ts @@ -41,7 +41,7 @@ const CALLER_KEY_HASH = createHash("sha256") .update(CALLER_PLAINTEXT) .digest("hex"); -type Provider = "gemini" | "deepseek"; +type Provider = "google" | "deepseek"; interface MatrixCase { readonly provider: Provider; @@ -52,7 +52,7 @@ interface MatrixCase { const CASES: ReadonlyArray = [ { - provider: "gemini", + provider: "google", upstreamModelId: "gemini-2.0-flash", displayPrefix: "matrix-gemini", expectedContent: "Hello from Gemini!", diff --git a/tests/e2e/src/cases/error-envelope-normalization-e2e.test.ts b/tests/e2e/src/cases/error-envelope-normalization-e2e.test.ts index fea4e1d9..f9dd8da9 100644 --- a/tests/e2e/src/cases/error-envelope-normalization-e2e.test.ts +++ b/tests/e2e/src/cases/error-envelope-normalization-e2e.test.ts @@ -53,7 +53,7 @@ const CALLER_KEY_HASH = createHash("sha256") .digest("hex"); interface ProviderCase { - readonly provider: "anthropic" | "gemini" | "deepseek"; + readonly provider: "anthropic" | "google" | "deepseek"; readonly upstreamModelId: string; readonly displayName: string; // The wire shape the upstream sends back on a 400. Each provider @@ -93,7 +93,7 @@ const CASES: ReadonlyArray = [ apiBaseSuffix: "", }, { - provider: "gemini", + provider: "google", upstreamModelId: "gemini-2.0-flash", displayName: "err-norm-gemini", // The gemini bridge talks to Google's OpenAI-compatibility diff --git a/tests/e2e/src/cases/responses-endpoint-e2e.test.ts b/tests/e2e/src/cases/responses-endpoint-e2e.test.ts index 72c01816..0f9f4b05 100644 --- a/tests/e2e/src/cases/responses-endpoint-e2e.test.ts +++ b/tests/e2e/src/cases/responses-endpoint-e2e.test.ts @@ -236,7 +236,7 @@ describe("responses endpoint e2e: /v1/responses dispatch + provider mismatch", ( apiBaseSuffix: "" as const, }, { - provider: "gemini" as const, + provider: "google" as const, modelName: "gemini-2.0-flash", secret: "sk-mock", apiBaseSuffix: "/v1" as const, From 2a128f59f0b0d8f5ab4b40e34701732ee870427e Mon Sep 17 00:00:00 2001 From: Jarvis Date: Thu, 14 May 2026 22:53:15 +0800 Subject: [PATCH 2/4] fix: rename gemini_bridge to google_bridge, update test comments --- crates/aisix-provider-google/src/lib.rs | 6 +++--- crates/aisix-proxy/src/lib.rs | 4 ++-- crates/aisix-proxy/src/messages.rs | 8 ++++---- crates/aisix-server/src/main.rs | 4 ++-- .../src/cases/error-envelope-normalization-e2e.test.ts | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/aisix-provider-google/src/lib.rs b/crates/aisix-provider-google/src/lib.rs index 20333b5d..6d65612d 100644 --- a/crates/aisix-provider-google/src/lib.rs +++ b/crates/aisix-provider-google/src/lib.rs @@ -29,7 +29,7 @@ use aisix_provider_openai::OpenAiBridge; pub const GOOGLE_DEFAULT_BASE: &str = "https://generativelanguage.googleapis.com/v1beta/openai"; /// Build a Bridge that speaks Google's OpenAI-compatible chat API. -pub fn gemini_bridge() -> OpenAiBridge { +pub fn google_bridge() -> OpenAiBridge { OpenAiBridge::new().with_name("google") } @@ -43,7 +43,7 @@ mod tests { #[test] fn bridge_reports_gemini_name() { - assert_eq!(gemini_bridge().name(), "google"); + assert_eq!(google_bridge().name(), "google"); } #[test] @@ -87,7 +87,7 @@ mod tests { let ctx = BridgeContext::new("req-1", Arc::new(model), Arc::new(pk)); let req = ChatFormat::new("my-gemini", vec![ChatMessage::user("hola")]); - let resp = gemini_bridge().chat(&req, &ctx).await.unwrap(); + let resp = google_bridge().chat(&req, &ctx).await.unwrap(); assert_eq!(resp.message.content, "ciao"); assert_eq!(resp.usage.total_tokens, 4); } diff --git a/crates/aisix-proxy/src/lib.rs b/crates/aisix-proxy/src/lib.rs index 440b00d2..c5cffdc7 100644 --- a/crates/aisix-proxy/src/lib.rs +++ b/crates/aisix-proxy/src/lib.rs @@ -3313,7 +3313,7 @@ event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"; /// different default base URL behavior). #[tokio::test] async fn matrix_openai_in_gemini_upstream_non_streaming() { - use aisix_provider_google::gemini_bridge; + use aisix_provider_google::google_bridge; let upstream = MockServer::start().await; Mock::given(method("POST")) @@ -3337,7 +3337,7 @@ event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n"; snap.apikeys .insert(apikey_entry("sk-caller", &["my-gemini"])); let hub = Arc::new(Hub::new()); - hub.register(Provider::Google, Arc::new(gemini_bridge())); + hub.register(Provider::Google, Arc::new(google_bridge())); let app = build_router(build_state(snap, hub)); let body = serde_json::json!({ diff --git a/crates/aisix-proxy/src/messages.rs b/crates/aisix-proxy/src/messages.rs index be97ea26..c64a6be3 100644 --- a/crates/aisix-proxy/src/messages.rs +++ b/crates/aisix-proxy/src/messages.rs @@ -1128,7 +1128,7 @@ data: [DONE]\n\n"; /// non-Anthropic Bridge in the workspace. #[tokio::test] async fn matrix_anthropic_in_gemini_upstream_non_streaming() { - use aisix_provider_google::gemini_bridge; + use aisix_provider_google::google_bridge; let upstream = MockServer::start().await; Mock::given(method("POST")) @@ -1154,7 +1154,7 @@ data: [DONE]\n\n"; let hub = Arc::new(Hub::new()); hub.register(Provider::Anthropic, Arc::new(AnthropicBridge::new())); - hub.register(Provider::Google, Arc::new(gemini_bridge())); + hub.register(Provider::Google, Arc::new(google_bridge())); let handle = SnapshotHandle::new(snap); let app = crate::build_router(crate::ProxyState::new(handle, hub, &cfg()).without_cache()); @@ -1343,10 +1343,10 @@ data: [DONE]\n\n"; #[tokio::test] async fn matrix_anthropic_in_gemini_upstream_streaming() { - use aisix_provider_google::gemini_bridge; + use aisix_provider_google::google_bridge; assert_anthropic_streams_through_openai_compat_upstream( Provider::Google, - Arc::new(gemini_bridge()), + Arc::new(google_bridge()), // Placeholder; helper rebuilds with the wiremock uri. gemini_model("my-claude-via-gemini"), "my-claude-via-gemini", diff --git a/crates/aisix-server/src/main.rs b/crates/aisix-server/src/main.rs index 9d618c41..97f1f8dd 100644 --- a/crates/aisix-server/src/main.rs +++ b/crates/aisix-server/src/main.rs @@ -30,7 +30,7 @@ use aisix_gateway::Hub; use aisix_obs::{init_tracing, install_otlp_tracer, Metrics}; use aisix_provider_anthropic::AnthropicBridge; use aisix_provider_deepseek::deepseek_bridge; -use aisix_provider_google::gemini_bridge; +use aisix_provider_google::google_bridge; use aisix_provider_openai::OpenAiBridge; use aisix_proxy::background::run_background_model_check_once; use aisix_proxy::budget::BudgetClient; @@ -841,7 +841,7 @@ fn build_hub() -> Hub { let hub = Hub::new(); hub.register(Provider::Openai, Arc::new(OpenAiBridge::new())); hub.register(Provider::Anthropic, Arc::new(AnthropicBridge::new())); - hub.register(Provider::Google, Arc::new(gemini_bridge())); + hub.register(Provider::Google, Arc::new(google_bridge())); hub.register(Provider::Deepseek, Arc::new(deepseek_bridge())); hub } diff --git a/tests/e2e/src/cases/error-envelope-normalization-e2e.test.ts b/tests/e2e/src/cases/error-envelope-normalization-e2e.test.ts index f9dd8da9..358b06ca 100644 --- a/tests/e2e/src/cases/error-envelope-normalization-e2e.test.ts +++ b/tests/e2e/src/cases/error-envelope-normalization-e2e.test.ts @@ -95,8 +95,8 @@ const CASES: ReadonlyArray = [ { provider: "google", upstreamModelId: "gemini-2.0-flash", - displayName: "err-norm-gemini", - // The gemini bridge talks to Google's OpenAI-compatibility + displayName: "err-norm-google", + // The google bridge talks to Google's OpenAI-compatibility // endpoint per , // which returns errors in OpenAI envelope shape. The // "normalization" the gateway performs for gemini is therefore From 76733a4d63ad1d741855bb4ddea898e1a8e53e48 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Thu, 14 May 2026 22:55:23 +0800 Subject: [PATCH 3/4] fix: sync OpenAPI enum, update remaining doc references - Add cohere and jina to OpenAPI Model.provider enum - Update docs/feature-matrix.md and provider-keys.md - Fix test name and doc comments in provider-google crate --- crates/aisix-admin/src/openapi.rs | 2 +- crates/aisix-provider-google/src/lib.rs | 6 +++--- docs/configuration/provider-keys.md | 2 +- docs/overview/feature-matrix.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/aisix-admin/src/openapi.rs b/crates/aisix-admin/src/openapi.rs index cf137b6d..62811b6e 100644 --- a/crates/aisix-admin/src/openapi.rs +++ b/crates/aisix-admin/src/openapi.rs @@ -271,7 +271,7 @@ const OPENAPI_JSON: &str = r##"{ "required": ["display_name"], "properties": { "display_name": {"type": "string", "example": "my-gpt4"}, - "provider": {"type": "string", "enum": ["openai","anthropic","google","deepseek"]}, + "provider": {"type": "string", "enum": ["openai","anthropic","google","deepseek","cohere","jina"]}, "model_name": {"type": "string", "example": "gpt-4o"}, "provider_key_id": {"type": "string", "example": "11111111-1111-1111-1111-111111111111"}, "timeout": {"type": "integer", "minimum": 0, "description": "Request timeout in milliseconds. Absent or 0 = no timeout."}, diff --git a/crates/aisix-provider-google/src/lib.rs b/crates/aisix-provider-google/src/lib.rs index 6d65612d..a21b3b5d 100644 --- a/crates/aisix-provider-google/src/lib.rs +++ b/crates/aisix-provider-google/src/lib.rs @@ -6,7 +6,7 @@ //! every field we care about — this crate only relabels the bridge //! (`name() == "google"`) so metrics and logs can distinguish traffic. //! -//! Operators configure Gemini access by setting on the Model: +//! Operators configure Google (Gemini) access by setting on the Model: //! //! ```yaml //! provider_config: @@ -14,7 +14,7 @@ //! api_base: "https://generativelanguage.googleapis.com/v1beta/openai" //! ``` //! -//! The Gemini native `:generateContent` format (different request/response +//! The Google Gemini native `:generateContent` format (different request/response //! shape, split role model) is intentionally out of scope here; routing //! to it would belong in its own crate with its own wire module. @@ -42,7 +42,7 @@ mod tests { use wiremock::{Mock, MockServer, ResponseTemplate}; #[test] - fn bridge_reports_gemini_name() { + fn bridge_reports_google_name() { assert_eq!(google_bridge().name(), "google"); } diff --git a/docs/configuration/provider-keys.md b/docs/configuration/provider-keys.md index 12639507..b82eae9a 100644 --- a/docs/configuration/provider-keys.md +++ b/docs/configuration/provider-keys.md @@ -56,7 +56,7 @@ Each provider has its own convention — the four current bridges do **not** sha | `google` | host plus the OpenAI-compat prefix `/v1beta/openai` | `/chat/completions` | `https://generativelanguage.googleapis.com/v1beta/openai` | | `anthropic` | bare host | `/v1/messages` | `https://api.anthropic.com` | -The OpenAI and Anthropic conventions match each upstream's official SDK — `openai-python` initialises `base_url = "https://api.openai.com/v1"`, while `anthropic-sdk-python` initialises `base_url = "https://api.anthropic.com"` and appends `/v1/messages` itself. DeepSeek is OpenAI-compatible but exposes `/chat/completions` directly at the host root, and Gemini's OpenAI-compatible surface lives under a fixed `/v1beta/openai` prefix that the bridge does not synthesize. +The OpenAI and Anthropic conventions match each upstream's official SDK — `openai-python` initialises `base_url = "https://api.openai.com/v1"`, while `anthropic-sdk-python` initialises `base_url = "https://api.anthropic.com"` and appends `/v1/messages` itself. DeepSeek is OpenAI-compatible but exposes `/chat/completions` directly at the host root, and Google's Gemini OpenAI-compatible surface lives under a fixed `/v1beta/openai` prefix that the bridge does not synthesize. ### Forms the gateway tolerates diff --git a/docs/overview/feature-matrix.md b/docs/overview/feature-matrix.md index 06c13d34..3dffd06d 100644 --- a/docs/overview/feature-matrix.md +++ b/docs/overview/feature-matrix.md @@ -21,7 +21,7 @@ Use it as a navigation aid, not as a replacement for detailed feature pages. | --- | --- | --- | | OpenAI-compatible proxy API | Available | Includes chat, completions, embeddings, images, audio, responses, rerank, and passthrough routes currently wired by the proxy router. | | Anthropic-style `/v1/messages` path | Available | Current behavior is implemented as a first-class route. Feature depth still varies by provider and message content shape. | -| Multi-provider model support | Available | Current provider enum includes OpenAI, Anthropic, Gemini, and DeepSeek. | +| Multi-provider model support | Available | Current provider enum includes OpenAI, Anthropic, Google (Gemini), DeepSeek, Cohere, and Jina. | | Provider-specific passthrough | Available | Use `/passthrough/:provider/*rest` for unsupported or provider-native routes. | | Standalone admin API | Available | Current admin surface includes models, API keys, provider keys, guardrails, cache policies, observability exporters, health, metrics, OpenAPI, and playground. | | API key allowlist authz | Available | Uses hashed caller keys and model allowlists. | From 23bc15af98f78da019527fd2396bd3a121d30a3d Mon Sep 17 00:00:00 2001 From: Jarvis Date: Thu, 14 May 2026 22:57:16 +0800 Subject: [PATCH 4/4] docs: add cohere and jina to all provider lists --- docs/configuration/models.md | 2 +- docs/overview/core-concepts.md | 2 ++ docs/overview/what-is-aisix-ai-gateway.md | 2 ++ docs/reference/provider-compatibility.md | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/configuration/models.md b/docs/configuration/models.md index cf5a78c6..1521ab28 100644 --- a/docs/configuration/models.md +++ b/docs/configuration/models.md @@ -117,7 +117,7 @@ curl -sS -X POST http://127.0.0.1:3001/admin/v1/models \ ## Field Notes - `display_name` is the alias clients send in proxy requests. -- `provider` currently supports `openai`, `anthropic`, `google`, and `deepseek`. +- `provider` currently supports `openai`, `anthropic`, `google`, `deepseek`, `cohere`, and `jina`. - `provider_key_id` must reference an existing `ProviderKey` resource. - `timeout` is in milliseconds. `0` or omission means no timeout. - `cost` stores pricing metadata used by budget and usage accounting paths. diff --git a/docs/overview/core-concepts.md b/docs/overview/core-concepts.md index 886952bc..f26b5965 100644 --- a/docs/overview/core-concepts.md +++ b/docs/overview/core-concepts.md @@ -24,6 +24,8 @@ The current provider enum includes: - `anthropic` - `google` - `deepseek` +- `cohere` +- `jina` ## Provider Key diff --git a/docs/overview/what-is-aisix-ai-gateway.md b/docs/overview/what-is-aisix-ai-gateway.md index 7a69d076..67afc345 100644 --- a/docs/overview/what-is-aisix-ai-gateway.md +++ b/docs/overview/what-is-aisix-ai-gateway.md @@ -80,6 +80,8 @@ The current provider enum includes: - `anthropic` - `google` - `deepseek` +- `cohere` +- `jina` Provider support is not identical across every endpoint. The current high-level support summary is captured in the [Feature Matrix](feature-matrix.md), and the current provider-oriented reference lives in [Provider Compatibility](../reference/provider-compatibility.md). diff --git a/docs/reference/provider-compatibility.md b/docs/reference/provider-compatibility.md index 86827d1b..ca40ea38 100644 --- a/docs/reference/provider-compatibility.md +++ b/docs/reference/provider-compatibility.md @@ -12,6 +12,8 @@ The current provider set is: - `anthropic` - `google` - `deepseek` +- `cohere` +- `jina` ## Compatibility Boundary