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::uploads::Uploads;
20use crate::resources::vector_stores::VectorStores;
21
22#[derive(Clone)]
27pub struct Client {
28 inner: Arc<Inner>,
29}
30
31struct Inner {
32 http: reqwest::Client,
33 config: Config,
34}
35
36impl std::fmt::Debug for Client {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 f.debug_struct("Client")
39 .field("config", &self.inner.config)
40 .finish()
41 }
42}
43
44impl Client {
45 pub fn new() -> Result<Self, OpenAIError> {
48 Self::builder().build()
49 }
50
51 pub fn builder() -> ClientBuilder {
53 ClientBuilder::new()
54 }
55
56 pub(crate) fn from_config(config: Config) -> Result<Self, OpenAIError> {
57 let http = reqwest::Client::builder()
62 .read_timeout(config.timeout)
63 .connect_timeout(config.connect_timeout)
64 .build()
65 .map_err(|e| OpenAIError::Config(format!("failed to build HTTP client: {e}")))?;
66 Ok(Self {
67 inner: Arc::new(Inner { http, config }),
68 })
69 }
70
71 pub(crate) fn http(&self) -> &reqwest::Client {
72 &self.inner.http
73 }
74
75 pub(crate) fn config(&self) -> &Config {
76 &self.inner.config
77 }
78
79 pub fn base_url(&self) -> &str {
81 &self.inner.config.base_url
82 }
83
84 pub fn chat(&self) -> Chat {
86 Chat::new(self.clone())
87 }
88
89 pub fn embeddings(&self) -> Embeddings {
91 Embeddings::new(self.clone())
92 }
93
94 pub fn models(&self) -> Models {
96 Models::new(self.clone())
97 }
98
99 pub fn moderations(&self) -> Moderations {
101 Moderations::new(self.clone())
102 }
103
104 pub fn completions(&self) -> Completions {
106 Completions::new(self.clone())
107 }
108
109 pub fn images(&self) -> Images {
111 Images::new(self.clone())
112 }
113
114 pub fn files(&self) -> Files {
116 Files::new(self.clone())
117 }
118
119 pub fn audio(&self) -> Audio {
121 Audio::new(self.clone())
122 }
123
124 pub fn batches(&self) -> Batches {
126 Batches::new(self.clone())
127 }
128
129 pub fn uploads(&self) -> Uploads {
131 Uploads::new(self.clone())
132 }
133
134 pub fn fine_tuning(&self) -> FineTuning {
136 FineTuning::new(self.clone())
137 }
138
139 pub fn vector_stores(&self) -> VectorStores {
141 VectorStores::new(self.clone())
142 }
143
144 pub fn assistants(&self) -> Assistants {
146 Assistants::new(self.clone())
147 }
148
149 pub fn threads(&self) -> Threads {
152 Threads::new(self.clone())
153 }
154
155 pub async fn connect_realtime(
162 &self,
163 model: &str,
164 ) -> Result<crate::realtime::RealtimeSession, crate::realtime::RealtimeError> {
165 let config = self.config();
166 if config.azure.is_some() {
167 return Err(crate::realtime::RealtimeError::Connect(
168 "Azure realtime is not supported; use realtime::connect with explicit options"
169 .into(),
170 ));
171 }
172 crate::realtime::connect(crate::realtime::RealtimeConnectOptions {
173 api_key: config.api_key.clone(),
174 base_url: config.base_url.clone(),
175 model: model.to_string(),
176 organization: config.organization.clone(),
177 project: config.project.clone(),
178 extra_headers: Vec::new(),
179 })
180 .await
181 }
182}