Skip to content

Commit 0c7b13c

Browse files
authored
feat(provider): add gemini provider (#21)
1 parent 8567670 commit 0c7b13c

3 files changed

Lines changed: 39 additions & 0 deletions

File tree

docs/internals/llm-types.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ For OpenAI-compatible providers, the concrete definition can stay very small. Th
140140

141141
`OpenAIDef` remains hand-written because it needs its own default quirk profile, while `DeepSeek` is the first macro-generated provider in the new stack.
142142

143+
`GoogleDef` currently stays on Gemini's OpenAI-compatible endpoint in the staged implementation, so it is also expressed as a small provider definition instead of a native Gemini transform. That keeps the migration incremental while leaving room for a later native Gemini provider.
144+
143145
`AnthropicDef` is the first hand-written non-OpenAI-compatible provider in the new stack. It combines a custom `ChatTransform` with `NativeAnthropicMessagesSupport` so Anthropic Messages requests can bypass the hub format when the caller already speaks the native protocol.
144146

145147
### Runtime provider instances

src/gateway/providers/gemini.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use crate::gateway::providers::macros::provider;
2+
3+
provider!(GoogleDef {
4+
display_name: "gemini",
5+
base_url: "https://generativelanguage.googleapis.com/v1beta/openai",
6+
chat_path: "/chat/completions",
7+
auth: api_key_header("x-goog-api-key"),
8+
});
9+
10+
#[cfg(test)]
11+
mod tests {
12+
use super::GoogleDef;
13+
use crate::gateway::{provider_instance::ProviderAuth, traits::ProviderMeta};
14+
15+
#[test]
16+
fn google_def_uses_compatible_gemini_endpoint_and_auth_header() {
17+
let provider = GoogleDef;
18+
let headers = provider
19+
.build_auth_headers(&ProviderAuth::ApiKey("gemini-key".into()))
20+
.unwrap();
21+
22+
assert_eq!(provider.name(), "gemini");
23+
assert_eq!(
24+
provider.default_base_url(),
25+
"https://generativelanguage.googleapis.com/v1beta/openai"
26+
);
27+
assert_eq!(
28+
provider.build_url(provider.default_base_url(), "gemini-2.5-flash"),
29+
"https://generativelanguage.googleapis.com/v1beta/openai/chat/completions"
30+
);
31+
assert_eq!(headers["x-goog-api-key"], "gemini-key");
32+
}
33+
}

src/gateway/providers/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
pub mod anthropic;
22
pub mod deepseek;
3+
pub mod gemini;
34
pub mod macros;
45
pub mod openai;
56

67
pub use anthropic::AnthropicDef;
78
pub use deepseek::DeepSeek;
9+
pub use gemini::GoogleDef;
810
pub use openai::OpenAIDef;
911

1012
use crate::gateway::{error::Result, provider_instance::ProviderRegistry};
@@ -13,6 +15,7 @@ pub fn default_provider_registry() -> Result<ProviderRegistry> {
1315
let builder = ProviderRegistry::builder()
1416
.register(OpenAIDef)?
1517
.register(AnthropicDef)?
18+
.register(GoogleDef)?
1619
.register(DeepSeek)?;
1720
Ok(builder.build())
1821
}
@@ -27,6 +30,7 @@ mod tests {
2730

2831
assert_eq!(registry.get("openai").unwrap().name(), "openai");
2932
assert_eq!(registry.get("anthropic").unwrap().name(), "anthropic");
33+
assert_eq!(registry.get("gemini").unwrap().name(), "gemini");
3034
assert_eq!(registry.get("deepseek").unwrap().name(), "deepseek");
3135
assert!(registry.get("missing").is_none());
3236
}

0 commit comments

Comments
 (0)