Skip to main content

rig_core/providers/together/
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// Together AI Client
11// ================================================================
12const TOGETHER_AI_BASE_URL: &str = "https://api.together.xyz";
13
14#[derive(Debug, Default, Clone, Copy)]
15pub struct TogetherExt;
16#[derive(Debug, Default, Clone, Copy)]
17pub struct TogetherExtBuilder;
18
19type TogetherApiKey = BearerAuth;
20
21pub type Client<H = reqwest::Client> = client::Client<TogetherExt, H>;
22pub type ClientBuilder<H = crate::markers::Missing> =
23    client::ClientBuilder<TogetherExtBuilder, TogetherApiKey, H>;
24
25impl Provider for TogetherExt {
26    type Builder = TogetherExtBuilder;
27
28    const VERIFY_PATH: &'static str = "/models";
29}
30
31impl DebugExt for TogetherExt {}
32
33impl crate::providers::openai::completion::OpenAICompatibleProvider for TogetherExt {
34    const PROVIDER_NAME: &'static str = "together";
35
36    type StreamingUsage = crate::providers::openai::Usage;
37
38    // Together's structured-output support is model-dependent; keep the
39    // pre-migration behavior of dropping `output_schema` with a warning.
40    const SUPPORTS_RESPONSE_FORMAT: bool = false;
41
42    type Response = crate::providers::openai::CompletionResponse;
43
44    // The client base URL is the bare host; embeddings build their own v1 path.
45    fn completion_path(&self, _model: &str) -> String {
46        "/v1/chat/completions".to_string()
47    }
48}
49
50impl<H> Capabilities<H> for TogetherExt {
51    type Completion = Capable<super::CompletionModel<H>>;
52    type Embeddings = Capable<super::EmbeddingModel<H>>;
53
54    type Transcription = Nothing;
55    type ModelListing = Nothing;
56    #[cfg(feature = "image")]
57    type ImageGeneration = Nothing;
58    #[cfg(feature = "audio")]
59    type AudioGeneration = Nothing;
60    type Rerank = Nothing;
61}
62
63impl ProviderBuilder for TogetherExtBuilder {
64    type Extension<H>
65        = TogetherExt
66    where
67        H: http_client::HttpClientExt;
68    type ApiKey = TogetherApiKey;
69
70    const BASE_URL: &'static str = TOGETHER_AI_BASE_URL;
71
72    fn build<H>(
73        _builder: &client::ClientBuilder<Self, Self::ApiKey, H>,
74    ) -> http_client::Result<Self::Extension<H>>
75    where
76        H: http_client::HttpClientExt,
77    {
78        Ok(TogetherExt)
79    }
80}
81
82impl ProviderClient for Client {
83    type Input = String;
84    type Error = crate::client::ProviderClientError;
85
86    /// Create a new Together AI client from the `TOGETHER_API_KEY` environment variable.
87    fn from_env() -> Result<Self, Self::Error> {
88        let api_key = crate::client::required_env_var("TOGETHER_API_KEY")?;
89        Self::new(&api_key).map_err(Into::into)
90    }
91
92    fn from_val(input: Self::Input) -> Result<Self, Self::Error> {
93        Self::new(&input).map_err(Into::into)
94    }
95}
96
97pub mod together_ai_api_types {
98    use serde::Deserialize;
99
100    impl ApiErrorResponse {
101        pub fn message(&self) -> String {
102            format!("Code `{}`: {}", self.code, self.error)
103        }
104    }
105
106    #[derive(Debug, Deserialize)]
107    pub struct ApiErrorResponse {
108        pub error: String,
109        pub code: String,
110    }
111
112    #[derive(Debug, Deserialize)]
113    #[serde(untagged)]
114    pub enum ApiResponse<T> {
115        Ok(T),
116        Error(ApiErrorResponse),
117    }
118}
119#[cfg(test)]
120mod tests {
121    #[test]
122    fn test_client_initialization() {
123        let _client =
124            crate::providers::together::Client::new("dummy-key").expect("Client::new() failed");
125        let _client_from_builder = crate::providers::together::Client::builder()
126            .api_key("dummy-key")
127            .build()
128            .expect("Client::builder() failed");
129    }
130}