use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReasoningEffort {
None,
Minimal,
Low,
Medium,
High,
Xhigh,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Reasoning {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub effort: Option<ReasoningEffort>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
}
impl Reasoning {
pub fn effort(effort: ReasoningEffort) -> Self {
Self {
effort: Some(effort),
summary: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ResponseFormatTextConfig {
Text,
JsonObject,
JsonSchema {
name: String,
schema: serde_json::Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
strict: Option<bool>,
},
}
impl ResponseFormatTextConfig {
pub fn json_schema(name: impl Into<String>, schema: serde_json::Value, strict: bool) -> Self {
Self::JsonSchema {
name: name.into(),
schema,
description: None,
strict: Some(strict),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResponseTextConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub format: Option<ResponseFormatTextConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub verbosity: Option<String>,
}
impl ResponseTextConfig {
pub fn format(format: ResponseFormatTextConfig) -> Self {
Self {
format: Some(format),
verbosity: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ResponseIncludable {
#[serde(rename = "file_search_call.results")]
FileSearchCallResults,
#[serde(rename = "web_search_call.results")]
WebSearchCallResults,
#[serde(rename = "web_search_call.action.sources")]
WebSearchCallActionSources,
#[serde(rename = "message.input_image.image_url")]
MessageInputImageImageUrl,
#[serde(rename = "computer_call_output.output.image_url")]
ComputerCallOutputOutputImageUrl,
#[serde(rename = "code_interpreter_call.outputs")]
CodeInterpreterCallOutputs,
#[serde(rename = "reasoning.encrypted_content")]
ReasoningEncryptedContent,
#[serde(rename = "message.output_text.logprobs")]
MessageOutputTextLogprobs,
}
impl ResponseIncludable {
pub fn as_str(&self) -> &'static str {
match self {
Self::FileSearchCallResults => "file_search_call.results",
Self::WebSearchCallResults => "web_search_call.results",
Self::WebSearchCallActionSources => "web_search_call.action.sources",
Self::MessageInputImageImageUrl => "message.input_image.image_url",
Self::ComputerCallOutputOutputImageUrl => "computer_call_output.output.image_url",
Self::CodeInterpreterCallOutputs => "code_interpreter_call.outputs",
Self::ReasoningEncryptedContent => "reasoning.encrypted_content",
Self::MessageOutputTextLogprobs => "message.output_text.logprobs",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Truncation {
Auto,
Disabled,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reasoning_serializes() {
let reasoning = Reasoning::effort(ReasoningEffort::Medium);
assert_eq!(
serde_json::to_value(&reasoning).unwrap(),
serde_json::json!({"effort": "medium"})
);
}
#[test]
fn text_json_schema_serializes() {
let text = ResponseTextConfig::format(ResponseFormatTextConfig::json_schema(
"out",
serde_json::json!({"type": "object"}),
true,
));
assert_eq!(
serde_json::to_value(&text).unwrap(),
serde_json::json!({
"format": {
"type": "json_schema",
"name": "out",
"schema": {"type": "object"},
"strict": true
}
})
);
}
#[test]
fn includable_uses_dotted_wire_values() {
assert_eq!(
serde_json::to_value(ResponseIncludable::FileSearchCallResults).unwrap(),
"file_search_call.results"
);
}
}