Skip to content

Commit 4c83fa4

Browse files
authored
feat: Gemini openai compatibility (#353)
* fix: change id and created fields to Option types in response structs (makes loose deserialization which give advantage to gemini openai compatibility) * fix: change created field to Option type in ImagesResponse struct for better deserialization * feat: add example for Gemini OpenAI compatibility with async_openai integration * fix: rollbacked type changes in async-openai, added more examples using byot features
1 parent c9acfed commit 4c83fa4

File tree

6 files changed

+540
-0
lines changed

6 files changed

+540
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "gemini-openai-compatibility"
3+
version = "0.1.0"
4+
edition = "2021"
5+
rust-version.workspace = true
6+
7+
[dependencies]
8+
async-openai = {path = "../../async-openai", features = ["byot"]}
9+
tokio = { version = "1.43.0", features = ["full"] }
10+
tracing-subscriber = { version = "0.3.19", features = ["env-filter"]}
11+
dotenv = "0.15.0"
12+
futures = "0.3.31"
13+
serde_json = "1.0.100"
14+
serde = { version = "1.0", features = ["derive"] }
15+
base64 = "0.22.1"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Gemini's OpenAI Compatibility Example
2+
3+
This example demonstrates how to use OpenAI's `async_openai` Rust library with Google's Gemini API. By modifying a few lines of configuration, you can integrate Gemini models while maintaining OpenAI compatibility.
4+
5+
## Features
6+
- **List Available Models**: Fetch a list of supported Gemini models.
7+
- **Retrieve Model Details**: Get detailed information about the `gemini-1.5-flash` model.
8+
- **Chat Completion**: Perform chat completions using Gemini's API.
9+
- **Stream Chat Messages**: Receive streaming responses for chat queries in real-time.
10+
- **Generate Images**: Leverage Gemini's image generation capabilities.
11+
- **Understand Images**: Analyze and extract information from images.
12+
- **Understand Audio**: Process and interpret audio inputs.
13+
- **Structured Output Response**: Generate structured outputs for complex queries.
14+
- **Function Calling**: Invoke functions dynamically based on input prompts.
15+
- **Create Embeddings**: Generate embeddings for text or other data types.
16+
- **Bring Your Own Type (BYOT)**: Use custom Gemini response types defined in `gemini_type.rs`.
17+
18+
## Prerequisites
19+
- Rust installed (`rustc` and `cargo`)
20+
- Set up your Google Gemini API key from [Google AI Studio](https://aistudio.google.com/)
21+
- Create a `.env` file with:
22+
```plaintext
23+
GEMINI_API_KEY=your_api_key_here
24+
```
25+
- Install dependencies:
26+
```sh
27+
cargo add async-openai dotenv futures tokio
28+
```
29+
30+
## Enabling BYOT Feature
31+
To enable the BYOT (Bring Your Own Type) feature in `async-openai`, modify your `Cargo.toml` as follows:
32+
```toml
33+
async-openai = {version = '{{version}}', features = ["byot"]}
34+
```
35+
36+
## Usage
37+
This example now uses the `byot` (Bring Your Own Type) feature to define custom types for Gemini responses. The Gemini types are defined in `gemini_type.rs`, and methods using these types have the `_byot` suffix.
38+
39+
### Running the Example
40+
To run the example:
41+
```sh
42+
cargo run
43+
```
44+
This will:
45+
1. List available models
46+
2. Retrieve details of `gemini-1.5-flash`
47+
3. Generate chat completion responses
48+
4. Stream chat messages
49+
5. Generate an image
50+
6. Understanding an image
51+
7. Understanding an audio
52+
8. Structured output response
53+
9. Function calling
54+
10. Create Embeddings
55+
56+
57+
## References
58+
- [Google Gemini's OpenAI compatibility](https://ai.google.dev/gemini-api/docs/openai)
59+
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use std::pin::Pin;
2+
3+
/// Gemini types (Generally user defined types) for Gemini API
4+
use async_openai::{
5+
error::OpenAIError,
6+
types::{ChatChoice, ChatChoiceStream, CompletionUsage, Image},
7+
};
8+
use futures::Stream;
9+
use serde::{Deserialize, Serialize};
10+
11+
#[derive(Debug, Serialize, Deserialize)]
12+
pub struct GeminiModel {
13+
pub id: String,
14+
pub object: String,
15+
pub owned_by: String,
16+
}
17+
18+
#[derive(Debug, Serialize, Deserialize)]
19+
pub struct ListGeminiModelResponse {
20+
pub data: Vec<GeminiModel>,
21+
pub object: String,
22+
}
23+
24+
#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
25+
/// Represents a streamed chunk of a chat completion response returned by model, based on the provided input.
26+
pub struct GeminiCreateChatCompletionStreamResponse {
27+
/// A list of chat completion choices. Can contain more than one elements if `n` is greater than 1. Can also be empty for the last chunk if you set `stream_options: {"include_usage": true}`.
28+
pub choices: Vec<ChatChoiceStream>,
29+
30+
/// The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp.
31+
pub created: u32,
32+
/// The model to generate the completion.
33+
pub model: String,
34+
35+
/// The object type, which is always `chat.completion.chunk`.
36+
pub object: String,
37+
38+
/// An optional field that will only be present when you set `stream_options: {"include_usage": true}` in your request.
39+
/// When present, it contains a null value except for the last chunk which contains the token usage statistics for the entire request.
40+
pub usage: Option<CompletionUsage>,
41+
}
42+
43+
/// A stream of chat completion responses.
44+
pub type GeminiChatCompletionResponseStream = Pin<
45+
Box<dyn Stream<Item = Result<GeminiCreateChatCompletionStreamResponse, OpenAIError>> + Send>,
46+
>;
47+
48+
/// Represents a chat completion response returned by model, based on the provided input.
49+
#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
50+
pub struct GeminiCreateChatCompletionResponse {
51+
/// A list of chat completion choices. Can be more than one if `n` is greater than 1.
52+
pub choices: Vec<ChatChoice>,
53+
/// The Unix timestamp (in seconds) of when the chat completion was created.
54+
pub created: u32,
55+
/// The model used for the chat completion.
56+
pub model: String,
57+
/// The object type, which is always `chat.completion`.
58+
pub object: String,
59+
/// usage statistics for the entire request.
60+
pub usage: Option<CompletionUsage>,
61+
}
62+
63+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
64+
pub struct GeminiImagesResponse {
65+
pub data: Vec<std::sync::Arc<Image>>,
66+
}
67+
68+
/// Represents an embedding vector returned by embedding endpoint.
69+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
70+
pub struct GeminiEmbedding {
71+
/// The object type, which is always "embedding".
72+
pub object: String,
73+
/// The embedding vector, which is a list of floats. The length of vector
74+
pub embedding: Vec<f32>,
75+
}
76+
77+
#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
78+
pub struct GeminiCreateEmbeddingResponse {
79+
pub object: String,
80+
/// The name of the model used to generate the embedding.
81+
pub model: String,
82+
/// The list of embeddings generated by the model.
83+
pub data: Vec<GeminiEmbedding>,
84+
}

0 commit comments

Comments
 (0)