Skip to main content

typesafe/
blocking.rs

1//! Synchronous client (feature `blocking`). Each client owns a private current-thread Tokio
2//! runtime, shared with its clones; do not call it from inside an async context.
3
4use std::sync::Arc;
5use std::time::Duration;
6
7use http::header::{HeaderName, HeaderValue};
8use serde::Serialize;
9use serde_json::Value;
10
11use crate::error::{Error, Result};
12use crate::question::Questions;
13use crate::response::{ListModelsResponse, SystemOneResponse};
14use crate::retry::RetryPolicy;
15use crate::rubric::Rubric;
16
17/// Blocking TypeSafe client. Cheap to clone; clones share the runtime and the connection pool.
18#[derive(Debug, Clone)]
19pub struct Client {
20    inner: crate::Client,
21    rt: Arc<tokio::runtime::Runtime>,
22}
23
24impl Client {
25    /// Wrap an async client configured via [`crate::Client::builder`] (or build one with
26    /// [`ClientBuilder::build_blocking`](crate::ClientBuilder::build_blocking)).
27    ///
28    /// # Errors
29    ///
30    /// [`Error::Config`], with the [`std::io::Error`] as its `source()`, if the runtime cannot be
31    /// started.
32    pub fn new(inner: crate::Client) -> Result<Self> {
33        let rt = tokio::runtime::Builder::new_current_thread()
34            .enable_all()
35            .build()
36            .map_err(|e| Error::config_caused("could not start the async runtime", e))?;
37        Ok(Self {
38            inner,
39            rt: Arc::new(rt),
40        })
41    }
42
43    /// A client configured entirely from the environment.
44    ///
45    /// # Errors
46    ///
47    /// [`Error::Config`] as for [`crate::Client::from_env`], or if the runtime cannot be started.
48    pub fn from_env() -> Result<Self> {
49        Self::new(crate::Client::from_env()?)
50    }
51
52    /// The default model.
53    pub fn default_model(&self) -> &str {
54        self.inner.default_model()
55    }
56
57    /// The Models resource; see [`crate::Client::models`].
58    pub fn models(&self) -> Models<'_> {
59        Models { client: self }
60    }
61
62    /// See [`crate::Client::system_one`].
63    pub fn system_one<S: Serialize>(
64        &self,
65        state: S,
66        questions: impl Into<Questions>,
67    ) -> SystemOneRequest<'_> {
68        SystemOneRequest {
69            rt: &self.rt,
70            req: self.inner.system_one(state, questions),
71        }
72    }
73
74    /// See [`crate::Client::ask`].
75    pub fn ask<R: Rubric>(&self, state: impl Serialize) -> AskRequest<'_, R> {
76        AskRequest {
77            rt: &self.rt,
78            req: self.inner.ask(state),
79        }
80    }
81}
82
83/// The blocking Models resource.
84#[derive(Debug, Clone, Copy)]
85pub struct Models<'a> {
86    client: &'a Client,
87}
88
89impl<'a> Models<'a> {
90    /// `GET /v1/models`.
91    pub fn list(&self) -> ListModelsRequest<'a> {
92        ListModelsRequest {
93            rt: &self.client.rt,
94            req: self.client.inner.models().list(),
95        }
96    }
97}
98
99macro_rules! forward {
100    ($($name:ident($($arg:ident: $ty:ty),*)),* $(,)?) => {$(
101        #[doc = concat!("See the async request's `", stringify!($name), "`.")]
102        pub fn $name(mut self, $($arg: $ty),*) -> Self {
103            self.req = self.req.$name($($arg),*);
104            self
105        }
106    )*};
107}
108
109/// Blocking `POST /v1/systemone`.
110#[must_use = "call .send()"]
111#[derive(Debug)]
112pub struct SystemOneRequest<'a> {
113    rt: &'a tokio::runtime::Runtime,
114    req: crate::SystemOneRequest,
115}
116
117impl SystemOneRequest<'_> {
118    forward!(
119        retry(policy: RetryPolicy),
120        timeout(timeout: Duration),
121        header(name: HeaderName, value: HeaderValue),
122    );
123
124    /// Override the model for this call.
125    pub fn model(mut self, model: impl Into<String>) -> Self {
126        self.req = self.req.model(model);
127        self
128    }
129
130    /// Add a top-level body field.
131    pub fn extra_body(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
132        self.req = self.req.extra_body(key, value);
133        self
134    }
135
136    /// Send and wait.
137    ///
138    /// # Errors
139    ///
140    /// As for the async request's `send`.
141    ///
142    /// # Panics
143    ///
144    /// When called from inside an async runtime.
145    pub fn send(self) -> Result<SystemOneResponse> {
146        self.rt.block_on(self.req.send())
147    }
148}
149
150/// Blocking [`crate::Client::ask`].
151#[must_use = "call .send()"]
152#[derive(Debug)]
153pub struct AskRequest<'a, R> {
154    rt: &'a tokio::runtime::Runtime,
155    req: crate::AskRequest<R>,
156}
157
158impl<R: Rubric> AskRequest<'_, R> {
159    forward!(
160        retry(policy: RetryPolicy),
161        timeout(timeout: Duration),
162        header(name: HeaderName, value: HeaderValue),
163    );
164
165    /// Override the model for this call.
166    pub fn model(mut self, model: impl Into<String>) -> Self {
167        self.req = self.req.model(model);
168        self
169    }
170
171    /// Add a top-level body field.
172    pub fn extra_body(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
173        self.req = self.req.extra_body(key, value);
174        self
175    }
176
177    /// Send, wait, and decode the answers.
178    ///
179    /// # Errors
180    ///
181    /// As for the async request's `send`.
182    ///
183    /// # Panics
184    ///
185    /// When called from inside an async runtime.
186    pub fn send(self) -> Result<R> {
187        self.rt.block_on(self.req.send())
188    }
189}
190
191/// Blocking `GET /v1/models`.
192#[must_use = "call .send()"]
193#[derive(Debug)]
194pub struct ListModelsRequest<'a> {
195    rt: &'a tokio::runtime::Runtime,
196    req: crate::ListModelsRequest,
197}
198
199impl ListModelsRequest<'_> {
200    forward!(
201        retry(policy: RetryPolicy),
202        timeout(timeout: Duration),
203        header(name: HeaderName, value: HeaderValue),
204    );
205
206    /// Send and wait.
207    ///
208    /// # Errors
209    ///
210    /// As for the async request's `send`.
211    ///
212    /// # Panics
213    ///
214    /// When called from inside an async runtime.
215    pub fn send(self) -> Result<ListModelsResponse> {
216        self.rt.block_on(self.req.send())
217    }
218}