feat: wire per-tier fallbacks in daemon model router setup

Reads the optional fallback field from each tier's config and builds
a tierFallbacks map passed to ModelRouter at startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
William Valentin
2026-02-07 12:10:17 -08:00
parent b9b70ce2b1
commit f0e3987d1c
+45 -3
View File
@@ -4,12 +4,12 @@ import type { AudioTranscriptionConfig } from '../models/media.js';
import type { Attachment } from '../channels/types.js';
import { isSupportedAudio, transcribeAudio } from '../models/media.js';
import { AnthropicClient, OpenAIClient, OllamaClient, LlamaCppClient, GeminiClient, BedrockClient, GitHubModelsClient, ModelRouter, DEFAULT_RETRY_CONFIG } from '../models/index.js';
import type { ModelClient, RetryConfig } from '../models/index.js';
import type { ModelClient, RetryConfig, ModelTier } from '../models/index.js';
import { AgentOrchestrator, type DelegationConfig } from '../backends/index.js';
import { OutboundAttachmentCollector } from '../backends/native/attachments.js';
import { SessionStore, SessionManager } from '../session/index.js';
import { HookEngine } from '../hooks/index.js';
import { ToolRegistry, ToolExecutor, ToolPolicy, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager, BrowserManager, createBrowserTools, createMediaSendTool } from '../tools/index.js';
import { ToolRegistry, ToolExecutor, ToolPolicy, allBuiltinTools, createWebSearchTools, createProcessTools, ProcessManager, BrowserManager, createBrowserTools, createMediaSendTool, createSessionTools, createAgentsListTool, createMessageSendTool, createCronTools } from '../tools/index.js';
import type { Tool } from '../tools/types.js';
import { MemoryStore } from '../memory/index.js';
import { createMemoryTools } from '../tools/builtin/index.js';
@@ -160,6 +160,26 @@ function createModelRouter(config: Config): ModelRouter {
}
}
// Build per-tier fallbacks from inline fallback config
const tierFallbacks = new Map<ModelTier, ModelClient[]>();
if (models.default.fallback) {
tierFallbacks.set('default', [createClientFromConfig(models.default.fallback)]);
}
if (models.fast?.fallback) {
tierFallbacks.set('fast', [createClientFromConfig(models.fast.fallback)]);
}
if (models.complex?.fallback) {
tierFallbacks.set('complex', [createClientFromConfig(models.complex.fallback)]);
}
if (models.local?.fallback) {
tierFallbacks.set('local', [createClientFromConfig(models.local.fallback)]);
}
if (tierFallbacks.size > 0) {
const tierNames = Array.from(tierFallbacks.keys()).join(', ');
console.log(`Per-tier fallbacks configured for: ${tierNames}`);
}
console.log(`Model router: default=${models.default.provider}/${models.default.model}, ` +
`fallback=[${models.fallback_chain.join(', ')}]`);
@@ -182,6 +202,7 @@ function createModelRouter(config: Config): ModelRouter {
complex: complexClient,
local: localClient,
fallbackChain,
tierFallbacks,
retryConfig,
labels: {
default: `${models.default.provider}/${models.default.model}`,
@@ -683,12 +704,33 @@ export async function startDaemon(config: Config): Promise<DaemonContext> {
channelRegistry.register(webChatAdapter);
// Register cron scheduler adapter (if any cron jobs configured)
let cronScheduler: CronScheduler | undefined;
if (config.automation.cron.length > 0) {
const cronScheduler = new CronScheduler(config.automation.cron, channelRegistry);
cronScheduler = new CronScheduler(config.automation.cron, channelRegistry);
channelRegistry.register(cronScheduler);
console.log(`Registered ${config.automation.cron.length} cron job(s)`);
}
// ── Register Tier 1 agent tools ─────────────────────────────
// Session management tools (list, history, create, delete)
for (const tool of createSessionTools(sessionManager)) {
toolRegistry.register(tool);
}
// Agent discovery tool
toolRegistry.register(createAgentsListTool(agentConfigRegistry));
// Cross-channel messaging tool
toolRegistry.register(createMessageSendTool(channelRegistry));
// Cron management tools (if scheduler is active)
if (cronScheduler) {
for (const tool of createCronTools(cronScheduler)) {
toolRegistry.register(tool);
}
}
// ── Signal Handlers ───────────────────────────────────────────
const signalHandler = () => {