1use super::shared::{FinishReason, ReasoningEffort, StopToken, Usage, WebSearchContextSize};
2use derive_builder::Builder;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::HashMap;
6use std::fmt::Display;
7
8#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
9pub struct ChatCompletionResponse {
10 #[serde(skip_serializing_if = "Option::is_none")]
12 pub id: Option<String>,
13 pub choices: Vec<ChatCompletionChoice>,
15 #[serde(default = "crate::shared::default_created")]
17 pub created: u32,
18 #[serde(default = "default_model")]
20 pub model: String,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub service_tier: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub system_fingerprint: Option<String>,
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub object: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub usage: Option<Usage>,
33}
34
35#[cfg(feature = "stream")]
36#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
37pub struct ChatCompletionChunkResponse {
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub id: Option<String>,
41 pub choices: Vec<ChatCompletionChunkChoice>,
43 #[serde(default = "crate::shared::default_created")]
45 pub created: u32,
46 #[serde(default = "default_model")]
48 pub model: String,
49 #[serde(skip_serializing_if = "Option::is_none")]
52 pub system_fingerprint: Option<String>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub object: Option<String>,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub usage: Option<Usage>,
59}
60
61fn default_model() -> String {
62 "".to_string()
63}
64
65#[derive(Serialize, Deserialize, Debug, Default, Builder, Clone, PartialEq)]
66#[builder(name = "ChatCompletionParametersBuilder")]
67#[builder(setter(into, strip_option), default)]
68pub struct ChatCompletionParameters {
69 pub messages: Vec<ChatMessage>,
71 pub model: String,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub store: Option<bool>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub reasoning_effort: Option<ReasoningEffort>,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub metadata: Option<HashMap<String, String>>,
82 #[serde(skip_serializing_if = "Option::is_none")]
85 pub frequency_penalty: Option<f32>,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub logit_bias: Option<HashMap<String, i32>>,
89 #[serde(skip_serializing_if = "Option::is_none")]
92 pub logprobs: Option<bool>,
93 #[serde(skip_serializing_if = "Option::is_none")]
96 pub top_logprobs: Option<u32>,
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub max_tokens: Option<u32>,
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub max_completion_tokens: Option<u32>,
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub n: Option<u32>,
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub modalities: Option<Vec<Modality>>,
109 #[serde(skip_serializing_if = "Option::is_none")]
112 pub prediction: Option<PredictedOutput>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub audio: Option<AudioParameters>,
116 #[serde(skip_serializing_if = "Option::is_none")]
119 pub presence_penalty: Option<f32>,
120 #[serde(skip_serializing_if = "Option::is_none")]
125 pub response_format: Option<ChatCompletionResponseFormat>,
126 #[serde(skip_serializing_if = "Option::is_none")]
131 pub seed: Option<u32>,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub stop: Option<StopToken>,
135 #[serde(skip_serializing_if = "Option::is_none")]
138 pub stream: Option<bool>,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub stream_options: Option<ChatCompletionStreamOptions>,
142 #[serde(skip_serializing_if = "Option::is_none")]
145 pub temperature: Option<f32>,
146 #[serde(skip_serializing_if = "Option::is_none")]
149 pub top_p: Option<f32>,
150 #[serde(skip_serializing_if = "Option::is_none")]
153 pub tools: Option<Vec<ChatCompletionTool>>,
154 #[serde(skip_serializing_if = "Option::is_none")]
159 pub tool_choice: Option<ChatCompletionToolChoice>,
160 #[serde(skip_serializing_if = "Option::is_none")]
162 pub parallel_tool_calls: Option<bool>,
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub safety_identifier: Option<String>,
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub prompt_cache_key: Option<String>,
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub web_search_options: Option<WebSearchOptions>,
172 #[serde(flatten)]
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub extra_body: Option<Value>,
176 #[serde(skip_serializing_if = "Option::is_none")]
179 pub query_params: Option<HashMap<String, String>>,
180}
181
182#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
183pub struct ChatCompletionStreamOptions {
184 pub include_usage: Option<bool>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub continuous_usage_stats: Option<bool>,
188}
189
190#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
191pub struct ChatCompletionToolChoiceFunction {
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub r#type: Option<ChatCompletionToolType>,
195 pub function: ChatCompletionToolChoiceFunctionName,
197}
198
199#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
200pub struct ChatCompletionToolChoiceFunctionName {
201 pub name: String,
203}
204
205#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
206pub struct ChatCompletionFunction {
207 pub name: String,
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub description: Option<String>,
212 pub parameters: serde_json::Value,
214}
215
216#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
217#[serde(tag = "type", rename_all = "snake_case")]
218pub enum ChatCompletionResponseFormat {
219 Text,
220 JsonObject,
221 JsonSchema { json_schema: JsonSchema },
222}
223
224#[derive(Serialize, Deserialize, Debug, Default, Builder, Clone, PartialEq)]
225#[builder(name = "JsonSchemaBuilder")]
226#[builder(setter(into, strip_option), default)]
227pub struct JsonSchema {
228 #[serde(skip_serializing_if = "Option::is_none")]
230 description: Option<String>,
231 name: String,
233 #[serde(skip_serializing_if = "Option::is_none")]
235 schema: Option<serde_json::Value>,
236 #[serde(skip_serializing_if = "Option::is_none")]
238 strict: Option<bool>,
239}
240
241#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
242pub struct ChatCompletionTool {
243 pub r#type: ChatCompletionToolType,
245 pub function: ChatCompletionFunction,
247}
248
249#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
250#[serde(tag = "role", rename_all = "snake_case")]
251pub enum ChatMessage {
252 Developer {
253 content: ChatMessageContent,
255 #[serde(skip_serializing_if = "Option::is_none")]
257 name: Option<String>,
258 },
259 System {
260 content: ChatMessageContent,
262 #[serde(skip_serializing_if = "Option::is_none")]
264 name: Option<String>,
265 },
266 User {
267 content: ChatMessageContent,
269 #[serde(skip_serializing_if = "Option::is_none")]
271 name: Option<String>,
272 },
273 Assistant {
274 #[serde(skip_serializing_if = "Option::is_none")]
276 content: Option<ChatMessageContent>,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 reasoning_content: Option<String>,
280 #[serde(skip_serializing_if = "Option::is_none")]
282 refusal: Option<String>,
283 #[serde(skip_serializing_if = "Option::is_none")]
285 name: Option<String>,
286 #[serde(skip_serializing_if = "Option::is_none")]
288 audio: Option<AudioDataIdParameter>,
289 #[serde(skip_serializing_if = "Option::is_none")]
291 tool_calls: Option<Vec<ToolCall>>,
292 },
293 Tool {
294 content: ChatMessageContent,
296 tool_call_id: String,
298 },
299}
300
301impl ChatMessage {
302 pub fn message(&self) -> Option<&ChatMessageContent> {
304 match self {
305 ChatMessage::Developer { content, .. }
306 | ChatMessage::System { content, .. }
307 | ChatMessage::User { content, .. }
308 | ChatMessage::Assistant {
309 content: Some(content),
310 ..
311 } => Some(content),
312 ChatMessage::Assistant { content: None, .. } => None,
313 ChatMessage::Tool { .. } => None,
314 }
315 }
316
317 pub fn text(&self) -> Option<&str> {
319 match self {
320 ChatMessage::Developer { content, .. }
321 | ChatMessage::System { content, .. }
322 | ChatMessage::User { content, .. }
323 | ChatMessage::Tool { content, .. }
324 | ChatMessage::Assistant {
325 content: Some(content),
326 ..
327 } => {
328 if let ChatMessageContent::Text(text) = content {
329 Some(text)
330 } else {
331 None
332 }
333 }
334 ChatMessage::Assistant { content: None, .. } => None,
335 }
336 }
337
338 pub fn name(&self) -> Option<&str> {
340 match self {
341 ChatMessage::Developer { name, .. }
342 | ChatMessage::System { name, .. }
343 | ChatMessage::User { name, .. }
344 | ChatMessage::Assistant { name, .. } => name.as_deref(),
345 ChatMessage::Tool { .. } => None,
346 }
347 }
348}
349
350#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
351#[serde(tag = "role", rename_all = "snake_case")]
352pub enum DeltaChatMessage {
353 Developer {
354 content: ChatMessageContent,
356 #[serde(skip_serializing_if = "Option::is_none")]
358 name: Option<String>,
359 },
360 System {
361 content: ChatMessageContent,
363 #[serde(skip_serializing_if = "Option::is_none")]
365 name: Option<String>,
366 },
367 User {
368 content: ChatMessageContent,
370 #[serde(skip_serializing_if = "Option::is_none")]
372 name: Option<String>,
373 },
374 Assistant {
375 #[serde(skip_serializing_if = "Option::is_none")]
377 content: Option<ChatMessageContent>,
378 #[serde(skip_serializing_if = "Option::is_none")]
380 reasoning_content: Option<String>,
381 #[serde(skip_serializing_if = "Option::is_none")]
383 refusal: Option<String>,
384 #[serde(skip_serializing_if = "Option::is_none")]
386 name: Option<String>,
387 #[serde(skip_serializing_if = "Option::is_none")]
389 tool_calls: Option<Vec<DeltaToolCall>>,
390 },
391 Tool {
392 content: String,
394 tool_call_id: String,
396 },
397 #[serde(untagged)]
398 Untagged {
399 #[serde(skip_serializing_if = "Option::is_none")]
400 content: Option<ChatMessageContent>,
401 #[serde(skip_serializing_if = "Option::is_none")]
402 reasoning_content: Option<String>,
403 #[serde(skip_serializing_if = "Option::is_none")]
404 refusal: Option<String>,
405 #[serde(skip_serializing_if = "Option::is_none")]
406 name: Option<String>,
407 #[serde(skip_serializing_if = "Option::is_none")]
408 tool_calls: Option<Vec<DeltaToolCall>>,
409 #[serde(skip_serializing_if = "Option::is_none")]
410 tool_call_id: Option<String>,
411 },
412}
413
414#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
415pub struct ToolCall {
416 pub id: String,
418 pub r#type: String,
420 pub function: Function,
422}
423
424#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
425pub struct DeltaToolCall {
426 #[serde(skip_serializing_if = "Option::is_none")]
428 pub index: Option<u32>,
429 #[serde(skip_serializing_if = "Option::is_none")]
431 pub id: Option<String>,
432 #[serde(skip_serializing_if = "Option::is_none")]
434 pub r#type: Option<String>,
435 pub function: DeltaFunction,
437}
438
439#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
440pub struct Function {
441 pub name: String,
443 pub arguments: String,
445}
446
447#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
448pub struct DeltaFunction {
449 #[serde(skip_serializing_if = "Option::is_none")]
451 pub name: Option<String>,
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub arguments: Option<String>,
455}
456
457#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
458pub struct ChatCompletionChoice {
459 pub index: u32,
461 pub message: ChatMessage,
463 #[serde(skip_serializing_if = "Option::is_none")]
465 pub finish_reason: Option<FinishReason>,
466 #[serde(skip_serializing_if = "Option::is_none")]
468 pub logprobs: Option<LogProps>,
469}
470
471#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
472pub struct AudioDataIdParameter {
473 pub id: String,
475}
476
477#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
478pub struct AudioParameters {
479 pub voice: Voice,
481 pub format: AudioFormat,
483}
484
485#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
486pub struct LogProps {
487 #[serde(skip_serializing_if = "Option::is_none")]
489 pub content: Option<Vec<LogPropsContent>>,
490 #[serde(skip_serializing_if = "Option::is_none")]
492 pub refusal: Option<Vec<LogPropsContent>>,
493}
494
495#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
496pub struct LogPropsContent {
497 #[serde(flatten)]
499 pub token_info: LogProbsContentInfo,
500 pub top_logprobs: Vec<LogProbsContentInfo>,
502}
503
504#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
505pub struct LogProbsContentInfo {
506 pub token: String,
508 pub logprob: f32,
511 pub bytes: Option<Vec<u8>>,
513}
514
515#[cfg(feature = "stream")]
516#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
517pub struct ChatCompletionChunkChoice {
518 pub index: Option<u32>,
520 pub delta: DeltaChatMessage,
522 #[serde(skip_serializing_if = "Option::is_none")]
524 pub finish_reason: Option<FinishReason>,
525 #[serde(skip_serializing_if = "Option::is_none")]
527 pub logprobs: Option<LogProps>,
528}
529
530#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
531pub struct ImageUrlType {
532 pub url: String,
534 #[serde(skip_serializing_if = "Option::is_none")]
536 pub detail: Option<ImageUrlDetail>,
537}
538
539#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
540pub struct PredictedOutput {
541 pub r#type: PredictedOutputType,
543 pub content: PredictedOutputContent,
546}
547
548#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
549#[serde(untagged)]
550pub enum PredictedOutputContent {
551 String(String),
552 Array(Vec<PredictedOutputArrayPart>),
553}
554
555#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
556pub struct PredictedOutputArrayPart {
557 pub r#type: String,
559 pub text: String,
561}
562
563#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
564#[serde(rename_all = "snake_case")]
565pub enum PredictedOutputType {
566 Content,
567}
568
569#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
570#[serde(rename_all = "snake_case")]
571pub enum Modality {
572 Text,
573 Audio,
574}
575
576#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
577#[serde(rename_all = "snake_case")]
578pub enum ImageUrlDetail {
579 Auto,
580 High,
581 Low,
582}
583
584#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
585#[serde(untagged)]
586pub enum ChatMessageContent {
587 Text(String),
588 ContentPart(Vec<ChatMessageContentPart>),
589 None,
590}
591
592#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
593#[serde(untagged)]
594pub enum ChatMessageContentPart {
595 Text(ChatMessageTextContentPart),
596 Image(ChatMessageImageContentPart),
597 Audio(ChatMessageAudioContentPart),
598}
599
600#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
601pub struct ChatMessageTextContentPart {
602 pub r#type: String,
604 pub text: String,
606}
607
608#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
609pub struct ChatMessageImageContentPart {
610 pub r#type: String,
612 pub image_url: ImageUrlType,
614}
615
616#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
617pub struct ChatMessageAudioContentPart {
618 pub r#type: String,
620 pub input_audio: InputAudioData,
622}
623
624#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
625pub struct ChatMessageImageUrl {
626 pub url: String,
628 pub detail: String,
630}
631
632#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
633pub struct InputAudioData {
634 pub data: String,
636 pub format: String,
638}
639
640#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
641pub struct WebSearchOptions {
642 #[serde(skip_serializing_if = "Option::is_none")]
644 pub search_context_size: Option<WebSearchContextSize>,
645 #[serde(skip_serializing_if = "Option::is_none")]
647 pub user_location: Option<ApproximateUserLocation>,
648}
649
650#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
651pub struct ApproximateUserLocation {
652 pub r#type: UserLocationType,
653 pub approximate: WebSearchUserLocation,
655}
656
657impl Display for ChatMessageContent {
658 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
659 match self {
660 ChatMessageContent::Text(text) => write!(f, "{text}"),
661 ChatMessageContent::ContentPart(tcp) => {
662 for part in tcp {
663 write!(f, "{part:?}")?;
664 }
665 Ok(())
666 }
667 ChatMessageContent::None => write!(f, ""),
668 }
669 }
670}
671
672#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
673#[serde(rename_all = "snake_case")]
674pub enum ChatCompletionToolType {
675 Function,
676}
677
678#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
679#[serde(rename_all = "snake_case")]
680pub enum ChatCompletionToolChoice {
681 None,
682 Auto,
683 Required,
684 #[serde(untagged)]
685 ChatCompletionToolChoiceFunction(ChatCompletionToolChoiceFunction),
686}
687
688#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
689pub struct WebSearchUserLocation {
690 pub city: Option<String>,
691 pub country: Option<String>,
692 pub region: Option<String>,
693 pub timezone: Option<String>,
694}
695
696#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
697#[serde(rename_all = "snake_case")]
698pub enum UserLocationType {
699 Approximate,
700}
701
702#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
703#[serde(rename_all = "snake_case")]
704pub enum Voice {
705 Alloy,
706 Ash,
707 Ballad,
708 Coral,
709 Echo,
710 Sage,
711 Shimmer,
712 Verse,
713}
714
715#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
716#[serde(rename_all = "snake_case")]
717pub enum AudioFormat {
718 Wav,
719 Mp3,
720 Flac,
721 Opus,
722 Pcm16,
723}
724
725impl Default for ChatMessageContent {
726 fn default() -> Self {
727 ChatMessageContent::Text("".to_string())
728 }
729}
730
731impl DeltaFunction {
732 pub fn merge(&mut self, other: &Self) {
733 if self.name.is_none() && other.name.is_some() {
734 self.name.clone_from(&other.name);
735 }
736
737 if let Some(arguments) = &other.arguments {
738 if let Some(self_arguments) = &mut self.arguments {
739 self_arguments.push_str(arguments);
740 } else {
741 self.arguments = Some(arguments.clone());
742 }
743 }
744 }
745
746 pub fn is_empty(&self) -> bool {
747 self.name.is_none() && self.arguments.is_none()
748 }
749}
750
751#[cfg(test)]
752mod tests {
753 use crate::chat::{
754 ChatCompletionResponseFormat, ChatCompletionToolChoice, ChatCompletionToolChoiceFunction,
755 ChatCompletionToolChoiceFunctionName, ChatCompletionToolType, ChatMessage,
756 ChatMessageContent, ChatMessageContentPart, ChatMessageTextContentPart, JsonSchemaBuilder,
757 };
758 use serde_json;
759
760 #[test]
761 fn test_chat_completion_response_format_serialization_deserialization() {
762 let json_schema = JsonSchemaBuilder::default()
763 .description("This is a test schema".to_string())
764 .name("test_schema".to_string())
765 .schema(Some(serde_json::json!({"type": "object"})))
766 .strict(true)
767 .build()
768 .unwrap();
769
770 let response_format = ChatCompletionResponseFormat::JsonSchema { json_schema };
771
772 let serialized = serde_json::to_string(&response_format).unwrap();
774 assert_eq!(serialized, "{\"type\":\"json_schema\",\"json_schema\":{\"description\":\"This is a test schema\",\"name\":\"test_schema\",\"schema\":{\"type\":\"object\"},\"strict\":true}}");
775
776 let deserialized: ChatCompletionResponseFormat = serde_json::from_str(&serialized).unwrap();
778 match deserialized {
779 ChatCompletionResponseFormat::JsonSchema { json_schema } => {
780 assert_eq!(
781 json_schema.description,
782 Some("This is a test schema".to_string())
783 );
784 assert_eq!(json_schema.name, "test_schema".to_string());
785 assert_eq!(
786 json_schema.schema,
787 Some(serde_json::json!({"type": "object"}))
788 );
789 assert_eq!(json_schema.strict, Some(true));
790 }
791 _ => panic!("Deserialized format should be JsonSchema"),
792 }
793 }
794
795 #[test]
796 fn test_chat_completion_tool_choice_required_serialization_deserialization() {
797 let tool_choice = ChatCompletionToolChoice::Required;
798
799 let serialized = serde_json::to_string(&tool_choice).unwrap();
800 assert_eq!(serialized, "\"required\"");
801
802 let deserialized: ChatCompletionToolChoice =
803 serde_json::from_str(serialized.as_str()).unwrap();
804 assert_eq!(deserialized, tool_choice)
805 }
806
807 #[test]
808 fn test_chat_completion_tool_choice_named_function_serialization_deserialization() {
809 let tool_choice = ChatCompletionToolChoice::ChatCompletionToolChoiceFunction(
810 ChatCompletionToolChoiceFunction {
811 r#type: Some(ChatCompletionToolType::Function),
812 function: ChatCompletionToolChoiceFunctionName {
813 name: "get_current_weather".to_string(),
814 },
815 },
816 );
817
818 let serialized = serde_json::to_string(&tool_choice).unwrap();
819 assert_eq!(
820 serialized,
821 "{\"type\":\"function\",\"function\":{\"name\":\"get_current_weather\"}}"
822 );
823
824 let deserialized: ChatCompletionToolChoice =
825 serde_json::from_str(serialized.as_str()).unwrap();
826 assert_eq!(deserialized, tool_choice)
827 }
828
829 #[test]
830 fn test_chat_message_tool_content_string_serialization_deserialization() {
831 let tool_message = ChatMessage::Tool {
832 content: ChatMessageContent::Text("tool_result".to_string()),
833 tool_call_id: "tool_call_id".to_string(),
834 };
835
836 let serialized = serde_json::to_string(&tool_message).unwrap();
837 assert_eq!(
838 serialized,
839 "{\"role\":\"tool\",\"content\":\"tool_result\",\"tool_call_id\":\"tool_call_id\"}"
840 );
841
842 let deserialized: ChatMessage = serde_json::from_str(serialized.as_str()).unwrap();
843 assert_eq!(deserialized, tool_message)
844 }
845
846 #[test]
847 fn test_chat_message_tool_content_array_serialization_deserialization() {
848 let content_array = vec![ChatMessageContentPart::Text(ChatMessageTextContentPart {
849 r#type: "text".to_string(),
850 text: "tool_result".to_string(),
851 })];
852 let tool_message = ChatMessage::Tool {
853 content: ChatMessageContent::ContentPart(content_array),
854 tool_call_id: "tool_call_id".to_string(),
855 };
856
857 let serialized = serde_json::to_string(&tool_message).unwrap();
858 assert_eq!(
859 serialized,
860 "{\"role\":\"tool\",\"content\":[{\"type\":\"text\",\"text\":\"tool_result\"}],\"tool_call_id\":\"tool_call_id\"}"
861 );
862
863 let deserialized: ChatMessage = serde_json::from_str(serialized.as_str()).unwrap();
864 assert_eq!(deserialized, tool_message)
865 }
866}