| Crates.io | open-ai-rust-responses-by-sshift |
| lib.rs | open-ai-rust-responses-by-sshift |
| version | 0.4.3 |
| created_at | 2025-05-24 20:10:16.222401+00 |
| updated_at | 2025-11-26 22:47:58.278348+00 |
| description | A feature-rich, async-first Rust wrapper for the OpenAI Responses API, with built-in support for streaming, function calling, file handling, and enhanced response monitoring |
| homepage | |
| repository | https://github.com/Singularity-Shift/openai-rust-responses-sshift |
| max_upload_size | |
| id | 1687721 |
| size | 712,811 |
π οΈ v0.4.3 Update: Code Quality Improvements - Fixed all Clippy warnings, added
#[must_use]attributes, and improved code quality. Zero breaking changes - all improvements are internal enhancements. π v0.4.2 Update: Streaming Enhancements - EnhancedResponseCreatedevent support with response ID tracking, improved streaming tests, and comprehensive helper methods. Full event type coverage for production-ready streaming. π v0.4.1 Update: MCP Authorization - Addedwith_bearer_token()convenience method for secure MCP server connections. Simplifies Bearer token authentication with automatic header formatting. Fully backward compatible. π v0.4.0 Update: Unified Tool Management - NewToolRegistryfor seamlessly combining local Rust tools with remote MCP tools. Priority routing automatically handles tool dispatch. AddedLocalTooltrait. Fully backward compatible - all changes are additive. π v0.3.0 Update: GPTβ5 family support (flagship, mini, nano), new verbosity control, reasoning effort tuning for GPTβ5, structured/freeβform function improvements, and an expanded example. Note: sourceβlevel break only for users constructing public structs with literals or exhaustively matchingModel. π‘οΈ v0.2.5 Update: Advanced Container Recovery System - Revolutionary error handling! SDK now automatically handles container expiration with configurable recovery policies. Choose from Default (auto-retry), Conservative (manual control), or Aggressive (maximum resilience) strategies. Zero breaking changes! π¨ v0.2.4 Update: Image-Guided Generation - Revolutionary new feature! Use input images to guide image generation with the GPT Image 1 model. Create style transfers, combine multiple images into logos, and generate artistic interpretations. See the comprehensive new example! π§βπ» v0.2.3 Update: Code Interpreter tool support! Run Python code in a secure container and get results directly from the model. See the new example and docs. π₯ v0.2.0 Update: Major update to image generation! The SDK now supports the official built-inimage_generationtool, replacing the previous function-based workaround. This is a breaking change. π v0.2.1 Update: Vision input landed! Supply images withinput_image_url(...)and get descriptions from GPT-4o. π v0.2.2 Update: Multi-image vision! Compare or analyse multiple pictures withinput_image_urlsorpush_image_url.
A comprehensive, async Rust SDK for the OpenAI Responses API with advanced reasoning capabilities, background processing, enhanced models, production-ready streaming, working image generation, and revolutionary image-guided generation.
tokio and reqwest for high performanceuse open_ai_rust_responses_by_sshift::{Client, Request, Model, ReasoningEffort, Verbosity};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
// Standard generation (no reasoning params required)
let standard = Request::builder()
.model(Model::GPT5Mini)
.input("Summarize this paragraph in 2 bullet points.")
.verbosity(Verbosity::Low) // Optional (GPTβ5 only): Low|Medium|High
.build();
// Reasoning-style control (maps to reasoning.effort on the wire)
let reasoning = Request::builder()
.model(Model::GPT5)
.input("Plan a multi-step data migration with trade-offs.")
.reasoning_effort(ReasoningEffort::High) // Minimal|Medium|High
.build();
let _ = client.responses.create(standard).await?;
let _ = client.responses.create(reasoning).await?;
Ok(())
}
See examples/gpt5_demo.rs for a complete, runnable showcase with function calling and usage reporting.
This SDK includes cutting-edge features with full API parity:
Connect to any remote MCP server to extend your AI's capabilities with external tools and resources.
use open_ai_rust_responses_by_sshift::mcp::{McpClient, transport::HttpTransport};
// Connect to a remote MCP server via HTTP (without authentication)
let transport = HttpTransport::new("http://localhost:8000/mcp");
let client = McpClient::new(Box::new(transport));
client.initialize().await?;
// Or connect with Bearer token authentication
let transport = HttpTransport::new("http://localhost:8000/mcp")
.with_bearer_token("your-api-token-here")?;
let client = McpClient::new(Box::new(transport));
client.initialize().await?;
// Or add custom headers
let transport = HttpTransport::new("http://localhost:8000/mcp")
.with_header("X-Custom-Header", "value")?
.with_bearer_token("your-api-token")?;
// HttpTransport implements Clone, so you can clone it for reuse
let transport = HttpTransport::new("http://localhost:8000/mcp")
.with_bearer_token("your-api-token")?;
let cloned_transport = transport.clone(); // Clone preserves headers and configuration
// List available tools
let tools = client.list_tools().await?;
// Call a tool
let result = client.call_tool("read_file", json!({ "path": "/path/to/file.txt" })).await?;
Interact with OpenAI's Realtime API for low-latency, multimodal experiences.
use open_ai_rust_responses_by_sshift::realtime::RealtimeClient;
// Connect to the Realtime API
let mut client = RealtimeClient::connect("sk-...", "gpt-4o-realtime-preview").await?;
// Send an event
client.send_event(json!({
"type": "response.create",
"response": {
"modalities": ["text"],
"instructions": "Hello, world!"
}
})).await?;
// Receive events
while let Some(event) = client.receive_event().await? {
println!("Received event: {:?}", event);
}
Revolutionary error handling: SDK automatically detects and recovers from expired containers without breaking user flow!
RetryScope and
inspect the active policy via client.responses.recovery_policy().Client::from_env_with_recovery_policy() to load optional overrides
(OAI_RECOVERY_MAX_RETRIES, OAI_RECOVERY_AUTO_RETRY,
OAI_RECOVERY_AUTO_PRUNE, OAI_RECOVERY_LOG, OAI_RECOVERY_SCOPE). Leaving
them unset preserves legacy defaults.create_no_recovery when you need the very first error without any
retry loop.use open_ai_rust_responses_by_sshift::{Client, RecoveryPolicy, Request, Tool, Container};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Choose your recovery strategy
let policy = RecoveryPolicy::default() // Auto-retry: 1 attempt
.with_auto_retry(true)
.with_notify_on_reset(true)
.with_reset_message("Your session was refreshed for optimal performance.");
let client = Client::new_with_recovery(&api_key, policy)?;
// Make requests normally - container expiration handled automatically!
let request = Request::builder()
.model("gpt-4o-mini")
.input("Continue our Python session from earlier...")
.tools(vec![Tool::code_interpreter(Some(Container::auto_type()))])
.previous_response_id("resp_123") // May reference expired container
.build();
// SDK automatically handles expiration and retries with fresh context
let response = client.responses.create(request).await?;
println!("Success: {}", response.output_text());
Ok(())
}
// Inspect the policy currently in use
let policy = client.responses.recovery_policy();
println!("retry scope: {}", policy.retry_scope.as_str());
Recovery Policies:
// Default: Balanced approach (recommended)
let client = Client::new(&api_key)?; // Auto-retry enabled, 1 attempt
// Conservative: Full control
let policy = RecoveryPolicy::conservative(); // No auto-retry, notifications on
let client = Client::new_with_recovery(&api_key, policy)?;
// Aggressive: Maximum resilience
let policy = RecoveryPolicy::aggressive(); // Auto-retry enabled, 3 attempts
let client = Client::new_with_recovery(&api_key, policy)?;
Advanced Recovery Information:
// Get detailed recovery information
let response_with_recovery = client.responses.create_with_recovery(request).await?;
if response_with_recovery.had_recovery() {
println!("Recovery performed:");
println!("- Attempts: {}", response_with_recovery.recovery_info.retry_count);
println!("- Successful: {}", response_with_recovery.recovery_info.successful);
if let Some(msg) = response_with_recovery.recovery_message() {
println!("- Message: {}", msg);
}
}
println!("Response: {}", response_with_recovery.response.output_text());
Skip Recovery When Needed:
// Surface the first failure without retrying
let response = client.responses.create_no_recovery(request).await?;
Manual Context Pruning:
// Proactively clean expired context
let cleaned_request = client.responses.prune_expired_context_manual(request);
let response = client.responses.create(cleaned_request).await?;
Debugging Retries:
let verbose_policy = RecoveryPolicy::default()
.with_logging(true)
.with_max_retries(2);
let client = Client::new_with_recovery(&api_key, verbose_policy)?;
DEBUG Preparing to send attempt 1 (retry_count=0, has_last_error=false)
DEBUG handle_error_with_retry: classification=container_expired, scope=all_recoverable, retry_count=0->1, retry_after=1s, decision=Continue
DEBUG Preparing to send attempt 2 (retry_count=1, has_last_error=true)
INFO Successfully recovered after 1 attempt(s) (classification=container_expired)
Environment Overrides:
use open_ai_rust_responses_by_sshift::{Client, RecoveryPolicy};
// Load defaults, overriding only when specific env vars are provided
let policy = RecoveryPolicy::from_env();
// OPENAI_API_KEY is still required, but recovery env vars are optional
let client = Client::from_env_with_recovery_policy()?;
Environment variables only adjust the fields you setβeverything else keeps the library defaults. Supported overrides include:
OAI_RECOVERY_MAX_RETRIES (u32)OAI_RECOVERY_AUTO_RETRY (bool)OAI_RECOVERY_AUTO_PRUNE (bool)OAI_RECOVERY_LOG (bool)OAI_RECOVERY_SCOPE (all, container, or transient)Key Benefits:
Test Container Expiration:
cargo run --example container_expiration_test
Revolutionary feature: Use input images to guide image generation with the GPT Image 1 model!
use open_ai_rust_responses_by_sshift::{Client, InputItem, Request, Tool, Model};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
// Example: Style transfer - transform an image into Van Gogh style
let reference_image = "https://example.com/landscape.jpg";
let request = Request::builder()
.model(Model::GPT4o)
.input_items(vec![
// System message for context
InputItem::message("system", vec![
InputItem::content_text("You are an expert in artistic style transfer.")
]),
// User message with image and instructions
InputItem::message("user", vec![
InputItem::content_text("Transform this landscape into Van Gogh's Starry Night style - swirling skies, bold brushstrokes, vibrant blues and yellows."),
InputItem::content_image_with_detail(reference_image, "high")
])
])
.tools(vec![Tool::image_generation()])
.temperature(0.8)
.build();
let response = client.responses.create(request).await?;
// Generated image is in response.output as ImageGenerationCall
println!("Style transfer complete: {}", response.output_text());
Ok(())
}
Multi-Image Logo Creation:
// Combine elements from multiple reference images
let request = Request::builder()
.model(Model::GPT4o)
.input_items(vec![
InputItem::message("user", vec![
InputItem::content_text("Create a modern logo combining the natural serenity from the first image with the character from the second image."),
InputItem::content_image_with_detail(nature_image, "high"),
InputItem::content_image_with_detail(character_image, "high")
])
])
.tools(vec![Tool::image_generation()])
.build();
Real-World Applications:
Run the comprehensive example:
cargo run --example image_guided_generation
use open_ai_rust_responses_by_sshift::{Client, ImageGenerateRequest};
// Method 1: Direct image generation via Images API
let image_request = ImageGenerateRequest::new("A serene mountain landscape")
.with_size("1024x1024")
.with_quality("high");
let image_response = client.images.generate(image_request).await?;
if let Some(url) = &image_response.data[0].url {
println!("Image URL: {}", url);
}
// Method 2: AI-triggered image generation via the new built-in tool
let request = Request::builder()
.model(Model::GPT4oMini)
.input("Create an image of a futuristic city")
.tools(vec![Tool::image_generation()]) // Use the new, simple tool
.build();
// The model handles image generation and returns the data directly
let response = client.responses.create(request).await?;
// See examples/image_generation_builtin.rs for how to save the image
use open_ai_rust_responses_by_sshift::{Client, Request, Model};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
// Public demo image
let image_url = "https://storage.googleapis.com/sshift-gpt-bucket/ledger-app/generated-image-1746132697428.png";
let request = Request::builder()
.model(Model::GPT4o) // GPT-4o or GPT-4oMini for vision
.input_image_url(image_url) // New helper does all the heavy lifting
.instructions("Describe the image in detail, mentioning colours, objects, and composition.")
.build();
let response = client.responses.create(request).await?;
println!("Description: {}", response.output_text());
Ok(())
}
Run it:
cargo run --example image_input --features stream
use open_ai_rust_responses_by_sshift::{Client, Request, Model, Tool};
use open_ai_rust_responses_by_sshift::types::Container;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
let request = Request::builder()
.model(Model::GPT4o)
.input("Calculate the 47th digit of pi using Python.")
.tools(vec![Tool::code_interpreter(Some(Container::auto_type()))])
.build();
let response = client.responses.create(request).await?;
println!("Result: {}", response.output_text());
Ok(())
}
use open_ai_rust_responses_by_sshift::types::{ReasoningParams, Effort, SummarySetting};
// Optimized configuration - fast and cost-effective
let request = Request::builder()
.model(Model::O4Mini) // Specialized reasoning model
.input("Solve this complex problem step by step")
.reasoning(ReasoningParams::new()
.with_effort(Effort::Low) // Fast responses
.with_summary(SummarySetting::Auto)) // Auto-generated summaries
.max_output_tokens(2000) // Reasoning models need more tokens
// Note: O4Mini doesn't support temperature (built-in optimization)
.build();
use open_ai_rust_responses_by_sshift::types::BackgroundHandle;
// Enable background mode for long-running tasks
let request = Request::builder()
.model(Model::O4Mini) // Efficient for background tasks
.input("Perform comprehensive analysis...")
.reasoning(ReasoningParams::new().with_effort(Effort::Low))
.background(true) // Returns HTTP 202 with handle for polling
.build();
// Would return BackgroundHandle for status polling
let response = client.responses.create(request).await?;
// Recommended models for different use cases
Model::GPT4oMini // Best default choice (recommended for most use cases)
Model::GPT4o // Advanced conversations
Model::O4Mini // Efficient reasoning tasks (2000 token default)
Model::O3 // Complex reasoning (most capable)
Model::O1 // Original reasoning model
Model::O1Mini // Compact reasoning
Model::O1Preview // Preview version
Model::GPT4o20241120 // Specific version
// ... and more
use open_ai_rust_responses_by_sshift::types::Include;
// Compile-time validated includes (API-compatible values)
let request = Request::builder()
.model(Model::GPT4oMini)
.input("Search and analyze")
.include(vec![
Include::FileSearchResults, // file_search_call.results
Include::WebSearchResults, // web_search_call.results
Include::ReasoningEncryptedContent, // reasoning.encrypted_content
])
.build();
// New response fields for comprehensive monitoring
let response = client.responses.create(request).await?;
// Status tracking
println!("Status: {}", response.status); // "completed", "in_progress", etc.
println!("Complete: {}", response.is_complete());
println!("Has errors: {}", response.has_errors());
// Token analytics
if let Some(usage) = &response.usage {
println!("Total tokens: {}", usage.total_tokens);
if let Some(details) = &usage.output_tokens_details {
println!("Reasoning tokens: {:?}", details.reasoning_tokens);
}
}
// Parameter echoing
println!("Temperature used: {:?}", response.temperature);
println!("Max output tokens: {:?}", response.max_output_tokens);
Want to try it right now?
# Add to Cargo.toml
cargo add open-ai-rust-responses-by-sshift tokio --features tokio/full
# Set your API key
export OPENAI_API_KEY=sk-your-api-key
# Run the comprehensive demo
cargo run --example comprehensive_demo --features stream
Add this to your Cargo.toml:
[dependencies]
open-ai-rust-responses-by-sshift = "0.4.3"
tokio = { version = "1.0", features = ["full"] }
# Optional: Enable streaming
# open-ai-rust-responses-by-sshift = { version = "0.4.3", features = ["stream"] }
use open_ai_rust_responses_by_sshift::{Client, Request, Model, Input};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create client with API key
let client = Client::new("sk-your-api-key")?;
// Or use environment variable
let client = Client::from_env()?;
// Create a simple request
let request = Request::builder()
.model(Model::GPT4oMini) // Recommended default model
.input("Hello, how are you today?")
.temperature(0.7)
.max_output_tokens(500) // Optimized for completion
.build();
// Get response
let response = client.responses.create(request).await?;
println!("Response: {}", response.output_text());
Ok(())
}
use open_ai_rust_responses_by_sshift::{Client, Request, Model};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
// First message
let request = Request::builder()
.model(Model::GPT4oMini) // Recommended default
.input("My name is Alice. What's a good recipe for pasta?")
.build();
let response1 = client.responses.create(request).await?;
println!("Chef: {}", response1.output_text());
// Continue conversation with response ID
let request2 = Request::builder()
.model(Model::GPT4oMini)
.input("Can you make it vegetarian?")
.previous_response_id(response1.id())
.build();
let response2 = client.responses.create(request2).await?;
println!("Chef: {}", response2.output_text());
Ok(())
}
use open_ai_rust_responses_by_sshift::{Client, Request, Model, Tool, ImageGenerateRequest};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
// Method 1: Direct image generation
let image_req = ImageGenerateRequest::new("A beautiful sunset over mountains")
.with_size("1024x1024")
.with_quality("high");
let image_response = client.images.generate(image_req).await?;
if let Some(url) = &image_response.data[0].url {
println!("Generated image: {}", url);
}
// Method 2: AI-triggered image generation
let request = Request::builder()
.model(Model::GPT4oMini)
.input("Create an image of a robot learning to paint")
.tools(vec![Tool::image_generation()]) // Use the new built-in tool
.build();
let response = client.responses.create(request).await?;
// The AI will automatically call the image generation tool
Ok(())
}
Enable the stream feature:
[dependencies]
open-ai-rust-responses-by-sshift = { version = "0.4.3", features = ["stream"] }
use open_ai_rust_responses_by_sshift::{Client, Request, Model, StreamEvent};
use futures::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
let request = Request::builder()
.model(Model::GPT4oMini) // Excellent for streaming performance
.input("Tell me a story about a robot.")
.max_output_tokens(500) // Optimized for streaming
.build();
let mut stream = client.responses.stream(request);
while let Some(event) = stream.next().await {
match event? {
StreamEvent::TextDelta { content, .. } => {
print!("{}", content);
}
StreamEvent::Done => break,
_ => {}
}
}
Ok(())
}
Track response IDs during streaming for continuation requests:
use open_ai_rust_responses_by_sshift::{Client, Request, Model, StreamEvent};
use futures::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
let request = Request::builder()
.model(Model::GPT4oMini)
.input("Count from 1 to 5")
.max_output_tokens(500)
.build();
let mut stream = client.responses.stream(request);
let mut response_id: Option<String> = None;
while let Some(event) = stream.next().await {
match event? {
StreamEvent::ResponseCreated { id } => {
response_id = Some(id.clone());
println!("π Response ID: {id}");
}
StreamEvent::TextDelta { content, .. } => {
print!("{content}");
}
StreamEvent::ImageProgress { url, .. } => {
if let Some(url) = url {
println!("\nπΈ Image: {url}");
}
}
StreamEvent::ToolCallCreated { name, .. } => {
println!("\nπ§ Tool: {name}");
}
StreamEvent::Done => break,
_ => {}
}
}
// Use response_id for continuation if needed
if let Some(id) = response_id {
println!("\nResponse ID: {id}");
}
Ok(())
}
Use convenient helper methods for stream events:
while let Some(event) = stream.next().await {
let event = event?;
// Extract text content
if let Some(text) = event.as_text_delta() {
print!("{text}");
}
// Extract response ID
if let Some(id) = event.as_response_id() {
println!("Response ID: {id}");
}
// Extract image URL
if let Some(url) = event.as_image_progress() {
println!("Image: {url}");
}
// Check if done
if event.is_done() {
break;
}
}
use open_ai_rust_responses_by_sshift::Client;
use open_ai_rust_responses_by_sshift::files::FilePurpose;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
// Upload a file
let file = client.files
.upload_file("./data/document.pdf", FilePurpose::Assistants, None)
.await?;
println!("Uploaded file: {} ({})", file.filename, file.id);
// List files
let files = client.files.list(None).await?;
println!("You have {} files", files.len());
// Download file content
let content = client.files.download(&file.id).await?;
println!("Downloaded {} bytes", content.len());
Ok(())
}
The Responses API handles function calling differently from the Assistants API. There is no submit_tool_outputs endpoint. Instead, tool outputs are submitted as input items in a new request:
use open_ai_rust_responses_by_sshift::{Client, Request, Model, Tool, ToolChoice};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?;
// 1. Define function tools
let calculator_tool = Tool::function(
"calculate",
"Perform basic arithmetic calculations",
json!({
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Mathematical expression to evaluate"
}
},
"required": ["expression"]
}),
);
// 2. Initial request with tools
let request = Request::builder()
.model(Model::GPT4oMini) // Excellent for function calling
.input("Calculate 15 * 7 + 23")
.tools(vec![calculator_tool.clone()])
.tool_choice(ToolChoice::auto())
.build();
let response = client.responses.create(request).await?;
// 3. Check for tool calls and execute functions
let tool_calls = response.tool_calls();
if !tool_calls.is_empty() {
let mut function_outputs = Vec::new();
for tool_call in &tool_calls {
if tool_call.name == "calculate" {
// Execute your function here
let result = "128"; // Calculate 15 * 7 + 23 = 128
function_outputs.push((tool_call.call_id.clone(), result.to_string()));
}
}
// 4. Submit tool outputs by creating a new request
// This is the correct pattern for the Responses API
let continuation_request = Request::builder()
.model(Model::GPT4oMini)
.with_function_outputs(response.id(), function_outputs)
.tools(vec![calculator_tool])
.build();
let final_response = client.responses.create(continuation_request).await?;
println!("Final response: {}", final_response.output_text());
}
Ok(())
}
Key Points for Function Calling:
submit_tool_outputs endpoint (unlike Assistants API)with_function_outputs() to submit tool resultsprevious_response_id to maintain conversation contextcall_id from tool calls to function outputsSee examples/function_calling.rs for a complete working example.
# Required
OPENAI_API_KEY=sk-your-api-key
# Optional
OPENAI_BASE_URL=https://api.openai.com/v1 # Custom base URL
OPENAI_ORG_ID=org-your-organization-id # Organization ID
use open_ai_rust_responses_by_sshift::{Client, Config};
let config = Config::new("sk-your-api-key")
.with_base_url("https://api.openai.com/v1")
.with_organization_id("org-your-org-id");
let client = Client::new_with_config(config)?;
Check out the examples/ directory for comprehensive examples:
basic.rs - Simple request/responseconversation.rs - Multi-turn conversationsstreaming.rs - Real-time streamingfunction_calling.rs - Function calling and tool outputsimage_generation.rs - Image generation via direct API and AI toolsimage_input.rs - Image input / vision descriptioncomprehensive_demo.rs - Complete feature showcase (files, vector stores, tools, images, etc.)Create a .env file with your API key:
echo "OPENAI_API_KEY=sk-your-api-key-here" > .env
Run the comprehensive demo to see all features:
cargo run --example comprehensive_demo --features stream
cargo run --example code_interpreter
This demo showcases ALL major features:
Other examples:
cargo run --example basic
cargo run --example conversation
cargo run --example streaming --features stream
cargo run --example function_calling
cargo run --example image_generation # NEW: Image generation demo
This crate provides comprehensive coverage of the OpenAI Responses API:
| Feature | Status | Notes |
|---|---|---|
| Responses | β | Create, retrieve, cancel, delete, 21 new fields |
| Streaming | β | Server-sent events with futures::Stream |
| Conversation Continuity | β | Response ID linking, 100% success rate |
| Messages | β | Message CRUD operations |
| Files | β | Upload, download, list, delete |
| Vector Stores | β | Create, search, manage |
| Tools | β | Built-in and custom function calling |
| Image Generation | β | Direct API + AI function tools (hosted tool pending) |
| Image Input (Vision) | β | Describe user-supplied images |
| Phase 1 Spec | β | 85% May 2025 spec coverage |
The crate uses comprehensive error types:
use open_ai_rust_responses_by_sshift::{Client, Error};
match client.responses.create(request).await {
Ok(response) => println!("Success: {}", response.output_text()),
Err(Error::Api { message, error_type, code }) => {
eprintln!("API Error: {} ({})", message, error_type);
}
Err(Error::Http(e)) => {
eprintln!("HTTP Error: {}", e);
}
Err(Error::Json(e)) => {
eprintln!("JSON Error: {}", e);
}
Err(Error::Stream(msg)) => {
eprintln!("Stream Error: {}", msg);
}
}
Client is designed to be reused across requestsreqwest client pools connections automaticallyTo run the test suite:
# Run unit and integration tests
cargo test
# Run tests with all features
cargo test --all-features
# Run integration tests that need API key (streaming, actual API calls)
OPENAI_API_KEY=sk-your-key cargo test --features stream -- --ignored --nocapture
# Run the comprehensive demo (requires API key)
OPENAI_API_KEY=sk-your-key cargo run --example comprehensive_demo --features stream
The --nocapture flag is important for streaming tests because it allows you to see the real-time streaming output. The streaming test will show:
π Starting streaming test...
π Response: 1, 2, 3, 4, 5...
β
Stream completed!
π Test results:
Events received: 12
Content length: 45 characters
For detailed test coverage and results, see TEST_REPORT.md.
If you see errors like "Unknown include field", use the type-safe Include enum:
// β Don't use raw strings (may break with API updates)
.include_strings(vec!["file_search.results".to_string()])
// β
Use type-safe includes (recommended)
use open_ai_rust_responses_by_sshift::types::Include;
.include(vec![Include::FileSearchResults]) // Maps to file_search_call.results
Reasoning models (O4Mini, O3, O1 series) don't support temperature:
// β This will cause API errors
let request = Request::builder()
.model(Model::O4Mini)
.temperature(0.7) // Error: O4Mini doesn't support temperature
.build();
// β
Correct usage for reasoning models
let request = Request::builder()
.model(Model::O4Mini)
.reasoning(ReasoningParams::new().with_effort(Effort::Low))
.max_output_tokens(2000) // Reasoning needs more tokens
// No temperature parameter - built-in optimization
.build();
// β
For general models that support temperature
let request = Request::builder()
.model(Model::GPT4oMini) // Recommended default
.temperature(0.7) // GPT4oMini supports temperature
.max_output_tokens(500) // Optimized for general use
.build();
Fixed in v0.1.7 by optimizing token allocations:
// β Old defaults caused truncation (200 tokens)
// β
New optimized defaults:
Model::GPT4oMini => 500 tokens // General responses
Model::O4Mini => 2000 tokens // Reasoning tasks
// Success rate improved from 50% to 100%
Native hosted tool pending, use function tool bridge:
// β This doesn't work yet (pending OpenAI release)
Tool::image_generation(None) // Hosted tool not available
// β
Use the function tool bridge (working now)
Tool::image_generation_function() // Pre-made function tool
No! β
Tests marked ignored are intentional:
ignored = Integration tests that need API keys (expensive/slow)--ignored flag to run integration tests when you have an API keyMake sure to use both flags:
cargo test test_create_stream --features stream -- --ignored --nocapture
# ^^^^^^^^^ ^^^^^^^^^
# run ignored show output
# Check if set
echo $OPENAI_API_KEY
# Set for current session
export OPENAI_API_KEY=sk-your-api-key
# Or use .env file
echo "OPENAI_API_KEY=sk-your-api-key" > .env
Contributions are welcome! Please read our Contributing Guide for details.
This project is licensed under the MIT License - see the LICENSE file for details.
This SDK powers real-world applications and services. Here are some notable projects:
Nova is a sophisticated AI-powered Telegram bot ecosystem with deep blockchain integration on Aptos. Built using this SDK, Nova provides:
Nova demonstrates the SDK's capabilities in production, handling real-time AI interactions, tool calling, and streaming responses for Telegram users and groups.
Fully backward compatible - All changes are internal code quality improvements. No API changes or breaking changes.
#[must_use] attribute to stream() method (no functional change)#[allow])Fully backward compatible - All changes are additive. Existing code continues to work without modification.
ResponseCreated events now include response IDs for continuation requestsas_response_id(), is_done() for easier event processingFully backward compatible - All changes are additive. Existing code continues to work without modification.
with_bearer_token() method: New convenience method for Bearer token authentication
with_header("Authorization", "Bearer <token>") as beforeHttpTransport::new(url).with_bearer_token("your-token")?Bearer prefix formattingwith_header() usage continues to workFully backward compatible - All changes are additive. Existing code continues to work without modification.
ToolRegistry: New unified tool management system for combining local and MCP tools
McpClient directly as beforeToolRegistry from mcp module and register your toolsexamples/local_and_mcp_tools.rs for usage examplesLocalTool Trait: New trait for defining local Rust-based tools
ToolRegistryMCP Authorization Support: Added header support for secure MCP connections
HttpTransport::with_bearer_token() for Bearer token authenticationHttpTransport::with_header() for custom headersHttpTransport::new(url).with_bearer_token(token)?.with_header(key, value)?HttpTransport::new() continues to work without headersTool, TextConfig, ReasoningParams), add ..Default::default() or switch to the builders/constructors.match on Model, add arms for GPTβ5 variants or a wildcard.