1use std::sync::Arc;
5
6use crate::config::{ClientBuilder, Config};
7use crate::error::OpenAIError;
8use crate::resources::assistants::{Assistants, Threads};
9use crate::resources::audio::Audio;
10use crate::resources::batches::Batches;
11use crate::resources::chat::Chat;
12use crate::resources::completions::Completions;
13use crate::resources::embeddings::Embeddings;
14use crate::resources::files::Files;
15use crate::resources::fine_tuning::FineTuning;
16use crate::resources::images::Images;
17use crate::resources::models::Models;
18use crate::resources::moderations::Moderations;
19use crate::resources::responses::Responses;
20use crate::resources::uploads::Uploads;
21use crate::resources::vector_stores::VectorStores;
22
23#[derive(Clone)]
28pub struct Client {
29 inner: Arc<Inner>,
30}
31
32struct Inner {
33 http: reqwest::Client,
34 config: Config,
35}
36
37impl std::fmt::Debug for Client {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_struct("Client")
40 .field("config", &self.inner.config)
41 .finish()
42 }
43}
44
45impl Client {
46 pub fn new() -> Result<Self, OpenAIError> {
49 Self::builder().build()
50 }
51
52 pub fn builder() -> ClientBuilder {
54 ClientBuilder::new()
55 }
56
57 pub(crate) fn from_config(config: Config) -> Result<Self, OpenAIError> {
58 let http = reqwest::Client::builder()
63 .read_timeout(config.timeout)
64 .connect_timeout(config.connect_timeout)
65 .build()
66 .map_err(|e| OpenAIError::Config(format!("failed to build HTTP client: {e}")))?;
67 Ok(Self {
68 inner: Arc::new(Inner { http, config }),
69 })
70 }
71
72 pub(crate) fn http(&self) -> &reqwest::Client {
73 &self.inner.http
74 }
75
76 pub(crate) fn config(&self) -> &Config {
77 &self.inner.config
78 }
79
80 pub fn base_url(&self) -> &str {
82 &self.inner.config.base_url
83 }
84
85 pub fn chat(&self) -> Chat {
87 Chat::new(self.clone())
88 }
89
90 pub fn embeddings(&self) -> Embeddings {
92 Embeddings::new(self.clone())
93 }
94
95 pub fn models(&self) -> Models {
97 Models::new(self.clone())
98 }
99
100 pub fn moderations(&self) -> Moderations {
102 Moderations::new(self.clone())
103 }
104
105 pub fn completions(&self) -> Completions {
107 Completions::new(self.clone())
108 }
109
110 pub fn images(&self) -> Images {
112 Images::new(self.clone())
113 }
114
115 pub fn files(&self) -> Files {
117 Files::new(self.clone())
118 }
119
120 pub fn audio(&self) -> Audio {
122 Audio::new(self.clone())
123 }
124
125 pub fn batches(&self) -> Batches {
127 Batches::new(self.clone())
128 }
129
130 pub fn uploads(&self) -> Uploads {
132 Uploads::new(self.clone())
133 }
134
135 pub fn fine_tuning(&self) -> FineTuning {
137 FineTuning::new(self.clone())
138 }
139
140 pub fn vector_stores(&self) -> VectorStores {
142 VectorStores::new(self.clone())
143 }
144
145 pub fn assistants(&self) -> Assistants {
147 Assistants::new(self.clone())
148 }
149
150 pub fn threads(&self) -> Threads {
153 Threads::new(self.clone())
154 }
155
156 pub fn responses(&self) -> Responses {
158 Responses::new(self.clone())
159 }
160
161 pub async fn connect_realtime(
168 &self,
169 model: &str,
170 ) -> Result<crate::realtime::RealtimeSession, crate::realtime::RealtimeError> {
171 let config = self.config();
172 if config.azure.is_some() {
173 return Err(crate::realtime::RealtimeError::Connect(
174 "Azure realtime is not supported; use realtime::connect with explicit options"
175 .into(),
176 ));
177 }
178 crate::realtime::connect(crate::realtime::RealtimeConnectOptions {
179 api_key: config.api_key.clone(),
180 base_url: config.base_url.clone(),
181 model: model.to_string(),
182 organization: config.organization.clone(),
183 project: config.project.clone(),
184 extra_headers: Vec::new(),
185 })
186 .await
187 }
188}