Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ members = [
"crates/infrastructure/db-migration",
"crates/infrastructure/models-catalog",
"crates/infrastructure/combo-sqlite",
"crates/infrastructure/alias-sqlite",
"apps/rook",
]

Expand Down
1 change: 1 addition & 0 deletions apps/rook/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ providers-groq = { path = "../../crates/infrastructure/providers-groq" }
encryption-inmemory = { path = "../../crates/infrastructure/encryption-inmemory" }
provider-sqlite = { path = "../../crates/infrastructure/provider-sqlite" }
auth-sqlite = { path = "../../crates/infrastructure/auth-sqlite" }
alias-sqlite = { path = "../../crates/infrastructure/alias-sqlite" }
db-migration = { path = "../../crates/infrastructure/db-migration" }
models-catalog = { path = "../../crates/infrastructure/models-catalog" }
tokio = { version = "1", features = ["full"] }
Expand Down
61 changes: 61 additions & 0 deletions apps/rook/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub struct RookConfig {
/// Combo (multi-step fallback chain) definitions
#[serde(default)]
pub combos: Vec<ComboConfig>,
/// Model aliases configuration
#[serde(default)]
pub model_aliases: ModelAliasesConfig,
}

#[derive(Debug, Clone, Deserialize)]
Expand Down Expand Up @@ -72,6 +75,36 @@ fn default_allow_env_fallback() -> bool {
true
}

#[derive(Debug, Clone, Deserialize)]
pub struct ModelAliasesConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_true")]
pub auto_seed: bool,
}

impl Default for ModelAliasesConfig {
fn default() -> Self {
Self {
enabled: true,
auto_seed: true,
}
}
}

impl From<ModelAliasesConfig> for rook_usecases::route_request::ModelAliasesConfig {
fn from(cfg: ModelAliasesConfig) -> Self {
Self {
enabled: cfg.enabled,
auto_seed: cfg.auto_seed,
}
}
}

fn default_true() -> bool {
true
}

#[derive(Debug, Clone, Deserialize)]
pub struct ProviderCrudConfig {
#[serde(default = "default_provider_crud_enabled")]
Expand Down Expand Up @@ -191,12 +224,34 @@ pub struct CacheConfig {
pub enabled: bool,
#[serde(rename = "ttl_secs")]
pub ttl_secs: u64,
#[serde(default)]
pub max_entries: Option<usize>,
}

impl CacheConfig {
pub fn ttl(&self) -> Duration {
Duration::from_secs(self.ttl_secs)
}

/// Validate cache configuration at startup
pub fn validate(&self) -> Result<(), String> {
// Reject TTL > 24 hours (86400 seconds)
if self.ttl_secs > 86400 {
return Err(format!(
"cache.ttl_secs ({}) exceeds 24h maximum (86400)",
self.ttl_secs
));
}

// Reject max_entries = Some(0)
if let Some(0) = self.max_entries {
return Err(
"cache.max_entries must be greater than 0 or None for unlimited".to_string(),
);
}

Ok(())
}
}

#[derive(Debug, Clone, Deserialize)]
Expand Down Expand Up @@ -290,6 +345,12 @@ impl RookConfig {
// Validate combo configurations
Self::validate_combos(&config.combos);

// Validate cache configuration
config
.cache
.validate()
.map_err(|e| anyhow::anyhow!("invalid cache config: {}", e))?;

Ok(config)
}

Expand Down
52 changes: 48 additions & 4 deletions apps/rook/src/di.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::path::Path;
use std::sync::Arc;
use std::time::Duration;

use alias_sqlite::SqliteModelAliasRepository;
use audit_sqlite::{SqliteAudit, SqliteUsageRepository};
use auth_sqlite::{SqliteApiKeyRepository, SqliteSessionRepository, SqliteUserRepository};
use cache_memory::InMemoryCache;
Expand All @@ -17,9 +18,9 @@ use provider_sqlite::SqliteProviderRepository;
use providers_ollama::OllamaProvider;
use rook_core::{
ApiKeyRepositoryPort, AuditPort, CachePort, Combo, ComboRepositoryPort, ComboStep,
ComboStrategy, ConnectionId, DecryptedCredentials, PasswordHasher, ProviderId, ProviderKind,
ProviderPort, ProviderRegistryPort, ProviderRepositoryPort, RouterPort, SessionRepositoryPort,
UsageRecorderPort,
ComboStrategy, ConnectionId, DecryptedCredentials, ModelAliasRepositoryPort, PasswordHasher,
ProviderId, ProviderKind, ProviderPort, ProviderRegistryPort, ProviderRepositoryPort,
RouterPort, SessionRepositoryPort, UsageRecorderPort,
};
use rook_usecases::{
AuthenticateClientApi, BootstrapStatus, EnsureAdminUser, FallbackRouter, HealthCheck,
Expand Down Expand Up @@ -72,7 +73,10 @@ impl RookContainer {

// 1. Cache
let cache: Arc<dyn CachePort> = if config.cache.enabled {
Arc::new(InMemoryCache::new(config.cache.ttl(), None))
Arc::new(InMemoryCache::new(
config.cache.ttl(),
config.cache.max_entries,
))
} else {
Arc::new(NoOpCache)
};
Expand Down Expand Up @@ -169,6 +173,32 @@ impl RookContainer {
let combo_repo: Arc<dyn ComboRepositoryPort> =
Arc::new(ComboSqliteRepository::new(&config.database.db_path)?);

// 7d. Model alias repository — SQLite-backed alias storage
let alias_repo: Arc<dyn ModelAliasRepositoryPort> =
Arc::new(SqliteModelAliasRepository::new(&config.database.db_path)?);

// 7e. Seed built-in aliases if enabled
if config.model_aliases.auto_seed {
let builtin_aliases = alias_sqlite::builtin::DEFAULT_ALIASES
.iter()
.map(|(alias, canonical, provider_id)| rook_core::ModelAlias {
alias: shared_kernel::ModelId::new(*alias),
canonical: shared_kernel::ModelId::new(*canonical),
provider_id: provider_id.map(shared_kernel::ProviderId::new),
created_at: shared_kernel::Utc::now(),
})
.collect::<Vec<_>>();

match alias_repo.seed(builtin_aliases).await {
Ok(count) => {
tracing::info!(count, "Seeded default model aliases");
}
Err(e) => {
tracing::warn!(error = ?e, "Failed to seed model aliases");
}
}
}

// 8. Run async initialization tasks concurrently:
// - registry refresh (if provider_crud enabled)
// - ensure admin user exists
Expand Down Expand Up @@ -228,6 +258,8 @@ impl RookContainer {
Some(combo_repo.clone()), // combo_repository - wired in Phase 4
Arc::new(config.pricing.clone()),
format_registry.clone(),
alias_repo.clone(), // model_alias_repository - wired in Phase 3
config.model_aliases.clone().into(),
),
ManageProviders::new(router.clone()),
health_check,
Expand Down Expand Up @@ -626,6 +658,18 @@ impl CachePort for NoOpCache {
async fn clear(&self) -> CortexResult<()> {
Ok(())
}
async fn stats(&self) -> CortexResult<rook_core::CacheStats> {
Ok(rook_core::CacheStats {
hits: 0,
misses: 0,
evictions: 0,
entries: 0,
max_entries: 0,
})
}
async fn delete_by_signature(&self, _: &str) -> CortexResult<usize> {
Ok(0)
}
}

// ---------------------------------------------------------------------------
Expand Down
Loading