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")]
content_type: String, text: String,
}
impl InputTextContent {
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")]
content_type: String, text: String,
#[serde(skip_serializing_if = "Option::is_none")]
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")]
content_type: String, 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")]
content_type: String, #[serde(skip_serializing_if = "Option::is_none")]
image_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
detail: Option<ImageDetail>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InputFileContent {
#[serde(rename = "type")]
content_type: String, #[serde(skip_serializing_if = "Option::is_none")]
filename: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
file_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
file_data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
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")]
item_type: String, role: String, content: Vec<InputContent>,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<String>,
}
impl UserMessageItemParam {
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")]
item_type: String, role: String, content: Vec<InputTextContent>,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
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")]
item_type: String, role: String, content: Vec<InputTextContent>,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantMessageItemParam {
#[serde(rename = "type")]
item_type: String, role: String, content: Vec<OutputContent>,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
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")]
item_type: String, id: String,
call_id: String,
name: String,
arguments: String,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<FunctionCallStatus>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCallOutputItemParam {
#[serde(rename = "type")]
item_type: String, call_id: String,
output: String,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<FunctionCallStatus>,
}
impl FunctionCallOutputItemParam {
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")]
content_type: String, text: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReasoningItemParam {
#[serde(rename = "type")]
item_type: String, summary: Vec<ReasoningSummaryContent>,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
encrypted_content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
usage: Option<ReasoningUsage>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReasoningUsage {
#[serde(skip_serializing_if = "Option::is_none")]
input_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
output_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
total_tokens: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ItemReferenceParam {
#[serde(rename = "type")]
item_type: String, 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")]
tool_type: String, name: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
parameters: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
strict: Option<bool>,
}
impl FunctionToolParam {
fn new(name: impl Into<String>) -> Self {
Self {
tool_type: "function".to_string(),
name: name.into(),
description: None,
parameters: None,
strict: None,
}
}
fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
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")]
choice_type: String, 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")]
reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseError {
code: String,
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 {
id: String,
object: String, #[serde(default)]
status: ResponseStatus,
output: Vec<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
usage: Option<ResponseUsage>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<ResponseError>,
#[serde(skip_serializing_if = "Option::is_none")]
incomplete_details: Option<IncompleteDetails>,
#[serde(skip_serializing_if = "Option::is_none")]
model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
created_at: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseUsage {
input_tokens: u64,
output_tokens: u64,
#[serde(skip_serializing_if = "Option::is_none")]
input_tokens_details: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
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");
}
}