Overview
The OpenAIProvider implements the LLMProvider trait for OpenAI’s API, supporting GPT-4, GPT-3.5, and o1 models. It also works with OpenAI-compatible APIs like Ollama, vLLM, and LocalAI.
Configuration
OpenAIConfig
pub struct OpenAIConfig {
pub api_key: String,
pub base_url: Option<String>,
pub org_id: Option<String>,
pub default_model: String,
pub default_temperature: f32,
pub default_max_tokens: u32,
pub timeout_secs: u64,
}
OpenAI API key (starts with sk-)
Custom API base URL (default: https://api.openai.com/v1)Use this for:
- Azure OpenAI endpoints
- OpenAI-compatible local services
- Proxy servers
OpenAI organization ID for multi-org accounts
Default model to use when not specified in requests
Default sampling temperature (0.0 to 2.0)
Default maximum tokens to generate
Request timeout in seconds
Creating a Provider
Basic Usage
use mofa_foundation::llm::OpenAIProvider;
let provider = OpenAIProvider::new("sk-xxx");
From Environment
// Reads OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL
let provider = OpenAIProvider::from_env();
With Configuration
use mofa_foundation::llm::{OpenAIProvider, OpenAIConfig};
let config = OpenAIConfig::new("sk-xxx")
.with_model("gpt-4o-mini")
.with_temperature(0.8)
.with_max_tokens(2048)
.with_timeout(120);
let provider = OpenAIProvider::with_config(config);
Supported Models
GPT-4o Series
- gpt-4o: Most capable model with vision (128K context)
- gpt-4o-mini: Smaller, faster GPT-4o (128K context)
GPT-4 Turbo
- gpt-4-turbo: GPT-4 Turbo with vision (128K context)
- gpt-4: Standard GPT-4 (8K context)
GPT-3.5
- gpt-3.5-turbo: Fast and cost-effective (16K context)
o1 Series
- o1: Advanced reasoning model
- o1-mini: Compact reasoning model
- o1-preview: Preview version
Features
Streaming Support
use futures::StreamExt;
use mofa_foundation::llm::{LLMClient, OpenAIProvider};
use std::sync::Arc;
let provider = Arc::new(OpenAIProvider::new("sk-xxx"));
let client = LLMClient::new(provider);
let mut stream = client.chat()
.user("Tell me a story")
.send_stream()
.await?;
while let Some(chunk) = stream.next().await {
if let Some(content) = chunk?.content() {
print!("{}", content);
}
}
Vision (Multi-Modal)
use mofa_foundation::llm::*;
let provider = Arc::new(OpenAIProvider::new("sk-xxx"));
let client = LLMClient::new(provider);
let content = MessageContent::Parts(vec![
ContentPart::Text {
text: "What is in this image?".to_string()
},
ContentPart::Image {
image_url: ImageUrl {
url: "data:image/png;base64,iVBORw0KG...".to_string(),
detail: Some(ImageDetail::High),
},
},
]);
let response = client.chat()
.user_with_content(content)
.send()
.await?;
use mofa_foundation::llm::*;
use serde_json::json;
let tool = function_tool(
"get_weather",
"Get the current weather for a location",
json!({
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
})
);
let response = client.chat()
.user("What's the weather in Paris?")
.tool(tool)
.send()
.await?;
// Check for tool calls
if let Some(tool_calls) = response.tool_calls() {
for call in tool_calls {
println!("Tool: {} Args: {}", call.function.name, call.function.arguments);
}
}
JSON Mode
let response = client.chat()
.system("You are a JSON API. Return {\"answer\": string}")
.user("What is 2+2?")
.json_mode()
.send()
.await?;
let content = response.content().unwrap();
let json: serde_json::Value = serde_json::from_str(content)?;
println!("Answer: {}", json["answer"]);
Embeddings
let provider = Arc::new(OpenAIProvider::new("sk-xxx"));
let client = LLMClient::new(provider);
// Single embedding
let embedding = client.embed("Hello world").await?;
println!("Dimension: {}", embedding.len());
// Batch embeddings
let embeddings = client.embed_batch(vec![
"First text".to_string(),
"Second text".to_string(),
]).await?;
OpenAI-Compatible APIs
Ollama
let provider = OpenAIProvider::ollama("llama3");
// Equivalent to:
// OpenAIProvider::local("http://localhost:11434/v1", "llama3")
Custom Local Service
let provider = OpenAIProvider::local(
"http://localhost:8080/v1",
"custom-model"
);
vLLM
let provider = OpenAIProvider::local(
"http://localhost:8000/v1",
"meta-llama/Llama-2-7b-chat-hf"
);
Azure OpenAI
let provider = OpenAIProvider::azure(
"https://your-resource.openai.azure.com",
"api-key",
"deployment-name"
);
let client = LLMClient::new(Arc::new(provider));
let response = client.ask("Hello").await?;
Error Handling
The provider maps OpenAI errors to LLMError variants:
use mofa_foundation::llm::LLMError;
match client.chat().user("Test").send().await {
Ok(response) => println!("Success: {}", response.content().unwrap()),
Err(LLMError::RateLimited(msg)) => println!("Rate limited: {}", msg),
Err(LLMError::QuotaExceeded(msg)) => println!("Quota exceeded: {}", msg),
Err(LLMError::ModelNotFound(msg)) => println!("Model not found: {}", msg),
Err(LLMError::ContextLengthExceeded(msg)) => println!("Context too long: {}", msg),
Err(LLMError::Timeout(msg)) => println!("Timeout: {}", msg),
Err(e) => println!("Other error: {}", e),
}
let info = provider.get_model_info("gpt-4o").await?;
println!("Model: {}", info.name);
println!("Context window: {} tokens", info.context_window.unwrap());
println!("Max output: {} tokens", info.max_output_tokens.unwrap());
println!("Supports streaming: {}", info.capabilities.streaming);
println!("Supports tools: {}", info.capabilities.tools);
println!("Supports vision: {}", info.capabilities.vision);
Complete Example
use mofa_foundation::llm::*;
use std::sync::Arc;
use futures::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create provider
let config = OpenAIConfig::new(std::env::var("OPENAI_API_KEY")?)
.with_model("gpt-4o")
.with_temperature(0.7);
let provider = Arc::new(OpenAIProvider::with_config(config));
let client = LLMClient::new(provider.clone());
// Simple query
let answer = client.ask("What is Rust?").await?;
println!("Answer: {}\n", answer);
// Streaming
println!("Streaming story:");
let mut stream = client.chat()
.user("Tell a very short story")
.send_stream()
.await?;
while let Some(chunk) = stream.next().await {
if let Some(content) = chunk?.content() {
print!("{}", content);
}
}
println!("\n");
// Tool calling
let weather_tool = function_tool(
"get_weather",
"Get weather for a location",
serde_json::json!({
"type": "object",
"properties": {
"location": { "type": "string" }
},
"required": ["location"]
})
);
let response = client.chat()
.user("What's the weather in Tokyo?")
.tool(weather_tool)
.send()
.await?;
if let Some(calls) = response.tool_calls() {
for call in calls {
println!("Would call: {} with {}", call.function.name, call.function.arguments);
}
}
// Check capabilities
println!("\nProvider: {}", provider.name());
println!("Supports streaming: {}", provider.supports_streaming());
println!("Supports tools: {}", provider.supports_tools());
println!("Supports vision: {}", provider.supports_vision());
Ok(())
}
Environment Variables
OPENAI_API_KEY: API key
OPENAI_BASE_URL: Custom base URL
OPENAI_MODEL: Default model