new TUI work that had been living in a detached re-init'd checkout, replayed onto the public lineage. keeps CI + skills. src-only; local scaffolding stays local.
140 lines
4.6 KiB
Rust
140 lines
4.6 KiB
Rust
/// Bridge module — LLM inference providers.
|
|
///
|
|
/// Connects Souveraine to inference providers behind the [`LlmProvider`] trait.
|
|
/// `BifrostClient` speaks to any OpenAI-compatible gateway; the OAuth-riding
|
|
/// ChatGPT provider lives in [`providers`]. [`build_provider`] selects the
|
|
/// active one from config; [`ProviderRegistry`] resolves a provider per agent.
|
|
pub mod bifrost;
|
|
pub mod model_router;
|
|
pub mod oauth;
|
|
pub mod provider;
|
|
pub mod providers;
|
|
|
|
pub use bifrost::BifrostClient;
|
|
pub use provider::LlmProvider;
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
|
|
use crate::api::models::AgentState;
|
|
use crate::core::config::{ConsciousnessConfig, ProviderConfig};
|
|
|
|
/// Build the active (global default) LLM provider from config.
|
|
///
|
|
/// Convenience wrapper that builds the full registry and returns its default.
|
|
/// For inference, prefer [`build_registry`] + [`ProviderRegistry::for_agent`].
|
|
pub fn build_provider(config: &ConsciousnessConfig) -> anyhow::Result<Arc<dyn LlmProvider>> {
|
|
let reg = build_registry(config)?;
|
|
Ok(reg.default_provider())
|
|
}
|
|
|
|
/// Build a provider from a [`ProviderConfig`] entry, dispatching on `provider_type`.
|
|
pub fn build_provider_from_config(
|
|
name: &str,
|
|
cfg: &ProviderConfig,
|
|
) -> anyhow::Result<Arc<dyn LlmProvider>> {
|
|
match cfg.provider_type.as_str() {
|
|
"openai-oauth" => {
|
|
let provider = providers::openai_oauth::OpenAiOAuthProvider::from_codex_login(
|
|
cfg.primary_model.clone(),
|
|
cfg.timeout_secs,
|
|
)?;
|
|
Ok(Arc::new(provider))
|
|
}
|
|
_ => {
|
|
// "openai-compatible" and any unrecognized type → OpenAI-compatible gateway.
|
|
let client = BifrostClient::new(
|
|
&cfg.base_url,
|
|
&cfg.api_key,
|
|
&cfg.virtual_key,
|
|
&cfg.primary_model,
|
|
cfg.timeout_secs,
|
|
)?
|
|
.with_id(name);
|
|
Ok(Arc::new(client))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Build the per-agent provider registry from the unified `[providers]` map.
|
|
///
|
|
/// Iterates every entry in `config.providers`, builds a provider for each, and
|
|
/// resolves the default from `[inference] provider`. Returns an error if no
|
|
/// providers are configured.
|
|
pub fn build_registry(config: &ConsciousnessConfig) -> anyhow::Result<ProviderRegistry> {
|
|
let mut map = HashMap::new();
|
|
|
|
for (name, pcfg) in &config.providers {
|
|
let provider = build_provider_from_config(name, pcfg)?;
|
|
map.insert(name.clone(), provider);
|
|
}
|
|
|
|
if map.is_empty() {
|
|
anyhow::bail!(
|
|
"no providers configured — add at least one [providers.<name>] section"
|
|
);
|
|
}
|
|
|
|
let default_name = config.inference.provider.clone();
|
|
let default = map
|
|
.get(&default_name)
|
|
.cloned()
|
|
.unwrap_or_else(|| map.values().next().unwrap().clone());
|
|
|
|
Ok(ProviderRegistry {
|
|
map,
|
|
default_name,
|
|
default,
|
|
})
|
|
}
|
|
|
|
/// Maps a provider name to a live [`LlmProvider`], resolving per agent.
|
|
#[derive(Clone)]
|
|
pub struct ProviderRegistry {
|
|
map: HashMap<String, Arc<dyn LlmProvider>>,
|
|
default_name: String,
|
|
default: Arc<dyn LlmProvider>,
|
|
}
|
|
|
|
impl std::fmt::Debug for ProviderRegistry {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("ProviderRegistry")
|
|
.field("providers", &self.map.keys().collect::<Vec<_>>())
|
|
.field("default", &self.default_name)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl ProviderRegistry {
|
|
/// Resolve the provider for an agent: its `_souveraine.provider` override,
|
|
/// else the global default. An unknown/missing name falls back to the
|
|
/// default rather than failing.
|
|
pub fn for_agent(&self, agent: &AgentState) -> Arc<dyn LlmProvider> {
|
|
match agent.souveraine.provider.as_deref() {
|
|
Some(name) if name != self.default_name => {
|
|
self.map.get(name).cloned().unwrap_or_else(|| self.default.clone())
|
|
}
|
|
_ => self.default.clone(),
|
|
}
|
|
}
|
|
|
|
/// All registered provider names.
|
|
pub fn provider_names(&self) -> Vec<&str> {
|
|
self.map.keys().map(|s| s.as_str()).collect()
|
|
}
|
|
|
|
/// Look up a provider by name. Returns `None` for unknown names.
|
|
pub fn get(&self, name: &str) -> Option<Arc<dyn LlmProvider>> {
|
|
self.map.get(name).cloned()
|
|
}
|
|
|
|
/// The global default provider (for contexts with no agent).
|
|
pub fn default_provider(&self) -> Arc<dyn LlmProvider> {
|
|
self.default.clone()
|
|
}
|
|
|
|
/// Name of the global default provider.
|
|
pub fn default_name(&self) -> &str {
|
|
&self.default_name
|
|
}
|
|
}
|