use foundation_db::traits::DocumentStore;
use crate::agentic::{AgentSession, AgentSessionBuilder, MemoryStore};
use crate::types::{
ModelId, ModelProvider, ProviderRouter, RoutableProvider, RoutableProviderBox, RoutingRule,
SessionId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Role {
Primary,
Memory,
Fallback,
}
#[derive(Default)]
pub struct RouterMix {
providers: Vec<Box<dyn RoutableProvider>>,
rules: Vec<RoutingRule>,
primary: Option<ModelId>,
memory: Option<ModelId>,
fallbacks: Vec<ModelId>,
}
impl RouterMix {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn primary<P>(self, provider: P, model: impl Into<ModelId>) -> Self
where
P: ModelProvider + Send + Sync + 'static,
P::Model: Send + Sync,
{
self.add_role(Role::Primary, provider, model.into())
}
#[must_use]
pub fn memory<P>(self, provider: P, model: impl Into<ModelId>) -> Self
where
P: ModelProvider + Send + Sync + 'static,
P::Model: Send + Sync,
{
self.add_role(Role::Memory, provider, model.into())
}
#[must_use]
pub fn fallback<P>(self, provider: P, model: impl Into<ModelId>) -> Self
where
P: ModelProvider + Send + Sync + 'static,
P::Model: Send + Sync,
{
self.add_role(Role::Fallback, provider, model.into())
}
fn add_role<P>(mut self, role: Role, provider: P, model: ModelId) -> Self
where
P: ModelProvider + Send + Sync + 'static,
P::Model: Send + Sync,
{
let provider_id = provider
.describe()
.expect(
"provider must return a descriptor with a provider_id to be \
registered in a RouterMix — a provider without identity cannot be routed",
)
.provider;
let name = model.name().to_string();
let boxed: Box<dyn RoutableProvider> =
Box::new(RoutableProviderBox::with_identity(provider, name.clone(), provider_id));
self.providers.push(boxed);
self.rules.push(RoutingRule {
model: model.clone(),
provider_name: name,
});
match role {
Role::Primary => self.primary = Some(model),
Role::Memory => self.memory = Some(model),
Role::Fallback => self.fallbacks.push(model),
}
self
}
#[must_use]
pub fn build(self) -> RouterPreset {
let mut builder = ProviderRouter::builder();
for provider in self.providers {
builder = builder.add_provider(provider);
}
for rule in self.rules {
builder = builder.rule(rule);
}
RouterPreset {
router: builder.build(),
primary_model: self.primary,
memory_model: self.memory,
fallback_models: self.fallbacks,
}
}
}
pub struct RouterPreset {
pub router: ProviderRouter,
pub primary_model: Option<ModelId>,
pub memory_model: Option<ModelId>,
pub fallback_models: Vec<ModelId>,
}
impl RouterPreset {
#[must_use]
pub fn into_agent_builder(self) -> AgentSessionBuilder {
AgentSession::builder(self)
}
#[deprecated(
note = "use into_agent_builder().with_session_id(id).with_doc_store(doc_store).with_memory_store(memory_store)"
)]
#[must_use]
pub fn into_agent_builder_with_stores<D, M>(
self,
session_id: SessionId,
doc_store: D,
memory_store: M,
) -> AgentSessionBuilder<D, M>
where
D: DocumentStore + 'static,
M: MemoryStore + 'static,
{
self.into_agent_builder()
.with_session_id(session_id)
.with_doc_store(doc_store)
.with_memory_store(memory_store)
}
}
impl From<ProviderRouter> for RouterPreset {
fn from(router: ProviderRouter) -> Self {
RouterPreset {
router,
primary_model: None,
memory_model: None,
fallback_models: Vec::new(),
}
}
}
impl<P> From<P> for RouterPreset
where
P: ModelProvider + Send + Sync + 'static,
P::Model: Send + Sync,
{
fn from(provider: P) -> Self {
ProviderRouter::from(provider).into()
}
}