use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ItemType {
Message,
FunctionCall,
FunctionCallOutput,
Reasoning,
ItemReference,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MessageRole {
User,
Assistant,
System,
Developer,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ItemStatus {
InProgress,
Completed,
Failed,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FunctionCallStatus {
InProgress,
Completed,
Failed,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InputTextContent {
#[serde(rename = "type")]
pub content_type: String, pub text: String,
}
impl InputTextContent {
pub fn new(text: impl Into<String>) -> Self {
Self {
content_type: "input_text".to_string(),
text: text.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputTextContent {
#[serde(rename = "type")]
pub content_type: String, pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub annotations: Option<Vec<Annotation>>,
}
impl OutputTextContent {
pub fn new(text: impl Into<String>) -> Self {
Self {
content_type: "output_text".to_string(),
text: text.into(),
annotations: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefusalContent {
#[serde(rename = "type")]
pub content_type: String, pub refusal: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ImageDetail {
Low,
High,
Auto,
Original,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InputImageContent {
#[serde(rename = "type")]
pub content_type: String, #[serde(skip_serializing_if = "Option::is_none")]
pub image_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<ImageDetail>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InputFileContent {
#[serde(rename = "type")]
pub content_type: String, #[serde(skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Annotation {
#[serde(rename = "url_citation")]
UrlCitation {
start_index: usize,
end_index: usize,
url: String,
#[serde(skip_serializing_if = "Option::is_none")]
title: Option<String>,
},
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum InputContent {
Text(InputTextContent),
Image(InputImageContent),
File(InputFileContent),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OutputContent {
Text(OutputTextContent),
Refusal(RefusalContent),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserMessageItemParam {
#[serde(rename = "type")]
pub item_type: String, pub role: String, pub content: Vec<InputContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
}
impl UserMessageItemParam {
pub fn new(text: impl Into<String>) -> Self {
Self {
item_type: "message".to_string(),
role: "user".to_string(),
content: vec![InputContent::Text(InputTextContent::new(text))],
id: None,
status: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemMessageItemParam {
#[serde(rename = "type")]
pub item_type: String, pub role: String, pub content: Vec<InputTextContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
}
impl SystemMessageItemParam {
pub fn new(text: impl Into<String>) -> Self {
Self {
item_type: "message".to_string(),
role: "system".to_string(),
content: vec![InputTextContent::new(text)],
id: None,
status: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeveloperMessageItemParam {
#[serde(rename = "type")]
pub item_type: String, pub role: String, pub content: Vec<InputTextContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantMessageItemParam {
#[serde(rename = "type")]
pub item_type: String, pub role: String, pub content: Vec<OutputContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
}
impl AssistantMessageItemParam {
pub fn new(text: impl Into<String>) -> Self {
Self {
item_type: "message".to_string(),
role: "assistant".to_string(),
content: vec![OutputContent::Text(OutputTextContent::new(text))],
id: None,
status: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCallItemParam {
#[serde(rename = "type")]
pub item_type: String, pub id: String,
pub call_id: String,
pub name: String,
pub arguments: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<FunctionCallStatus>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCallOutputItemParam {
#[serde(rename = "type")]
pub item_type: String, pub call_id: String,
pub output: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<FunctionCallStatus>,
}
impl FunctionCallOutputItemParam {
pub fn new(call_id: impl Into<String>, output: impl Into<String>) -> Self {
Self {
item_type: "function_call_output".to_string(),
call_id: call_id.into(),
output: output.into(),
id: None,
status: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReasoningSummaryContent {
#[serde(rename = "type")]
pub content_type: String, pub text: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReasoningItemParam {
#[serde(rename = "type")]
pub item_type: String, pub summary: Vec<ReasoningSummaryContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub encrypted_content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<ReasoningUsage>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReasoningUsage {
#[serde(skip_serializing_if = "Option::is_none")]
pub input_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_tokens: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ItemReferenceParam {
#[serde(rename = "type")]
pub item_type: String, pub id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ItemParam {
UserMessage(UserMessageItemParam),
SystemMessage(SystemMessageItemParam),
DeveloperMessage(DeveloperMessageItemParam),
AssistantMessage(AssistantMessageItemParam),
FunctionCall(FunctionCallItemParam),
FunctionCallOutput(FunctionCallOutputItemParam),
Reasoning(ReasoningItemParam),
ItemReference(ItemReferenceParam),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionToolParam {
#[serde(rename = "type")]
pub tool_type: String, pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub strict: Option<bool>,
}
impl FunctionToolParam {
pub fn new(name: impl Into<String>) -> Self {
Self {
tool_type: "function".to_string(),
name: name.into(),
description: None,
parameters: None,
strict: None,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn with_parameters(mut self, parameters: Value) -> Self {
self.parameters = Some(parameters);
self
}
pub fn with_strict(mut self, strict: bool) -> Self {
self.strict = Some(strict);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ToolChoiceValue {
Auto,
None,
Required,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpecificFunctionChoice {
#[serde(rename = "type")]
pub choice_type: String, pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ToolChoiceParam {
Value(ToolChoiceValue),
Specific(SpecificFunctionChoice),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IncompleteDetails {
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseError {
pub code: String,
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ResponseStatus {
Queued,
#[default]
InProgress,
Completed,
Failed,
Cancelled,
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpenResponsesResponse {
pub id: String,
pub object: String, #[serde(default)]
pub status: ResponseStatus,
pub output: Vec<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<ResponseUsage>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<ResponseError>,
#[serde(skip_serializing_if = "Option::is_none")]
pub incomplete_details: Option<IncompleteDetails>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseUsage {
pub input_tokens: u64,
pub output_tokens: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_tokens_details: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output_tokens_details: Option<Value>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_user_message_serialization() {
let msg = UserMessageItemParam::new("Hello, world!");
let json = serde_json::to_value(&msg).unwrap();
assert_eq!(json["type"], "message");
assert_eq!(json["role"], "user");
assert_eq!(json["content"][0]["type"], "input_text");
assert_eq!(json["content"][0]["text"], "Hello, world!");
}
#[test]
fn test_function_tool_param() {
let tool = FunctionToolParam::new("get_weather")
.with_description("Get the current weather")
.with_parameters(json!({
"type": "object",
"properties": {
"location": {"type": "string"}
}
}));
let json = serde_json::to_value(&tool).unwrap();
assert_eq!(json["type"], "function");
assert_eq!(json["name"], "get_weather");
assert!(json["description"].is_string());
}
#[test]
fn test_function_call_output() {
let output = FunctionCallOutputItemParam::new("call_123", r#"{"temperature": 72}"#);
let json = serde_json::to_value(&output).unwrap();
assert_eq!(json["type"], "function_call_output");
assert_eq!(json["call_id"], "call_123");
}
}