1use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7pub use crate::shared::{
9 CompletionTokensDetails, FinishReason, PromptTokensDetails, ReasoningEffort, Role,
10 SearchContextSize, ServiceTier, Usage,
11};
12
13#[derive(Debug, Clone, Serialize)]
17#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
18pub struct ChatCompletionRequest {
19 pub model: String,
21
22 pub messages: Vec<ChatCompletionMessageParam>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub frequency_penalty: Option<f64>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub logit_bias: Option<HashMap<String, i32>>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub logprobs: Option<bool>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub top_logprobs: Option<u8>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub max_completion_tokens: Option<i64>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub n: Option<i32>,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub presence_penalty: Option<f64>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub response_format: Option<ResponseFormat>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub seed: Option<i64>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub service_tier: Option<ServiceTier>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub stop: Option<Stop>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub stream: Option<bool>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub stream_options: Option<StreamOptions>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub temperature: Option<f64>,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub top_p: Option<f64>,
84
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub tools: Option<Vec<Tool>>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub tool_choice: Option<ToolChoice>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub parallel_tool_calls: Option<bool>,
96
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub user: Option<String>,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub store: Option<bool>,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub metadata: Option<HashMap<String, String>>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
111 pub modalities: Option<Vec<String>>,
112
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub reasoning_effort: Option<ReasoningEffort>,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub verbosity: Option<String>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub audio: Option<ChatCompletionAudioParam>,
124
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub prediction: Option<PredictionContent>,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub web_search_options: Option<WebSearchOptions>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub prompt_cache_key: Option<String>,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub cache_control: Option<serde_json::Value>,
140
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub provider: Option<serde_json::Value>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub session_id: Option<String>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub max_tokens: Option<i64>,
152
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub functions: Option<Vec<FunctionDef>>,
156
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub function_call: Option<FunctionCallOption>,
160}
161
162impl ChatCompletionRequest {
163 pub fn new(model: impl Into<String>, messages: Vec<ChatCompletionMessageParam>) -> Self {
165 Self {
166 model: model.into(),
167 messages,
168 frequency_penalty: None,
169 logit_bias: None,
170 logprobs: None,
171 top_logprobs: None,
172 max_completion_tokens: None,
173 n: None,
174 presence_penalty: None,
175 response_format: None,
176 seed: None,
177 service_tier: None,
178 stop: None,
179 stream: None,
180 stream_options: None,
181 temperature: None,
182 top_p: None,
183 tools: None,
184 tool_choice: None,
185 parallel_tool_calls: None,
186 user: None,
187 store: None,
188 metadata: None,
189 modalities: None,
190 reasoning_effort: None,
191 verbosity: None,
192 audio: None,
193 prediction: None,
194 web_search_options: None,
195 prompt_cache_key: None,
196 cache_control: None,
197 provider: None,
198 session_id: None,
199 max_tokens: None,
200 functions: None,
201 function_call: None,
202 }
203 }
204
205 pub fn model(mut self, model: impl Into<String>) -> Self {
207 self.model = model.into();
208 self
209 }
210
211 pub fn messages(mut self, messages: Vec<ChatCompletionMessageParam>) -> Self {
213 self.messages = messages;
214 self
215 }
216
217 pub fn temperature(mut self, temperature: f64) -> Self {
219 self.temperature = Some(temperature);
220 self
221 }
222
223 pub fn max_completion_tokens(mut self, max: i64) -> Self {
225 self.max_completion_tokens = Some(max);
226 self
227 }
228
229 pub fn tools(mut self, tools: Vec<Tool>) -> Self {
231 self.tools = Some(tools);
232 self
233 }
234
235 pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
237 self.tool_choice = Some(choice);
238 self
239 }
240
241 pub fn response_format(mut self, format: ResponseFormat) -> Self {
243 self.response_format = Some(format);
244 self
245 }
246
247 pub fn reasoning_effort(mut self, effort: ReasoningEffort) -> Self {
249 self.reasoning_effort = Some(effort);
250 self
251 }
252
253 pub fn prediction(mut self, prediction: PredictionContent) -> Self {
255 self.prediction = Some(prediction);
256 self
257 }
258
259 pub fn top_p(mut self, top_p: f64) -> Self {
261 self.top_p = Some(top_p);
262 self
263 }
264
265 pub fn seed(mut self, seed: i64) -> Self {
267 self.seed = Some(seed);
268 self
269 }
270
271 pub fn stop(mut self, stop: Stop) -> Self {
273 self.stop = Some(stop);
274 self
275 }
276
277 pub fn user(mut self, user: impl Into<String>) -> Self {
279 self.user = Some(user.into());
280 self
281 }
282
283 pub fn store(mut self, store: bool) -> Self {
285 self.store = Some(store);
286 self
287 }
288
289 pub fn n(mut self, n: i32) -> Self {
291 self.n = Some(n);
292 self
293 }
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
298#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
299#[serde(untagged)]
300#[non_exhaustive]
301pub enum Stop {
302 Single(String),
303 Multiple(Vec<String>),
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
308#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
309pub struct StreamOptions {
310 #[serde(skip_serializing_if = "Option::is_none")]
312 pub include_usage: Option<bool>,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
317#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
318pub struct ChatCompletionAudioParam {
319 pub format: String,
321 pub voice: String,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
327#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
328pub struct PredictionContent {
329 #[serde(rename = "type")]
331 pub type_: String,
332 pub content: serde_json::Value,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
338#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
339pub struct WebSearchOptions {
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub search_context_size: Option<SearchContextSize>,
343 #[serde(skip_serializing_if = "Option::is_none")]
345 pub user_location: Option<WebSearchUserLocation>,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
350#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
351pub struct WebSearchUserLocation {
352 #[serde(rename = "type")]
354 pub type_: String,
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub approximate: Option<ApproximateLocation>,
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize)]
362#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
363pub struct ApproximateLocation {
364 #[serde(skip_serializing_if = "Option::is_none")]
365 pub city: Option<String>,
366 #[serde(skip_serializing_if = "Option::is_none")]
367 pub country: Option<String>,
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub region: Option<String>,
370 #[serde(skip_serializing_if = "Option::is_none")]
371 pub timezone: Option<String>,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
377#[serde(tag = "type")]
378#[non_exhaustive]
379pub enum ResponseFormat {
380 #[serde(rename = "text")]
381 Text,
382 #[serde(rename = "json_object")]
383 JsonObject,
384 #[serde(rename = "json_schema")]
385 JsonSchema { json_schema: JsonSchema },
386}
387
388#[derive(Debug, Clone, Serialize, Deserialize)]
390#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
391pub struct JsonSchema {
392 pub name: String,
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub description: Option<String>,
395 #[serde(skip_serializing_if = "Option::is_none")]
396 pub schema: Option<serde_json::Value>,
397 #[serde(skip_serializing_if = "Option::is_none")]
398 pub strict: Option<bool>,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
405#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
406#[serde(tag = "role")]
407#[non_exhaustive]
408pub enum ChatCompletionMessageParam {
409 #[serde(rename = "system")]
410 System {
411 content: String,
412 #[serde(skip_serializing_if = "Option::is_none")]
413 name: Option<String>,
414 },
415 #[serde(rename = "developer")]
416 Developer {
417 content: String,
418 #[serde(skip_serializing_if = "Option::is_none")]
419 name: Option<String>,
420 },
421 #[serde(rename = "user")]
422 User {
423 content: UserContent,
424 #[serde(skip_serializing_if = "Option::is_none")]
425 name: Option<String>,
426 },
427 #[serde(rename = "assistant")]
428 Assistant {
429 #[serde(skip_serializing_if = "Option::is_none")]
430 content: Option<String>,
431 #[serde(skip_serializing_if = "Option::is_none")]
432 name: Option<String>,
433 #[serde(skip_serializing_if = "Option::is_none")]
434 tool_calls: Option<Vec<ToolCall>>,
435 #[serde(skip_serializing_if = "Option::is_none")]
436 refusal: Option<String>,
437 },
438 #[serde(rename = "tool")]
439 Tool {
440 content: String,
441 tool_call_id: String,
442 },
443}
444
445#[derive(Debug, Clone, Serialize, Deserialize)]
447#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
448#[serde(untagged)]
449#[non_exhaustive]
450pub enum UserContent {
451 Text(String),
452 Parts(Vec<ContentPart>),
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
457#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
458#[serde(tag = "type")]
459#[non_exhaustive]
460pub enum ContentPart {
461 #[serde(rename = "text")]
462 Text { text: String },
463 #[serde(rename = "image_url")]
464 ImageUrl { image_url: ImageUrl },
465 #[serde(rename = "input_audio")]
466 InputAudio { input_audio: InputAudio },
467}
468
469#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
471#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
472#[serde(rename_all = "snake_case")]
473#[non_exhaustive]
474pub enum ImageDetail {
475 Auto,
476 Low,
477 High,
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize)]
482#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
483pub struct ImageUrl {
484 pub url: String,
485 #[serde(skip_serializing_if = "Option::is_none")]
486 pub detail: Option<ImageDetail>,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
491#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
492pub struct InputAudio {
493 pub data: String,
494 pub format: String,
495}
496
497#[derive(Debug, Clone, Serialize, Deserialize)]
501#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
502pub struct Tool {
503 #[serde(rename = "type")]
504 pub type_: String,
505 pub function: FunctionDef,
506}
507
508fn strip_format_recursive(value: &mut serde_json::Value) {
511 if let Some(map) = value.as_object_mut() {
512 map.remove("format");
513 if map.get("minimum") == Some(&serde_json::json!(0.0)) {
514 map.remove("minimum");
515 }
516 for v in map.values_mut() {
517 strip_format_recursive(v);
518 }
519 } else if let Some(arr) = value.as_array_mut() {
520 for v in arr {
521 strip_format_recursive(v);
522 }
523 }
524}
525
526impl Tool {
527 pub fn function(
531 name: impl Into<String>,
532 description: impl Into<String>,
533 mut parameters: serde_json::Value,
534 ) -> Self {
535 strip_format_recursive(&mut parameters);
536 Self {
537 type_: "function".to_string(),
538 function: FunctionDef {
539 name: name.into(),
540 description: Some(description.into()),
541 parameters: Some(parameters),
542 strict: Some(true),
543 },
544 }
545 }
546
547 pub fn web_search() -> Self {
549 Self {
550 type_: "web_search".to_string(),
551 function: FunctionDef {
552 name: "".to_string(),
553 description: None,
554 parameters: None,
555 strict: None,
556 },
557 }
558 }
559
560 pub fn file_search() -> Self {
562 Self {
563 type_: "file_search".to_string(),
564 function: FunctionDef {
565 name: "".to_string(),
566 description: None,
567 parameters: None,
568 strict: None,
569 },
570 }
571 }
572
573 pub fn code_interpreter() -> Self {
575 Self {
576 type_: "code_interpreter".to_string(),
577 function: FunctionDef {
578 name: "".to_string(),
579 description: None,
580 parameters: None,
581 strict: None,
582 },
583 }
584 }
585}
586
587#[derive(Debug, Clone, Serialize, Deserialize)]
589#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
590pub struct FunctionDef {
591 pub name: String,
592 #[serde(skip_serializing_if = "Option::is_none")]
593 pub description: Option<String>,
594 #[serde(skip_serializing_if = "Option::is_none")]
595 pub parameters: Option<serde_json::Value>,
596 #[serde(skip_serializing_if = "Option::is_none")]
597 pub strict: Option<bool>,
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize)]
602#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
603#[serde(untagged)]
604#[non_exhaustive]
605pub enum FunctionCallOption {
606 Mode(String),
608 Named { name: String },
610}
611
612#[derive(Debug, Clone, Serialize, Deserialize)]
614#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
615#[serde(untagged)]
616#[non_exhaustive]
617pub enum ToolChoice {
618 Mode(String),
620 Named {
622 #[serde(rename = "type")]
623 type_: String,
624 function: ToolChoiceFunction,
625 },
626}
627
628#[derive(Debug, Clone, Serialize, Deserialize)]
630#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
631pub struct ToolChoiceFunction {
632 pub name: String,
633}
634
635#[derive(Debug, Clone, Serialize, Deserialize)]
637#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
638pub struct ToolCall {
639 pub id: String,
640 #[serde(rename = "type")]
641 pub type_: String,
642 pub function: FunctionCall,
643}
644
645#[derive(Debug, Clone, Serialize, Deserialize)]
647#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
648pub struct FunctionCall {
649 pub name: String,
650 pub arguments: String,
651}
652
653#[derive(Debug, Clone, Deserialize)]
657#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
658pub struct ChatCompletionResponse {
659 pub id: String,
660 pub choices: Vec<ChatCompletionChoice>,
661 pub created: i64,
662 pub model: String,
663 pub object: String,
664 #[serde(default)]
665 pub service_tier: Option<ServiceTier>,
666 #[serde(default)]
667 pub system_fingerprint: Option<String>,
668 #[serde(default)]
669 pub usage: Option<Usage>,
670 #[serde(default)]
672 pub session_id: Option<String>,
673}
674
675#[derive(Debug, Clone, Deserialize)]
677#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
678pub struct ChatCompletionChoice {
679 pub finish_reason: FinishReason,
680 pub index: i32,
681 pub message: ChatCompletionMessage,
682 #[serde(default)]
683 pub logprobs: Option<ChoiceLogprobs>,
684}
685
686#[derive(Debug, Clone, Deserialize)]
688#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
689pub struct ChatCompletionMessage {
690 pub role: Role,
691 #[serde(default)]
692 pub content: Option<String>,
693 #[serde(default)]
694 pub refusal: Option<String>,
695 #[serde(default)]
696 pub tool_calls: Option<Vec<ToolCall>>,
697 #[serde(default)]
698 pub annotations: Option<Vec<Annotation>>,
699}
700
701#[derive(Debug, Clone, Deserialize)]
703#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
704pub struct ChoiceLogprobs {
705 #[serde(default)]
706 pub content: Option<Vec<TokenLogprob>>,
707 #[serde(default)]
708 pub refusal: Option<Vec<TokenLogprob>>,
709}
710
711#[derive(Debug, Clone, Deserialize)]
713#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
714pub struct TokenLogprob {
715 pub token: String,
716 pub logprob: f64,
717 #[serde(default)]
718 pub bytes: Option<Vec<u8>>,
719 #[serde(default)]
720 pub top_logprobs: Option<Vec<TopLogprob>>,
721}
722
723#[derive(Debug, Clone, Serialize, Deserialize)]
725#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
726pub struct TopLogprob {
727 pub token: String,
728 pub logprob: f64,
729 #[serde(default)]
730 pub bytes: Option<Vec<u8>>,
731}
732
733#[derive(Debug, Clone, Serialize, Deserialize)]
735#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
736pub struct Annotation {
737 #[serde(rename = "type")]
738 pub type_: String,
739 #[serde(default)]
740 pub url_citation: Option<UrlCitation>,
741}
742
743#[derive(Debug, Clone, Serialize, Deserialize)]
745#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
746pub struct UrlCitation {
747 pub end_index: i32,
748 pub start_index: i32,
749 pub title: String,
750 pub url: String,
751}
752
753#[derive(Debug, Clone, Deserialize)]
757#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
758pub struct ChatCompletionChunk {
759 pub id: String,
760 pub choices: Vec<ChunkChoice>,
761 pub created: i64,
762 pub model: String,
763 pub object: String,
764 #[serde(default)]
765 pub service_tier: Option<ServiceTier>,
766 #[serde(default)]
767 pub system_fingerprint: Option<String>,
768 #[serde(default)]
769 pub usage: Option<Usage>,
770 #[serde(default)]
772 pub session_id: Option<String>,
773}
774
775#[derive(Debug, Clone, Deserialize)]
777#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
778pub struct ChunkChoice {
779 pub delta: ChoiceDelta,
780 pub finish_reason: Option<FinishReason>,
781 pub index: i32,
782 #[serde(default)]
783 pub logprobs: Option<ChoiceLogprobs>,
784}
785
786#[derive(Debug, Clone, Deserialize)]
788#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
789pub struct ChoiceDelta {
790 #[serde(default)]
791 pub content: Option<String>,
792 #[serde(default)]
793 pub role: Option<Role>,
794 #[serde(default)]
795 pub refusal: Option<String>,
796 #[serde(default)]
797 pub tool_calls: Option<Vec<DeltaToolCall>>,
798}
799
800#[derive(Debug, Clone, Deserialize)]
802#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
803pub struct DeltaToolCall {
804 pub index: i32,
805 #[serde(default)]
806 pub id: Option<String>,
807 #[serde(default)]
808 pub function: Option<DeltaFunctionCall>,
809 #[serde(default, rename = "type")]
810 pub type_: Option<String>,
811}
812
813#[derive(Debug, Clone, Deserialize)]
815#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
816pub struct DeltaFunctionCall {
817 #[serde(default)]
818 pub arguments: Option<String>,
819 #[serde(default)]
820 pub name: Option<String>,
821}