Skip to content

feat: Gemini openai compatibility #353

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/gemini-openai-compatibility/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "gemini-openai-compatibility"
version = "0.1.0"
edition = "2021"
rust-version.workspace = true

[dependencies]
async-openai = {path = "../../async-openai", features = ["byot"]}
tokio = { version = "1.43.0", features = ["full"] }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"]}
dotenv = "0.15.0"
futures = "0.3.31"
serde_json = "1.0.100"
serde = { version = "1.0", features = ["derive"] }
base64 = "0.22.1"
59 changes: 59 additions & 0 deletions examples/gemini-openai-compatibility/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Gemini's OpenAI Compatibility Example

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.

## Features
- **List Available Models**: Fetch a list of supported Gemini models.
- **Retrieve Model Details**: Get detailed information about the `gemini-1.5-flash` model.
- **Chat Completion**: Perform chat completions using Gemini's API.
- **Stream Chat Messages**: Receive streaming responses for chat queries in real-time.
- **Generate Images**: Leverage Gemini's image generation capabilities.
- **Understand Images**: Analyze and extract information from images.
- **Understand Audio**: Process and interpret audio inputs.
- **Structured Output Response**: Generate structured outputs for complex queries.
- **Function Calling**: Invoke functions dynamically based on input prompts.
- **Create Embeddings**: Generate embeddings for text or other data types.
- **Bring Your Own Type (BYOT)**: Use custom Gemini response types defined in `gemini_type.rs`.

## Prerequisites
- Rust installed (`rustc` and `cargo`)
- Set up your Google Gemini API key from [Google AI Studio](https://aistudio.google.com/)
- Create a `.env` file with:
```plaintext
GEMINI_API_KEY=your_api_key_here
```
- Install dependencies:
```sh
cargo add async-openai dotenv futures tokio
```

## Enabling BYOT Feature
To enable the BYOT (Bring Your Own Type) feature in `async-openai`, modify your `Cargo.toml` as follows:
```toml
async-openai = {version = '{{version}}', features = ["byot"]}
```

## Usage
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.

### Running the Example
To run the example:
```sh
cargo run
```
This will:
1. List available models
2. Retrieve details of `gemini-1.5-flash`
3. Generate chat completion responses
4. Stream chat messages
5. Generate an image
6. Understanding an image
7. Understanding an audio
8. Structured output response
9. Function calling
10. Create Embeddings


## References
- [Google Gemini's OpenAI compatibility](https://ai.google.dev/gemini-api/docs/openai)

Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
84 changes: 84 additions & 0 deletions examples/gemini-openai-compatibility/src/gemini_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::pin::Pin;

/// Gemini types (Generally user defined types) for Gemini API
use async_openai::{
error::OpenAIError,
types::{ChatChoice, ChatChoiceStream, CompletionUsage, Image},
};
use futures::Stream;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct GeminiModel {
pub id: String,
pub object: String,
pub owned_by: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ListGeminiModelResponse {
pub data: Vec<GeminiModel>,
pub object: String,
}

#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
/// Represents a streamed chunk of a chat completion response returned by model, based on the provided input.
pub struct GeminiCreateChatCompletionStreamResponse {
/// 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}`.
pub choices: Vec<ChatChoiceStream>,

/// The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp.
pub created: u32,
/// The model to generate the completion.
pub model: String,

/// The object type, which is always `chat.completion.chunk`.
pub object: String,

/// An optional field that will only be present when you set `stream_options: {"include_usage": true}` in your request.
/// When present, it contains a null value except for the last chunk which contains the token usage statistics for the entire request.
pub usage: Option<CompletionUsage>,
}

/// A stream of chat completion responses.
pub type GeminiChatCompletionResponseStream = Pin<
Box<dyn Stream<Item = Result<GeminiCreateChatCompletionStreamResponse, OpenAIError>> + Send>,
>;

/// Represents a chat completion response returned by model, based on the provided input.
#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
pub struct GeminiCreateChatCompletionResponse {
/// A list of chat completion choices. Can be more than one if `n` is greater than 1.
pub choices: Vec<ChatChoice>,
/// The Unix timestamp (in seconds) of when the chat completion was created.
pub created: u32,
/// The model used for the chat completion.
pub model: String,
/// The object type, which is always `chat.completion`.
pub object: String,
/// usage statistics for the entire request.
pub usage: Option<CompletionUsage>,
}

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct GeminiImagesResponse {
pub data: Vec<std::sync::Arc<Image>>,
}

/// Represents an embedding vector returned by embedding endpoint.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct GeminiEmbedding {
/// The object type, which is always "embedding".
pub object: String,
/// The embedding vector, which is a list of floats. The length of vector
pub embedding: Vec<f32>,
}

#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
pub struct GeminiCreateEmbeddingResponse {
pub object: String,
/// The name of the model used to generate the embedding.
pub model: String,
/// The list of embeddings generated by the model.
pub data: Vec<GeminiEmbedding>,
}
Loading