openai_rust_sdk/models/threads/
thread.rs1use crate::api::base::Validate;
4use crate::{De, Ser};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8use super::builders::MetadataBuilder;
9use super::message::MessageRequest;
10use super::types::{default_thread_object, SortOrder};
11use super::validation::common::validate_metadata;
12
13#[derive(Debug, Clone, PartialEq, Ser, De)]
15pub struct Thread {
16 pub id: String,
18 #[serde(default = "default_thread_object")]
20 pub object: String,
21 pub created_at: i64,
23 #[serde(default)]
25 pub metadata: HashMap<String, String>,
26}
27
28#[derive(Debug, Clone, Ser, De)]
30pub struct ThreadRequest {
31 #[serde(default, skip_serializing_if = "Vec::is_empty")]
33 pub messages: Vec<MessageRequest>,
34 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
36 pub metadata: HashMap<String, String>,
37}
38
39impl ThreadRequest {
40 #[must_use]
42 pub fn builder() -> ThreadRequestBuilder {
43 ThreadRequestBuilder::new()
44 }
45
46 #[must_use]
48 pub fn new() -> Self {
49 Self {
50 messages: Vec::new(),
51 metadata: HashMap::new(),
52 }
53 }
54
55 pub fn validate(&self) -> Result<(), String> {
57 validate_metadata(&self.metadata).map_err(|e| format!("Thread {}", e))?;
59
60 for message in &self.messages {
62 message.validate()?;
63 }
64
65 Ok(())
66 }
67}
68
69impl Validate for ThreadRequest {
70 fn validate(&self) -> Result<(), String> {
71 self.validate()
72 }
73}
74
75impl Default for ThreadRequest {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81#[derive(Debug, Clone, Default)]
83pub struct ThreadRequestBuilder {
84 messages: Vec<MessageRequest>,
86 metadata: HashMap<String, String>,
88}
89
90impl MetadataBuilder for ThreadRequestBuilder {
91 fn get_metadata_mut(&mut self) -> &mut HashMap<String, String> {
92 &mut self.metadata
93 }
94}
95
96impl ThreadRequestBuilder {
97 #[must_use]
99 pub fn new() -> Self {
100 Self::default()
101 }
102
103 #[must_use]
105 pub fn message(mut self, message: MessageRequest) -> Self {
106 self.messages.push(message);
107 self
108 }
109
110 #[must_use]
112 pub fn messages(mut self, messages: Vec<MessageRequest>) -> Self {
113 self.messages.extend(messages);
114 self
115 }
116
117 pub fn metadata_pair(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
119 self.add_metadata_pair(key, value);
120 self
121 }
122
123 #[must_use]
125 pub fn metadata(mut self, metadata: HashMap<String, String>) -> Self {
126 self.set_metadata(metadata);
127 self
128 }
129
130 #[must_use]
132 pub fn build(self) -> ThreadRequest {
133 ThreadRequest {
134 messages: self.messages,
135 metadata: self.metadata,
136 }
137 }
138}
139
140crate::impl_list_response!(ListThreadsResponse, Thread, "Response from listing threads");
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use crate::models::threads::types::MessageRole;
147
148 #[test]
149 fn test_thread_request_builder() {
150 let request = ThreadRequest::builder()
151 .metadata_pair("purpose", "testing")
152 .build();
153
154 assert_eq!(request.metadata.len(), 1);
155 assert_eq!(
156 request.metadata.get("purpose"),
157 Some(&"testing".to_string())
158 );
159 assert!(request.messages.is_empty());
160 }
161
162 #[test]
163 fn test_thread_request_validation() {
164 let request = ThreadRequest::builder()
166 .metadata_pair("key", "value")
167 .build();
168 assert!(request.validate().is_ok());
169
170 let mut request = ThreadRequest::new();
172 for i in 0..17 {
173 request
174 .metadata
175 .insert(format!("key{}", i), "value".to_string());
176 }
177 assert!(request.validate().is_err());
178 }
179
180 #[test]
181 fn test_thread_request_with_messages() {
182 let message = MessageRequest::new(MessageRole::User, "Hello");
183 let request = ThreadRequest::builder().message(message).build();
184
185 assert_eq!(request.messages.len(), 1);
186 assert_eq!(request.messages[0].content, "Hello");
187 }
188}