1mod api;
18mod client;
19
20use std::marker::PhantomData;
21
22use chat_core::types::provider_meta::ProviderMeta;
23
24pub use crate::client::CompletionsClient;
25pub use chat_core::error::{ChatError, ChatFailure};
26pub use chat_core::transport::{Request, ReqwestTransport, Response, Transport, TransportError};
27
28pub struct WithoutModel;
29pub struct WithModel;
30
31pub struct WithoutUrl;
32pub struct WithUrl;
33
34pub struct CompletionsBuilder<M = WithoutModel, U = WithoutUrl, T: Transport = ReqwestTransport> {
35 model_name: Option<String>,
36 api_key: Option<String>,
37 scheme: String,
38 host: String,
39 base_path: String,
40 extra_headers: Vec<(String, String)>,
41 transport: Option<T>,
42 meta: ProviderMeta,
43 _m: PhantomData<M>,
44 _u: PhantomData<U>,
45}
46
47impl Default for CompletionsBuilder<WithoutModel, WithoutUrl, ReqwestTransport> {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl CompletionsBuilder<WithoutModel, WithoutUrl, ReqwestTransport> {
54 pub fn new() -> Self {
55 Self {
56 model_name: None,
57 api_key: None,
58 scheme: String::new(),
59 host: String::new(),
60 base_path: String::new(),
61 extra_headers: Vec::new(),
62 transport: Some(ReqwestTransport::default()),
63 meta: ProviderMeta::default(),
64 _m: PhantomData,
65 _u: PhantomData,
66 }
67 }
68}
69
70impl<M, U, T: Transport> CompletionsBuilder<M, U, T> {
71 pub fn with_api_key(mut self, api_key: impl Into<String>) -> Self {
72 self.api_key = Some(api_key.into());
73 self
74 }
75
76 pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
77 self.extra_headers.push((key.into(), value.into()));
78 self
79 }
80
81 pub fn with_description(mut self, description: impl Into<String>) -> Self {
82 self.meta.description = Some(description.into());
83 self
84 }
85
86 pub fn with_metadata(
87 mut self,
88 key: impl Into<String>,
89 value: impl std::any::Any + Send + Sync + 'static,
90 ) -> Self {
91 self.meta.data.insert(key.into(), Box::new(value));
92 self
93 }
94
95 pub fn with_transport<T2: Transport>(self, transport: T2) -> CompletionsBuilder<M, U, T2> {
97 CompletionsBuilder {
98 model_name: self.model_name,
99 api_key: self.api_key,
100 scheme: self.scheme,
101 host: self.host,
102 base_path: self.base_path,
103 extra_headers: self.extra_headers,
104 transport: Some(transport),
105 meta: self.meta,
106 _m: PhantomData,
107 _u: PhantomData,
108 }
109 }
110}
111
112impl<U, T: Transport> CompletionsBuilder<WithoutModel, U, T> {
113 pub fn with_model(self, model_name: impl Into<String>) -> CompletionsBuilder<WithModel, U, T> {
114 CompletionsBuilder {
115 model_name: Some(model_name.into()),
116 api_key: self.api_key,
117 scheme: self.scheme,
118 host: self.host,
119 base_path: self.base_path,
120 extra_headers: self.extra_headers,
121 transport: self.transport,
122 meta: self.meta,
123 _m: PhantomData,
124 _u: PhantomData,
125 }
126 }
127}
128
129impl<M, T: Transport> CompletionsBuilder<M, WithoutUrl, T> {
130 pub fn with_base_url(self, base_url: impl AsRef<str>) -> CompletionsBuilder<M, WithUrl, T> {
136 let parsed = url::Url::parse(base_url.as_ref()).expect("Invalid base URL");
137 let scheme = parsed.scheme().to_string();
138 let host = parsed
139 .host_str()
140 .expect("base URL missing host")
141 .to_string()
142 + &parsed.port().map(|p| format!(":{p}")).unwrap_or_default();
143 let base_path = parsed.path().trim_end_matches('/').to_string();
144
145 CompletionsBuilder {
146 model_name: self.model_name,
147 api_key: self.api_key,
148 scheme,
149 host,
150 base_path,
151 extra_headers: self.extra_headers,
152 transport: self.transport,
153 meta: self.meta,
154 _m: PhantomData,
155 _u: PhantomData,
156 }
157 }
158}
159
160impl<T: Transport> CompletionsBuilder<WithModel, WithUrl, T> {
161 pub fn build(self) -> CompletionsClient<T> {
165 let transport = self.transport.expect(
166 "No transport provided. Call .with_transport() or rely on the default ReqwestTransport.",
167 );
168
169 CompletionsClient {
170 model_name: self.model_name.unwrap(),
171 api_key: self.api_key,
172 scheme: self.scheme,
173 host: self.host,
174 base_path: self.base_path,
175 extra_headers: self.extra_headers,
176 transport,
177 meta: self.meta,
178 }
179 }
180}