1use std::collections::HashMap;
4use std::sync::{Arc, OnceLock};
5use std::time::Duration;
6
7use async_openai::config::OpenAIConfig;
8use async_openai::Client as OpenAIClient;
9
10use crate::constants::{DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT};
11use crate::error::Result;
12use crate::http::{HttpConfig, HttpTransport};
13use crate::openai_delegate::new_openai_delegate;
14use crate::options::FloopyOptions;
15use crate::resources::{
16 Batches, Constraints, Decisions, Evaluations, Experiments, Export, Feedback, Files, Routing,
17 Sessions,
18};
19
20#[derive(Clone)]
26pub struct Floopy {
27 transport: Arc<HttpTransport>,
28 openai: Arc<OnceLock<OpenAIClient<OpenAIConfig>>>,
29}
30
31impl Floopy {
32 pub fn new(api_key: impl Into<String>) -> Result<Self> {
39 Self::builder(api_key).build()
40 }
41
42 #[must_use]
45 pub fn builder(api_key: impl Into<String>) -> FloopyBuilder {
46 FloopyBuilder::new(api_key)
47 }
48
49 #[must_use]
57 pub fn openai(&self) -> &OpenAIClient<OpenAIConfig> {
58 self.openai.get_or_init(|| {
59 new_openai_delegate(&self.transport)
60 .expect("delegate construction with SDK-produced headers is infallible")
61 })
62 }
63
64 #[must_use]
66 pub fn base_url(&self) -> &str {
67 self.transport.base_url()
68 }
69
70 #[must_use]
72 pub fn feedback(&self) -> Feedback {
73 Feedback::new(self.transport.clone())
74 }
75
76 #[must_use]
78 pub fn decisions(&self) -> Decisions {
79 Decisions::new(self.transport.clone())
80 }
81
82 #[must_use]
84 pub fn experiments(&self) -> Experiments {
85 Experiments::new(self.transport.clone())
86 }
87
88 #[must_use]
90 pub fn constraints(&self) -> Constraints {
91 Constraints::new(self.transport.clone())
92 }
93
94 #[must_use]
96 pub fn export(&self) -> Export {
97 Export::new(self.transport.clone())
98 }
99
100 #[must_use]
102 pub fn files(&self) -> Files {
103 Files::new(self.transport.clone())
104 }
105
106 #[must_use]
108 pub fn batches(&self) -> Batches {
109 Batches::new(self.transport.clone())
110 }
111
112 #[must_use]
114 pub fn evaluations(&self) -> Evaluations {
115 Evaluations::new(self.transport.clone())
116 }
117
118 #[must_use]
120 pub fn routing(&self) -> Routing {
121 Routing::new(self.transport.clone())
122 }
123
124 #[must_use]
126 pub fn sessions(&self) -> Sessions {
127 Sessions::new(self.transport.clone())
128 }
129}
130
131impl std::fmt::Debug for Floopy {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 f.debug_struct("Floopy")
135 .field("base_url", &self.transport.base_url())
136 .finish_non_exhaustive()
137 }
138}
139
140pub struct FloopyBuilder {
142 api_key: String,
143 base_url: String,
144 timeout: Duration,
145 max_retries: u32,
146 default_headers: HashMap<String, String>,
147 options: Option<FloopyOptions>,
148 http_client: Option<reqwest::Client>,
149}
150
151impl FloopyBuilder {
152 fn new(api_key: impl Into<String>) -> Self {
153 Self {
154 api_key: api_key.into(),
155 base_url: DEFAULT_BASE_URL.to_owned(),
156 timeout: DEFAULT_TIMEOUT,
157 max_retries: DEFAULT_MAX_RETRIES,
158 default_headers: HashMap::new(),
159 options: None,
160 http_client: None,
161 }
162 }
163
164 #[must_use]
169 pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
170 self.base_url = base_url.into();
171 self
172 }
173
174 #[must_use]
176 pub fn timeout(mut self, timeout: Duration) -> Self {
177 self.timeout = timeout;
178 self
179 }
180
181 #[must_use]
183 pub fn max_retries(mut self, max_retries: u32) -> Self {
184 self.max_retries = max_retries;
185 self
186 }
187
188 #[must_use]
190 pub fn default_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
191 self.default_headers.insert(key.into(), value.into());
192 self
193 }
194
195 #[must_use]
197 pub fn options(mut self, options: FloopyOptions) -> Self {
198 self.options = Some(options);
199 self
200 }
201
202 #[must_use]
204 pub fn http_client(mut self, client: reqwest::Client) -> Self {
205 self.http_client = Some(client);
206 self
207 }
208
209 pub fn build(self) -> Result<Floopy> {
216 let transport = HttpTransport::new(HttpConfig {
217 api_key: self.api_key,
218 base_url: self.base_url,
219 timeout: self.timeout,
220 max_retries: self.max_retries,
221 default_headers: self.default_headers,
222 default_options: self.options,
223 http_client: self.http_client,
224 })?;
225 Ok(Floopy {
226 transport: Arc::new(transport),
227 openai: Arc::new(OnceLock::new()),
228 })
229 }
230}