Watch
1
0
Fork
You've already forked souveraine
0
souveraine/src/bridge/mod.rs
Fimeg d35370b9c9 merge fork work onto public: cockpit/portrait/sidebar widgets, select_list, expression_cache, sensorium+settings rework
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.
2026-06-19 10:10:13 -04:00

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
}
}