Skip to main content

rig_core/providers/doubleword/
client.rs

1use crate::{
2    client::{
3        self, BearerAuth, Capabilities, Capable, DebugExt, Nothing, Provider, ProviderBuilder,
4        ProviderClient,
5    },
6    http_client,
7};
8
9// ================================================================
10// Doubleword Client
11// ================================================================
12// Base URL carries the `/v1`, so request paths are bare (`/chat/completions`).
13const DOUBLEWORD_API_BASE_URL: &str = "https://api.doubleword.ai/v1";
14
15#[derive(Debug, Default, Clone, Copy)]
16pub struct DoublewordExt;
17#[derive(Debug, Default, Clone, Copy)]
18pub struct DoublewordExtBuilder;
19
20type DoublewordApiKey = BearerAuth;
21
22pub type Client<H = reqwest::Client> = client::Client<DoublewordExt, H>;
23pub type ClientBuilder<H = crate::markers::Missing> =
24    client::ClientBuilder<DoublewordExtBuilder, DoublewordApiKey, H>;
25
26impl Provider for DoublewordExt {
27    type Builder = DoublewordExtBuilder;
28
29    const VERIFY_PATH: &'static str = "/models";
30}
31
32impl DebugExt for DoublewordExt {}
33
34impl crate::providers::openai::completion::OpenAICompatibleProvider for DoublewordExt {
35    const PROVIDER_NAME: &'static str = "doubleword";
36
37    type StreamingUsage = crate::providers::openai::Usage;
38    type Response = crate::providers::openai::CompletionResponse;
39}
40
41impl<H> Capabilities<H> for DoublewordExt {
42    type Completion = Capable<super::completion::CompletionModel<H>>;
43    type Embeddings = Capable<super::EmbeddingModel<H>>;
44
45    type Transcription = Nothing;
46    type ModelListing = Nothing;
47    #[cfg(feature = "image")]
48    type ImageGeneration = Nothing;
49    #[cfg(feature = "audio")]
50    type AudioGeneration = Nothing;
51    type Rerank = Nothing;
52}
53
54impl ProviderBuilder for DoublewordExtBuilder {
55    type Extension<H>
56        = DoublewordExt
57    where
58        H: http_client::HttpClientExt;
59    type ApiKey = DoublewordApiKey;
60
61    const BASE_URL: &'static str = DOUBLEWORD_API_BASE_URL;
62
63    fn build<H>(
64        _builder: &client::ClientBuilder<Self, Self::ApiKey, H>,
65    ) -> http_client::Result<Self::Extension<H>>
66    where
67        H: http_client::HttpClientExt,
68    {
69        Ok(DoublewordExt)
70    }
71}
72
73impl ProviderClient for Client {
74    type Input = String;
75    type Error = crate::client::ProviderClientError;
76
77    /// Create a new Doubleword client from the `DOUBLEWORD_API_KEY` environment
78    /// variable. The base URL can optionally be overridden with
79    /// `DOUBLEWORD_BASE_URL` (defaults to `https://api.doubleword.ai/v1`).
80    fn from_env() -> Result<Self, Self::Error> {
81        let base_url = crate::client::optional_env_var("DOUBLEWORD_BASE_URL")?;
82        let api_key = crate::client::required_env_var("DOUBLEWORD_API_KEY")?;
83
84        let mut builder = Client::builder().api_key(&api_key);
85
86        if let Some(base) = base_url {
87            builder = builder.base_url(&base);
88        }
89
90        builder.build().map_err(Into::into)
91    }
92
93    fn from_val(input: Self::Input) -> Result<Self, Self::Error> {
94        Self::new(&input).map_err(Into::into)
95    }
96}
97
98pub mod doubleword_api_types {
99    use serde::Deserialize;
100
101    impl ApiErrorResponse {
102        pub fn message(&self) -> String {
103            self.error.message.clone()
104        }
105    }
106
107    #[derive(Debug, Deserialize)]
108    pub struct ApiErrorResponse {
109        pub error: ApiError,
110    }
111
112    #[derive(Debug, Deserialize)]
113    pub struct ApiError {
114        pub message: String,
115        #[serde(default)]
116        pub r#type: Option<String>,
117        #[serde(default)]
118        pub code: Option<String>,
119    }
120
121    #[derive(Debug, Deserialize)]
122    #[serde(untagged)]
123    pub enum ApiResponse<T> {
124        Ok(T),
125        Error(ApiErrorResponse),
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    #[test]
132    fn test_client_initialization() {
133        let _client =
134            crate::providers::doubleword::Client::new("dummy-key").expect("Client::new() failed");
135        let _client_from_builder = crate::providers::doubleword::Client::builder()
136            .api_key("dummy-key")
137            .build()
138            .expect("Client::builder() failed");
139    }
140}