openai_protocol/builders/realtime/
response.rs1use std::collections::HashMap;
6
7use crate::{
8 realtime_conversation::RealtimeConversationItem,
9 realtime_response::{
10 RealtimeResponse, RealtimeResponseCreateAudioOutput, RealtimeResponseObject,
11 RealtimeResponseStatus, RealtimeResponseUsage, ResponseStatus,
12 },
13 realtime_session::{MaxOutputTokens, OutputModality},
14};
15
16#[must_use = "Builder does nothing until .build() is called"]
20#[derive(Clone, Debug)]
21pub struct RealtimeResponseBuilder {
22 id: String,
23 object: Option<RealtimeResponseObject>,
24 status: Option<ResponseStatus>,
25 status_details: Option<RealtimeResponseStatus>,
26 output: Vec<RealtimeConversationItem>,
27 metadata: HashMap<String, String>,
28 audio: Option<RealtimeResponseCreateAudioOutput>,
29 usage: Option<RealtimeResponseUsage>,
30 conversation_id: Option<String>,
31 output_modalities: Option<Vec<OutputModality>>,
32 max_output_tokens: Option<MaxOutputTokens>,
33}
34
35impl RealtimeResponseBuilder {
36 pub fn new(id: impl Into<String>) -> Self {
41 Self {
42 id: id.into(),
43 object: Some(RealtimeResponseObject::RealtimeResponse),
44 status: Some(ResponseStatus::InProgress),
45 status_details: None,
46 output: Vec::new(),
47 metadata: HashMap::new(),
48 audio: None,
49 usage: None,
50 conversation_id: None,
51 output_modalities: None,
52 max_output_tokens: None,
53 }
54 }
55
56 pub fn status(mut self, status: ResponseStatus) -> Self {
58 self.status = Some(status);
59 self
60 }
61
62 pub fn status_details(mut self, details: RealtimeResponseStatus) -> Self {
64 self.status_details = Some(details);
65 self
66 }
67
68 pub fn output(mut self, output: Vec<RealtimeConversationItem>) -> Self {
70 self.output = output;
71 self
72 }
73
74 pub fn add_output(mut self, item: RealtimeConversationItem) -> Self {
76 self.output.push(item);
77 self
78 }
79
80 pub fn metadata(mut self, metadata: HashMap<String, String>) -> Self {
82 self.metadata = metadata;
83 self
84 }
85
86 pub fn add_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
88 self.metadata.insert(key.into(), value.into());
89 self
90 }
91
92 pub fn audio(mut self, audio: RealtimeResponseCreateAudioOutput) -> Self {
94 self.audio = Some(audio);
95 self
96 }
97
98 pub fn usage(mut self, usage: RealtimeResponseUsage) -> Self {
100 self.usage = Some(usage);
101 self
102 }
103
104 pub fn maybe_usage(mut self, usage: Option<RealtimeResponseUsage>) -> Self {
106 if let Some(u) = usage {
107 self.usage = Some(u);
108 }
109 self
110 }
111
112 pub fn conversation_id(mut self, id: impl Into<String>) -> Self {
114 self.conversation_id = Some(id.into());
115 self
116 }
117
118 pub fn output_modalities(mut self, modalities: Vec<OutputModality>) -> Self {
120 self.output_modalities = Some(modalities);
121 self
122 }
123
124 pub fn max_output_tokens(mut self, max: MaxOutputTokens) -> Self {
126 self.max_output_tokens = Some(max);
127 self
128 }
129
130 pub fn build(self) -> RealtimeResponse {
132 let metadata = if self.metadata.is_empty() {
133 None
134 } else {
135 Some(self.metadata)
136 };
137
138 let output = if self.output.is_empty() {
139 None
140 } else {
141 Some(self.output)
142 };
143
144 RealtimeResponse {
145 id: Some(self.id),
146 object: self.object,
147 status: self.status,
148 status_details: self.status_details,
149 output,
150 metadata,
151 audio: self.audio,
152 usage: self.usage,
153 conversation_id: self.conversation_id,
154 output_modalities: self.output_modalities,
155 max_output_tokens: self.max_output_tokens,
156 }
157 }
158}
159
160#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_build_minimal() {
170 let response = RealtimeResponseBuilder::new("resp_123").build();
171
172 assert_eq!(response.id.as_ref().unwrap(), "resp_123");
173 assert_eq!(
174 response.object.as_ref().unwrap(),
175 &RealtimeResponseObject::RealtimeResponse
176 );
177 assert_eq!(
178 response.status.as_ref().unwrap(),
179 &ResponseStatus::InProgress
180 );
181 assert!(response.output.is_none());
182 assert!(response.metadata.is_none());
183 assert!(response.usage.is_none());
184 }
185
186 #[test]
187 fn test_build_complete() {
188 let usage = RealtimeResponseUsage {
189 total_tokens: Some(100),
190 input_tokens: Some(40),
191 output_tokens: Some(60),
192 input_token_details: None,
193 output_token_details: None,
194 };
195
196 let response = RealtimeResponseBuilder::new("resp_full")
197 .status(ResponseStatus::Completed)
198 .conversation_id("conv_123")
199 .output_modalities(vec![OutputModality::Audio, OutputModality::Text])
200 .max_output_tokens(MaxOutputTokens::Integer(4096))
201 .add_metadata("session", "test")
202 .maybe_usage(Some(usage))
203 .build();
204
205 assert_eq!(response.id.as_ref().unwrap(), "resp_full");
206 assert_eq!(
207 response.status.as_ref().unwrap(),
208 &ResponseStatus::Completed
209 );
210 assert_eq!(response.conversation_id.as_ref().unwrap(), "conv_123");
211 assert_eq!(response.output_modalities.as_ref().unwrap().len(), 2);
212 assert!(response.metadata.is_some());
213 assert_eq!(response.usage.as_ref().unwrap().total_tokens, Some(100));
214 }
215}