| Crates.io | open-ai-rust-responses-by-sshift |
| lib.rs | open-ai-rust-responses-by-sshift |
| version | 0.3.1 |
| created_at | 2025-05-24 20:10:16.222401+00 |
| updated_at | 2025-09-03 13:19:09.51576+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 | 640,954 |
π 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 matching
Model. π‘οΈ 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:
Revolutionary error handling: SDK automatically detects and recovers from expired containers without breaking user flow!
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(())
}
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());
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?;
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.3.0"
tokio = { version = "1.0", features = ["full"] }
# Optional: Enable streaming
# open-ai-rust-responses-by-sshift = { version = "0.3.0", 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.3.0", 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(())
}
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.
Tool, TextConfig, ReasoningParams), add ..Default::default() or switch to the builders/constructors.match on Model, add arms for GPTβ5 variants or a wildcard.