openai_ergonomic/responses/
chat.rs

1//! Chat completion response types and helpers.
2
3use openai_client_base::models::{
4    ChatCompletionMessageToolCallsInner, ChatCompletionResponseMessage,
5    CreateChatCompletionResponse, CreateChatCompletionResponseChoicesInner,
6};
7
8/// Extension trait for chat completion responses.
9pub trait ChatCompletionResponseExt {
10    /// Get the content of the first choice, if available.
11    fn content(&self) -> Option<&str>;
12
13    /// Get the tool calls from the first choice, if available.
14    fn tool_calls(&self) -> Vec<&ChatCompletionMessageToolCallsInner>;
15
16    /// Check if the response has tool calls.
17    fn has_tool_calls(&self) -> bool;
18
19    /// Get the first choice from the response.
20    fn first_choice(&self) -> Option<&CreateChatCompletionResponseChoicesInner>;
21
22    /// Get the message from the first choice.
23    fn first_message(&self) -> Option<&ChatCompletionResponseMessage>;
24
25    /// Check if the response was refused.
26    fn is_refusal(&self) -> bool;
27
28    /// Get the refusal message if the response was refused.
29    fn refusal(&self) -> Option<&str>;
30
31    /// Get the finish reason for the first choice.
32    fn finish_reason(&self) -> Option<String>;
33}
34
35impl ChatCompletionResponseExt for CreateChatCompletionResponse {
36    fn content(&self) -> Option<&str> {
37        self.choices
38            .first()
39            .and_then(|choice| choice.message.content.as_deref())
40    }
41
42    fn tool_calls(&self) -> Vec<&ChatCompletionMessageToolCallsInner> {
43        self.choices
44            .first()
45            .and_then(|choice| choice.message.tool_calls.as_ref())
46            .map(|calls| calls.iter().collect())
47            .unwrap_or_default()
48    }
49
50    fn has_tool_calls(&self) -> bool {
51        !self.tool_calls().is_empty()
52    }
53
54    fn first_choice(&self) -> Option<&CreateChatCompletionResponseChoicesInner> {
55        self.choices.first()
56    }
57
58    fn first_message(&self) -> Option<&ChatCompletionResponseMessage> {
59        self.first_choice().map(|choice| choice.message.as_ref())
60    }
61
62    fn is_refusal(&self) -> bool {
63        self.first_message()
64            .and_then(|msg| msg.refusal.as_ref())
65            .is_some()
66    }
67
68    fn refusal(&self) -> Option<&str> {
69        self.first_message()
70            .and_then(|msg| msg.refusal.as_ref())
71            .map(std::string::String::as_str)
72    }
73
74    fn finish_reason(&self) -> Option<String> {
75        use openai_client_base::models::create_chat_completion_response_choices_inner::FinishReason;
76        self.first_choice()
77            .map(|choice| match &choice.finish_reason {
78                FinishReason::Stop => "stop".to_string(),
79                FinishReason::Length => "length".to_string(),
80                FinishReason::ToolCalls => "tool_calls".to_string(),
81                FinishReason::ContentFilter => "content_filter".to_string(),
82                FinishReason::FunctionCall => "function_call".to_string(),
83            })
84    }
85}
86
87/// Extension trait for tool calls.
88pub trait ToolCallExt {
89    /// Get the function name from the tool call.
90    fn function_name(&self) -> &str;
91
92    /// Get the function arguments as a string.
93    fn function_arguments(&self) -> &str;
94
95    /// Parse the function arguments as JSON.
96    fn parse_arguments<T: serde::de::DeserializeOwned>(&self) -> Result<T, serde_json::Error>;
97}
98
99impl ToolCallExt for ChatCompletionMessageToolCallsInner {
100    fn function_name(&self) -> &str {
101        match self {
102            ChatCompletionMessageToolCallsInner::ChatCompletionMessageToolCall(tool_call) => {
103                &tool_call.function.name
104            }
105            ChatCompletionMessageToolCallsInner::ChatCompletionMessageCustomToolCall(_) => {
106                // Custom tool calls don't have function names in the same way
107                ""
108            }
109        }
110    }
111
112    fn function_arguments(&self) -> &str {
113        match self {
114            ChatCompletionMessageToolCallsInner::ChatCompletionMessageToolCall(tool_call) => {
115                &tool_call.function.arguments
116            }
117            ChatCompletionMessageToolCallsInner::ChatCompletionMessageCustomToolCall(_) => {
118                // Custom tool calls don't have function arguments in the same way
119                ""
120            }
121        }
122    }
123
124    fn parse_arguments<T: serde::de::DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
125        serde_json::from_str(self.function_arguments())
126    }
127}
128
129// Re-export types for convenience
130pub use openai_client_base::models::{
131    ChatCompletionResponseMessage as ChatMessage,
132    CreateChatCompletionResponse as ChatCompletionResponse,
133    CreateChatCompletionResponseChoicesInner as ChatChoice,
134};
135
136// Re-export the FunctionCall type with a more ergonomic alias
137pub use openai_client_base::models::{
138    ChatCompletionMessageToolCallFunction as FunctionCall,
139    ChatCompletionResponseMessageFunctionCall,
140};
141
142// Re-export ToolCall with an alias
143pub use openai_client_base::models::ChatCompletionMessageToolCallsInner as ToolCall;