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

FeatureOpenAIClient (Chat Completions)OpenAIResponsesClient (Responses)
Endpoint/v1/chat/completions/v1/responses
ModelsChat-compatible modelsCurrent GPT and reasoning models
Reasoning summariesNot availableNative support
Built-in toolsNot availableWeb search, file search, code interpreter
Server-side stateManual message historyprevious_response_id
Structured outputresponse_formattext.format (planned)
MaturityStable, widely adoptedNewer, 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 EffortDescription
NoneDisable reasoning for the lowest latency
MinimalLegacy minimal reasoning on models that support it
LowLow reasoning effort
MediumBalanced reasoning
HighHigh reasoning effort
XHighExtra-high reasoning effort
MaxMaximum 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 SummaryDescription
AutoModel decides whether to include a summary
ConciseBrief summary of reasoning
DetailedThorough 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

ModelTypeDescription
gpt-5.6-terraReasoningBalanced default for production agents
gpt-5.6-solReasoningFlagship reasoning and coding
gpt-5.6-lunaReasoningCost-efficient, high-volume workloads
gpt-5.6ReasoningFlagship alias
gpt-5ReasoningPrevious-generation compatibility
gpt-4.1 familyChatCompatibility and explicit sampling controls
o3 / o4-miniReasoningPrevious-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::Text with partial: true
  • Reasoning summary deltas arrive as Part::Thinking with partial: true
  • Function calls are emitted from the final ResponseCompleted event with correct names and arguments
  • The final event has turn_complete: true with 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 StatusError CategoryRetryable
401UnauthorizedNo
429RateLimitedYes
500, 502, 503, 504UnavailableYes
OtherInternalNo
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:

  1. Basic non-streaming chat
  2. Basic streaming chat
  3. Reasoning model with summary (o4-mini compatibility path)
  4. Tool calling with function tools
  5. Multi-turn conversation
  6. System instructions
  7. Temperature and generation config (gpt-4.1-nano compatibility path)

Additional Examples

Six standalone example crates demonstrate specific Responses API features:

ExampleRun CommandFeature
WebSocket transportcargo run --manifest-path examples/openai_ws_minimal/Cargo.tomlLow-latency persistent connection
Background modecargo run --manifest-path examples/openai_background/Cargo.tomlSubmit & poll workflow
Conversations APIcargo run --manifest-path examples/openai_conversations/Cargo.tomlServer-managed multi-turn
Built-in toolscargo run --manifest-path examples/openai_builtin_tools/Cargo.tomlImage gen, web search
Deep researchcargo run --manifest-path examples/openai_deep_research/Cargo.tomlAuto-background research
Open Responsescargo run --manifest-path examples/openai_open_responses/Cargo.tomlProvider-agnostic endpoints


Previous: ← Cloud Providers | Next: Ollama (Local) β†’