Watch
1
0
Fork
You've already forked souveraine
0

providers now per-agent: z.ai rides, bifrost stays home

Vanguard gets his own inference pipe. z.ai GLM Coding Plan, /paas/v4.
BifrostClient learned to leave versioned URLs alone. ProviderRegistry
dispatches per agent; every engine asks who it's talking to before
dialing. Annie and Hal none the wiser.
This commit is contained in:
Fimeg 2026-06-15 18:06:23 -04:00
commit 80bfb3a599
15 changed files with 326 additions and 57 deletions

View file

@ -37,6 +37,7 @@ use serde::{Deserialize, Serialize};
use crate::bridge::bifrost::{ChatCompletionRequest, Message};
use crate::bridge::LlmProvider;
use crate::bridge::ProviderRegistry;
use crate::core::config::{ArchivistConfig, SynthesisElement};
use crate::core::memory::MemoryRepo;
use crate::server::AgentInventory;
@ -95,7 +96,7 @@ impl SynthesisReport {
pub struct ArchivistEngine {
agents: Arc<AgentInventory>,
bifrost: Arc<dyn LlmProvider>,
providers: Arc<ProviderRegistry>,
rate_delay: Arc<AtomicU64>,
config: ArchivistConfig,
/// Subconscious model handle — used to resolve `compression_model: "auto"`.
@ -105,24 +106,35 @@ pub struct ArchivistEngine {
impl ArchivistEngine {
pub fn new(
agents: Arc<AgentInventory>,
bifrost: Arc<dyn LlmProvider>,
providers: Arc<ProviderRegistry>,
rate_delay: Arc<AtomicU64>,
config: ArchivistConfig,
subconscious_model: Option<String>,
) -> Self {
Self {
agents,
bifrost,
providers,
rate_delay,
config,
subconscious_model,
}
}
/// Resolve the compression model. `"auto"` (or empty) falls back to the
/// subconscious model, then to a sensible default. A real capability-aware
/// auto-selection algorithm is deferred (task Phase 4).
fn resolve_model(&self) -> String {
/// Resolve the compression model. The agent's own
/// `_souveraine.archivist_model` override wins outright. Otherwise the
/// configured `compression_model` is used, with `"auto"` (or empty)
/// falling back to the subconscious model, then a sensible default.
/// A real capability-aware auto-selection algorithm is deferred.
async fn resolve_model(&self, agent_id: &str) -> String {
if let Some(per_agent) = self
.agents
.get(agent_id)
.await
.ok()
.and_then(|a| a.souveraine.archivist_model.clone())
{
return per_agent;
}
let configured = self.config.compression_model.trim();
if configured.is_empty() || configured.eq_ignore_ascii_case("auto") {
self.subconscious_model
@ -197,9 +209,16 @@ impl ArchivistEngine {
let raw = format_journal_input(&entries);
let approx_tokens_compressed = raw.len() / 4;
let model = self.resolve_model();
let model = self.resolve_model(agent_id).await;
let llm: Arc<dyn LlmProvider> = self
.agents
.get(agent_id)
.await
.ok()
.map(|a| self.providers.for_agent(&a))
.unwrap_or_else(|| self.providers.default_provider());
let synthesis = self
.run_synthesis(&model, &raw, start_date, end_date)
.run_synthesis(&llm, &model, &raw, start_date, end_date)
.await?;
let output_label = format!("{SYNTHESIS_DIR}/{end_date}");
@ -229,6 +248,7 @@ impl ArchivistEngine {
/// No tool loop — the Archivist produces a record, it doesn't act.
async fn run_synthesis(
&self,
llm: &Arc<dyn LlmProvider>,
model: &str,
raw_journal: &str,
start: NaiveDate,
@ -253,7 +273,7 @@ impl ArchivistEngine {
};
tracing::info!(model = %model, "archivist synthesis call starting");
let (response, strain) = self.bifrost.chat_completion_with_strain(request).await?;
let (response, strain) = llm.chat_completion_with_strain(request).await?;
for event in &strain {
if let crate::bridge::bifrost::InferenceStrain::Transient { status, model, .. } = event {