OpenAI Responses API
ADK-Rust provides a dedicated client for OpenAI's Responses API (/v1/responses endpoint) β the successor to the Chat Completions API. The Responses API is the recommended way to use current GPT-5.6 models, including their full reasoning-effort range.
Overview
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β OpenAI Responses API Client β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Endpoint: POST /v1/responses β
β Client: OpenAIResponsesClient β
β Config: OpenAIResponsesConfig β
β Feature: openai β
β β
β Capabilities: β
β β’ Streaming and non-streaming β
β β’ Reasoning summaries β
β β’ Tool / function calling β
β β’ Multi-turn via previous_response_id β
β β’ Built-in tools (web search, file search, code interpreter) β
β β’ System instructions β
β β’ Model-aware sampling controls and max_output_tokens β
β β’ Automatic retry with exponential backoff β
β β
β vs Chat Completions (OpenAIClient): β
β β’ Stateful conversations (server-side context) β
β β’ Native reasoning summaries β
β β’ Built-in tool hosting β
β β’ Simpler multi-turn (no manual message history) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
When to Use Which Client
| Feature | OpenAIClient (Chat Completions) | OpenAIResponsesClient (Responses) |
|---|---|---|
| Endpoint | /v1/chat/completions | /v1/responses |
| Models | Chat-compatible models | Current GPT and reasoning models |
| Reasoning summaries | Not available | Native support |
| Built-in tools | Not available | Web search, file search, code interpreter |
| Server-side state | Manual message history | previous_response_id |
| Structured output | response_format | text.format (planned) |
| Maturity | Stable, widely adopted | Newer, recommended by OpenAI |
Use OpenAIResponsesClient when you need reasoning models with summaries, built-in tools, or want to use OpenAI's latest API. Use OpenAIClient for backward compatibility with existing Chat Completions workflows.
Installation
[dependencies]
adk-rust = { version = "2.1.0", features = ["openai"] }
adk-tool = "2.1.0"
Or with adk-model directly:
[dependencies]
adk-model = { version = "2.1.0", features = ["openai"] }
Set your API key:
export OPENAI_API_KEY="sk-..."
Quick Start
use adk_rust::prelude::*;
use adk_rust::session::{CreateRequest, SessionService};
use adk_rust::futures::StreamExt;
use adk_model::openai::{OpenAIResponsesClient, OpenAIResponsesConfig};
use std::collections::HashMap;
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenvy::dotenv().ok();
let api_key = std::env::var("OPENAI_API_KEY")?;
// 1. Create the Responses API client
let config = OpenAIResponsesConfig::new(&api_key, "gpt-5.6-terra");
let model = Arc::new(OpenAIResponsesClient::new(config)?);
// 2. Build an agent
let agent = Arc::new(
LlmAgentBuilder::new("assistant")
.instruction("You are a helpful assistant. Be concise.")
.model(model)
.build()?,
);
// 3. Create a session
let sessions: Arc<dyn SessionService> = Arc::new(InMemorySessionService::new());
sessions.create(CreateRequest {
app_name: "my_app".into(),
user_id: "user".into(),
session_id: Some("s1".into()),
state: HashMap::new(),
}).await?;
// 4. Run through the Runner
let runner = Runner::builder()
.app_name("my_app")
.agent(agent)
.session_service(sessions)
.build()?;
let message = Content::new("user").with_text("What is the capital of France?");
let mut stream = runner.run(
adk_rust::UserId::new("user")?,
adk_rust::SessionId::new("s1")?,
message,
).await?;
while let Some(event) = stream.next().await {
let event = event?;
if let Some(content) = &event.llm_response.content {
for part in &content.parts {
if let Some(text) = part.text() {
print!("{text}");
}
}
}
}
println!();
Ok(())
}
Configuration
Basic Configuration
use adk_model::openai::OpenAIResponsesConfig;
// Minimal β just API key and model
let config = OpenAIResponsesConfig::new("sk-...", "gpt-5.6-luna");
// With organization and project
let config = OpenAIResponsesConfig::new("sk-...", "gpt-5.6-terra")
.with_organization("org-...")
.with_project("proj-...");
// Custom base URL (for proxies or compatible APIs)
let config = OpenAIResponsesConfig::new("sk-...", "gpt-5.6-terra")
.with_base_url("https://my-proxy.example.com/v1");
Reasoning Models
For GPT-5.6 reasoning models, configure reasoning effort and summary:
use adk_model::openai::{
OpenAIReasoningEffort, OpenAIResponsesClient,
OpenAIResponsesConfig, ReasoningSummary,
};
let config = OpenAIResponsesConfig::new("sk-...", "gpt-5.6-terra")
.with_reasoning_summary(ReasoningSummary::Detailed);
let model = OpenAIResponsesClient::new_with_reasoning_effort(
config,
OpenAIReasoningEffort::Max,
)?;
| Reasoning Effort | Description |
|---|---|
None | Disable reasoning for the lowest latency |
Minimal | Legacy minimal reasoning on models that support it |
Low | Low reasoning effort |
Medium | Balanced reasoning |
High | High reasoning effort |
XHigh | Extra-high reasoning effort |
Max | Maximum reasoning on supported models |
GPT-5.6 supports None, Low, Medium, High, XHigh, and Max through the
Responses API. Chat Completions supports up to XHigh.
| Reasoning Summary | Description |
|---|---|
Auto | Model decides whether to include a summary |
Concise | Brief summary of reasoning |
Detailed | Thorough summary of reasoning |
Reasoning summaries appear as Part::Thinking in the response stream, letting you show the model's thought process to users.
Retry Configuration
use adk_model::retry::RetryConfig;
let client = OpenAIResponsesClient::new(config)?
.with_retry_config(RetryConfig {
max_retries: 3,
..Default::default()
});
Retries are automatic for rate limits (429), server errors (500/502/503/504), and network failures.
Available Models
| Model | Type | Description |
|---|---|---|
gpt-5.6-terra | Reasoning | Balanced default for production agents |
gpt-5.6-sol | Reasoning | Flagship reasoning and coding |
gpt-5.6-luna | Reasoning | Cost-efficient, high-volume workloads |
gpt-5.6 | Reasoning | Flagship alias |
gpt-5 | Reasoning | Previous-generation compatibility |
gpt-4.1 family | Chat | Compatibility and explicit sampling controls |
o3 / o4-mini | Reasoning | Previous-generation reasoning compatibility |
Features
Tool Calling
Function tools work the same way as with OpenAIClient β define tools on the agent and the runner handles the tool call loop:
use adk_rust::prelude::*;
use adk_model::openai::{OpenAIResponsesClient, OpenAIResponsesConfig};
use adk_tool::FunctionTool;
use std::sync::Arc;
async fn get_weather(
_ctx: Arc<dyn ToolContext>,
args: serde_json::Value,
) -> Result<serde_json::Value> {
let city = args["city"].as_str().unwrap_or("unknown");
Ok(serde_json::json!({
"city": city,
"temperature_f": 72,
"conditions": "Sunny"
}))
}
let weather_tool = FunctionTool::new(
"get_weather",
"Get current weather for a city. Requires a 'city' string parameter.",
get_weather,
);
let config = OpenAIResponsesConfig::new(&api_key, "gpt-5.6-terra");
let model = Arc::new(OpenAIResponsesClient::new(config)?);
let agent = LlmAgentBuilder::new("weather_agent")
.instruction("Use the get_weather tool to answer weather questions.")
.model(model)
.tool(Arc::new(weather_tool))
.build()?;
Multi-Turn Conversations
The Runner automatically manages conversation history through sessions. Each turn's context is preserved:
// Turn 1
let msg1 = Content::new("user").with_text("My name is Alice.");
let mut stream = runner.run(uid.clone(), sid.clone(), msg1).await?;
// ... consume stream ...
// Turn 2 β the model remembers the previous turn
let msg2 = Content::new("user").with_text("What is my name?");
let mut stream = runner.run(uid.clone(), sid.clone(), msg2).await?;
// Response: "Your name is Alice."
Per-Request Reasoning Override
Override reasoning settings per-request using LlmRequest extensions:
use adk_rust::prelude::*;
let agent = LlmAgentBuilder::new("flexible_reasoner")
.model(model)
.generate_content_config(GenerateContentConfig {
extensions: {
let mut ext = std::collections::HashMap::new();
ext.insert("openai".to_string(), serde_json::json!({
"reasoning": {
"effort": "high",
"summary": "detailed"
}
}));
ext
},
..Default::default()
})
.build()?;
Built-In Tools
The Responses API supports OpenAI-hosted tools. Prefer the typed wrappers from adk-tool:
use adk_tool::OpenAIWebSearchTool;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("researcher")
.model(model)
.tool(Arc::new(OpenAIWebSearchTool::new().preview()))
.build()?;
Available wrappers include OpenAIWebSearchTool, OpenAIFileSearchTool, OpenAICodeInterpreterTool, OpenAIImageGenerationTool, OpenAIComputerUseTool, OpenAIMcpTool, OpenAILocalShellTool, OpenAIShellTool, and OpenAIApplyPatchTool.
Previous Response ID
For server-side conversation state (bypassing local session history), pass previous_response_id:
let agent = LlmAgentBuilder::new("stateful")
.model(model)
.generate_content_config(GenerateContentConfig {
extensions: {
let mut ext = std::collections::HashMap::new();
ext.insert("openai".to_string(), serde_json::json!({
"previous_response_id": "resp_abc123"
}));
ext
},
..Default::default()
})
.build()?;
Streaming Behavior
The Responses API client streams text and reasoning deltas in real-time:
- Text deltas arrive as
Part::Textwithpartial: true - Reasoning summary deltas arrive as
Part::Thinkingwithpartial: true - Function calls are emitted from the final
ResponseCompletedevent with correct names and arguments - The final event has
turn_complete: truewith usage metadata and finish reason
This means you see text appearing token-by-token while the model generates, and function calls arrive as complete objects ready for execution.
Provider Metadata
Every response includes provider metadata with the response_id:
if let Some(meta) = &response.provider_metadata {
let response_id = meta["openai"]["response_id"].as_str();
// Use for previous_response_id, logging, debugging
}
Additional metadata may include:
encrypted_contentβ from reasoning models (for context preservation)built_in_tool_outputsβ results from web search, file search, code interpreter
Error Handling
Errors are mapped to structured AdkError with appropriate categories:
| HTTP Status | Error Category | Retryable |
|---|---|---|
| 401 | Unauthorized | No |
| 429 | RateLimited | Yes |
| 500, 502, 503, 504 | Unavailable | Yes |
| Other | Internal | No |
match runner.run(uid, sid, message).await {
Ok(stream) => { /* process stream */ }
Err(e) if e.is_retryable() => { /* retry logic */ }
Err(e) if e.is_unauthorized() => { /* check API key */ }
Err(e) => { /* handle other errors */ }
}
Background Mode & Cancellation
For long-running requests, submit with background: true and poll for completion:
use adk_model::openai::{OpenAIResponsesClient, OpenAIResponsesConfig};
let client = OpenAIResponsesClient::new(config)?;
// Submit with background: true via extensions
let mut gen_config = GenerateContentConfig::default();
gen_config.extensions.insert("openai".into(), serde_json::json!({ "background": true }));
// ... send request, extract response_id from provider_metadata ...
// Poll until terminal status
let response = client.poll_response("resp_abc123").await?;
// Check provider_metadata["openai"]["status"]: "completed", "in_progress", "failed", "cancelled"
// Cancel a running background response
let cancelled = client.cancel_response("resp_abc123").await?;
Deep research models (o3-deep-research, o4-mini-deep-research) automatically enable background mode without explicit background: true.
Example
A complete 7-scenario example is available at examples/openai_responses/:
export OPENAI_API_KEY=sk-...
cargo run --manifest-path examples/openai_responses/Cargo.toml
Scenarios covered:
- Basic non-streaming chat
- Basic streaming chat
- Reasoning model with summary (
o4-minicompatibility path) - Tool calling with function tools
- Multi-turn conversation
- System instructions
- Temperature and generation config (
gpt-4.1-nanocompatibility path)
Additional Examples
Six standalone example crates demonstrate specific Responses API features:
| Example | Run Command | Feature |
|---|---|---|
| WebSocket transport | cargo run --manifest-path examples/openai_ws_minimal/Cargo.toml | Low-latency persistent connection |
| Background mode | cargo run --manifest-path examples/openai_background/Cargo.toml | Submit & poll workflow |
| Conversations API | cargo run --manifest-path examples/openai_conversations/Cargo.toml | Server-managed multi-turn |
| Built-in tools | cargo run --manifest-path examples/openai_builtin_tools/Cargo.toml | Image gen, web search |
| Deep research | cargo run --manifest-path examples/openai_deep_research/Cargo.toml | Auto-background research |
| Open Responses | cargo run --manifest-path examples/openai_open_responses/Cargo.toml | Provider-agnostic endpoints |
Related
- Cloud Model Providers β All supported LLM providers
- Ollama (Local) β Run models locally
- LlmAgent β Using models with agents
- Function Tools β Adding tools to agents
Previous: β Cloud Providers | Next: Ollama (Local) β