Skip to main content

talon_core/runtime/
clients.rs

1//! HTTP clients built from [`TalonConfig`].
2
3use crate::config::TalonConfig;
4use crate::error::TalonError;
5use crate::expansion::ExpansionClient;
6use crate::inference::{EmbeddingClient, InferenceError, RerankClient};
7use crate::llm::ChatClient;
8
9/// Runtime HTTP clients for search, sync, and recall.
10#[derive(Debug, Clone)]
11pub struct TalonClients {
12    /// Embedding endpoint client.
13    pub embedding: EmbeddingClient,
14    /// Rerank endpoint client.
15    pub rerank: RerankClient,
16    /// Query expansion chat client.
17    pub expansion: ExpansionClient,
18}
19
20impl TalonClients {
21    /// Builds all runtime clients from config.
22    ///
23    /// # Errors
24    ///
25    /// Returns [`TalonError::Config`] when auth resolution fails.
26    /// Returns [`TalonError::Internal`] when an HTTP client cannot be built.
27    pub fn from_config(config: &TalonConfig) -> Result<Self, TalonError> {
28        let embedding = EmbeddingClient::from_config(&config.embedding, &config.credentials)
29            .map_err(map_inference_build)?;
30        let rerank = RerankClient::from_config(
31            &config.rerank,
32            &config.credentials,
33            config.search.rerank_batch_size,
34        )
35        .map_err(map_inference_build)?;
36        let expansion = build_expansion_client(config).map_err(map_inference_build)?;
37        Ok(Self {
38            embedding,
39            rerank,
40            expansion,
41        })
42    }
43}
44
45/// Builds the expansion chat client from `[chat.expansion]`.
46///
47/// # Errors
48///
49/// Returns [`InferenceError`] when auth resolution or client construction fails.
50pub fn build_expansion_client(config: &TalonConfig) -> Result<ExpansionClient, InferenceError> {
51    let chat = build_chat_client(
52        &config.chat.expansion.base_url,
53        &config.chat.expansion.auth,
54        &config.credentials,
55        &config.chat.expansion.model,
56        config.chat.expansion.max_output_tokens,
57    )?;
58    Ok(ExpansionClient::from_chat(chat))
59}
60
61/// Builds an ask-stage chat client, inheriting transport defaults from expansion.
62///
63/// # Errors
64///
65/// Returns [`InferenceError`] when auth resolution or client construction fails.
66pub fn build_ask_chat_client(
67    config: &TalonConfig,
68    model: &str,
69    max_tokens: Option<u32>,
70) -> Result<ChatClient, InferenceError> {
71    let ask = &config.chat.ask;
72    let expansion = &config.chat.expansion;
73    build_chat_client(
74        ask.resolved_base_url(expansion),
75        &ask.resolved_auth(expansion),
76        &config.credentials,
77        model,
78        max_tokens,
79    )
80}
81
82fn build_chat_client(
83    base_url: &str,
84    auth: &crate::config::EndpointAuthConfig,
85    credentials: &crate::config::CredentialsConfig,
86    model: &str,
87    max_tokens: Option<u32>,
88) -> Result<ChatClient, InferenceError> {
89    let resolved = auth
90        .resolve(credentials)
91        .map_err(|err| InferenceError::Config {
92            message: err.to_string(),
93        })?;
94    ChatClient::with_timeout_max_tokens_and_auth(
95        base_url,
96        model,
97        crate::expansion::client::DEFAULT_EXPANSION_TIMEOUT,
98        max_tokens,
99        resolved,
100    )
101    .map_err(|err| InferenceError::Build {
102        message: err.to_string(),
103    })
104}
105
106fn map_inference_build(err: InferenceError) -> TalonError {
107    match err {
108        InferenceError::Config { message } => TalonError::Config { message },
109        other => TalonError::Internal {
110            message: other.to_string(),
111        },
112    }
113}