artificial_openai/adapter.rs
1use std::{env, sync::Arc};
2
3use artificial_core::error::{ArtificialError, Result};
4
5use crate::client::OpenAiClient;
6
7/// Thin wrapper that wires the HTTP client [`OpenAiClient`] into a value that
8/// implements [`artificial_core::backend::Backend`].
9///
10/// Think of it as the **service locator** for the OpenAI back-end:
11///
12/// * stores the API key (and optionally a custom base URL in the future),
13/// * owns a shareable, connection-pooled `reqwest::Client`,
14/// * provides a fluent [`OpenAiAdapterBuilder`] so callers don’t have to juggle
15/// `Option<String>` manually.
16///
17/// The type itself purposefully exposes **no additional methods**—all user-
18/// facing functionality sits on the generic [`artificial_core::ArtificialClient`]
19/// once the adapter is plugged in.
20pub struct OpenAiAdapter {
21 pub(crate) client: Arc<OpenAiClient>,
22}
23
24impl OpenAiAdapter {}
25
26/// Builder for [`OpenAiAdapter`].
27///
28/// # Typical usage
29///
30/// ```rust,no_run
31/// use artificial_openai::OpenAiAdapterBuilder;
32///
33/// let backend = OpenAiAdapterBuilder::new_from_env()
34/// .build()
35/// .expect("OPENAI_API_KEY must be set");
36/// ```
37///
38/// The builder pattern keeps future options (proxy URL, organisation ID, …)
39/// backwards compatible without breaking existing `build()` calls.
40#[derive(Default)]
41pub struct OpenAiAdapterBuilder {
42 pub(crate) api_key: Option<String>,
43}
44
45impl OpenAiAdapterBuilder {
46 /// Create an *empty* builder. Remember to supply an API key manually.
47 pub fn new() -> Self {
48 Self::default()
49 }
50
51 /// Convenience constructor that tries to load the `OPENAI_API_KEY`
52 /// environment variable.
53 ///
54 /// # Panics
55 ///
56 /// Never panics. Missing keys only surface during [`Self::build`].
57 pub fn new_from_env() -> Self {
58 Self {
59 api_key: env::var("OPENAI_API_KEY").ok(),
60 }
61 }
62
63 /// Finalise the builder and return a ready-to-use adapter.
64 ///
65 /// # Errors
66 ///
67 /// * [`ArtificialError::Invalid`] – if the API key is missing.
68 pub fn build(self) -> Result<OpenAiAdapter> {
69 Ok(OpenAiAdapter {
70 client: Arc::new(OpenAiClient::new(self.api_key.ok_or(
71 ArtificialError::Invalid("missing env variable: `OPENAI_API_KEY`".into()),
72 )?)),
73 })
74 }
75}