openai_responses 0.1.0

Rust SDK for the OpenAI Responses API
Documentation
use chrono::{DateTime, Utc, serde::ts_seconds};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use crate::types::OutputContent;

use super::{InputItem, OutputItem, ReasoningConfig, TextConfig, Tool, ToolChoice, Truncation};

/// The Response object.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Response {
    /// When this Response was created.
    #[serde(with = "ts_seconds")]
    pub created_at: DateTime<Utc>,
    /// Unique identifier for this Response.
    pub id: String,
    /// Details about why the response is incomplete.
    pub incomplete_details: Option<IncompleteDetails>,
    /// Inserts a system (or developer) message as the first item in the model's context.
    /// When using along with `previous_response_id`, the instructions from a previous response will be not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses.
    pub instructions: Option<String>,
    /// An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](https://platform.openai.com/docs/guides/reasoning).
    pub max_output_tokens: Option<u64>,
    /// Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
    /// Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
    pub metadata: HashMap<String, String>,
    /// Model ID used to generate the response, like gpt-4o or o1. OpenAI offers a wide range of models with different capabilities, performance characteristics, and price points.
    /// Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare available models.
    pub model: String,
    /// An array of content items generated by the model.
    /// - The length and order of items in the `output` array is dependent on the model's response.
    /// - Rather than accessing the first item in the `output` array and assuming it's an assistant message with the content generated by the model, you might consider using the `output_text` function.
    pub output: Vec<OutputItem>,
    /// Whether to allow the model to run tool calls in parallel.
    pub parallel_tool_calls: bool,
    /// The unique ID of the previous response to the model. Use this to create multi-turn conversations.
    /// Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state).
    pub previous_response_id: Option<String>,
    /// Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning).
    /// Only available for o-series models.
    pub reasoning: ReasoningConfig,
    /// The status of the response generation.
    pub status: ResponseStatus,
    /// What sampling temperature to use, between 0 and 2.
    /// Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
    /// We generally recommend altering this or `top_p` but not both.
    pub temperature: f32,
    /// Configuration options for a text response from the model. Can be plain text or structured JSON data. Learn more:
    /// - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
    /// - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
    pub text: TextConfig,
    /// How the model should select which tool (or tools) to use when generating a response.
    /// See the `tools` parameter to see how to specify which tools the model can call.
    pub tool_choice: ToolChoice,
    /// An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter.
    /// The two categories of tools you can provide the model are:
    /// - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like [web search](https://platform.openai.com/docs/guides/tools-web-search) or [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools).
    /// - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code. Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
    pub tools: Vec<Tool>,
    /// An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.
    /// We generally recommend altering this or `temperature` but not both.
    pub top_p: f32,
    /// The truncation strategy to use for the model response.
    pub truncation: Truncation,
    /// Represents token usage details including input tokens, output tokens, a breakdown of output tokens, and the total tokens used.
    pub usage: Usage,
    /// Whether the response was stored on OpenAI's server for later retrieval.
    pub store: bool,
    /// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids).
    pub user: Option<String>,
}

impl Response {
    #[must_use]
    pub fn output_text(&self) -> String {
        self.output
            .iter()
            .filter_map(|output| match output {
                OutputItem::Message(message) => Some(message),
                _ => None,
            })
            .flat_map(|message| &message.content)
            .map(|content| match content {
                OutputContent::Text { text, .. } => text.to_owned(),
                OutputContent::Refusal { refusal } => refusal.to_owned(),
            })
            .collect::<String>()
    }
}

/// Represents token usage details including input tokens, output tokens, a breakdown of output tokens, and the total tokens used.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Usage {
    /// The number of input tokens.
    pub input_tokens: u64,
    /// A detailed breakdown of the input tokens.
    pub input_tokens_details: InputTokensDetails,
    /// The number of output tokens.
    pub output_tokens: u64,
    /// A detailed breakdown of the output tokens.
    pub output_tokens_details: OutputTokensDetails,
    /// The total number of tokens used.
    pub total_tokens: u64,
}

/// A detailed breakdown of the input tokens.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InputTokensDetails {
    /// The number of cached tokens.
    pub cached_tokens: u64,
}

/// A detailed breakdown of the output tokens.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputTokensDetails {
    /// The number of reasoning tokens.
    pub reasoning_tokens: u64,
}

/// Details about why a response is incomplete.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IncompleteDetails {
    /// The reason why the response is incomplete.
    pub reason: String,
}

/// The status of the response generation.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ResponseStatus {
    Failed,
    Completed,
    Incomplete,
    InProgress,
}

/// An error object returned when the model fails to generate a Response.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Error {
    /// The type of error.
    pub r#type: String,
    /// A human-readable description of the error.
    pub message: String,
    /// The error code for the response.
    pub code: Option<String>,
    /// The parameter that caused the error.
    pub param: Option<String>,
}

/// A list of items used to generate a model response.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InputItemList {
    /// A list of items used to generate this response.
    data: Vec<InputItem>,
    /// The ID of the first item in the list.
    first_id: String,
    /// The ID of the last item in the list.
    last_id: String,
    /// Whether there are more items available.
    has_more: bool,
}

#[allow(clippy::redundant_pub_crate)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum ResponseResult {
    Ok(Box<Response>),
    Err { error: Error },
}

impl From<ResponseResult> for Result<Response, Error> {
    fn from(val: ResponseResult) -> Self {
        match val {
            ResponseResult::Err { error } => Err(error),
            ResponseResult::Ok(response) => Ok(*response),
        }
    }
}