feat(models): add background task model override config and runtime wiring
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { GatewayRequest, OutboundMessage } from '../protocol.js';
|
||||
import { makeResponse, makeError, ErrorCode } from '../protocol.js';
|
||||
import type { Config } from '../../config/index.js';
|
||||
import { MODEL_PROVIDERS, type Config, type ModelProvider } from '../../config/index.js';
|
||||
|
||||
export interface ConfigHandlerDeps {
|
||||
config: Config;
|
||||
@@ -163,6 +163,156 @@ const PATCHABLE_KEYS: Record<string, (config: Config, value: unknown) => boolean
|
||||
config.server.nodes.push.enabled = value;
|
||||
return true;
|
||||
},
|
||||
'agents.primary_tier': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.primary_tier = value;
|
||||
return true;
|
||||
},
|
||||
'agents.delegation.compaction': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.delegation.compaction = value;
|
||||
return true;
|
||||
},
|
||||
'agents.delegation.memory_extraction': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.delegation.memory_extraction = value;
|
||||
return true;
|
||||
},
|
||||
'agents.delegation.classification': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.delegation.classification = value;
|
||||
return true;
|
||||
},
|
||||
'agents.delegation.tool_summarisation': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.delegation.tool_summarisation = value;
|
||||
return true;
|
||||
},
|
||||
'agents.delegation.complex_reasoning': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.delegation.complex_reasoning = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.compaction.enabled': (config, value) => {
|
||||
if (typeof value !== 'boolean') {return false;}
|
||||
config.agents.background_models.compaction ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.compaction.enabled = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.compaction.provider': (config, value) => {
|
||||
if (!MODEL_PROVIDERS.includes(String(value) as ModelProvider)) {return false;}
|
||||
config.agents.background_models.compaction ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.compaction.provider = value as ModelProvider;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.compaction.model': (config, value) => {
|
||||
if (typeof value !== 'string' || value.trim().length === 0) {return false;}
|
||||
config.agents.background_models.compaction ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.compaction.model = value.trim();
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.compaction.fallback_tier': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.background_models.compaction ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.compaction.fallback_tier = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.memory_extraction.enabled': (config, value) => {
|
||||
if (typeof value !== 'boolean') {return false;}
|
||||
config.agents.background_models.memory_extraction ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.memory_extraction.enabled = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.memory_extraction.provider': (config, value) => {
|
||||
if (!MODEL_PROVIDERS.includes(String(value) as ModelProvider)) {return false;}
|
||||
config.agents.background_models.memory_extraction ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.memory_extraction.provider = value as ModelProvider;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.memory_extraction.model': (config, value) => {
|
||||
if (typeof value !== 'string' || value.trim().length === 0) {return false;}
|
||||
config.agents.background_models.memory_extraction ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.memory_extraction.model = value.trim();
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.memory_extraction.fallback_tier': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.background_models.memory_extraction ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.memory_extraction.fallback_tier = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.classification.enabled': (config, value) => {
|
||||
if (typeof value !== 'boolean') {return false;}
|
||||
config.agents.background_models.classification ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.classification.enabled = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.classification.provider': (config, value) => {
|
||||
if (!MODEL_PROVIDERS.includes(String(value) as ModelProvider)) {return false;}
|
||||
config.agents.background_models.classification ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.classification.provider = value as ModelProvider;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.classification.model': (config, value) => {
|
||||
if (typeof value !== 'string' || value.trim().length === 0) {return false;}
|
||||
config.agents.background_models.classification ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.classification.model = value.trim();
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.classification.fallback_tier': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.background_models.classification ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.classification.fallback_tier = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.tool_summarisation.enabled': (config, value) => {
|
||||
if (typeof value !== 'boolean') {return false;}
|
||||
config.agents.background_models.tool_summarisation ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.tool_summarisation.enabled = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.tool_summarisation.provider': (config, value) => {
|
||||
if (!MODEL_PROVIDERS.includes(String(value) as ModelProvider)) {return false;}
|
||||
config.agents.background_models.tool_summarisation ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.tool_summarisation.provider = value as ModelProvider;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.tool_summarisation.model': (config, value) => {
|
||||
if (typeof value !== 'string' || value.trim().length === 0) {return false;}
|
||||
config.agents.background_models.tool_summarisation ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.tool_summarisation.model = value.trim();
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.tool_summarisation.fallback_tier': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.background_models.tool_summarisation ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.tool_summarisation.fallback_tier = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.complex_reasoning.enabled': (config, value) => {
|
||||
if (typeof value !== 'boolean') {return false;}
|
||||
config.agents.background_models.complex_reasoning ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.complex_reasoning.enabled = value;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.complex_reasoning.provider': (config, value) => {
|
||||
if (!MODEL_PROVIDERS.includes(String(value) as ModelProvider)) {return false;}
|
||||
config.agents.background_models.complex_reasoning ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.complex_reasoning.provider = value as ModelProvider;
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.complex_reasoning.model': (config, value) => {
|
||||
if (typeof value !== 'string' || value.trim().length === 0) {return false;}
|
||||
config.agents.background_models.complex_reasoning ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.complex_reasoning.model = value.trim();
|
||||
return true;
|
||||
},
|
||||
'agents.background_models.complex_reasoning.fallback_tier': (config, value) => {
|
||||
if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;}
|
||||
config.agents.background_models.complex_reasoning ??= { enabled: true, provider: 'openai', model: 'gpt-4o-mini', fallback_tier: 'fast' };
|
||||
config.agents.background_models.complex_reasoning.fallback_tier = value;
|
||||
return true;
|
||||
},
|
||||
'automation.delivery_mode': (config, value) => {
|
||||
if (value !== 'shared_session' && value !== 'isolated_job' && value !== 'announce') {return false;}
|
||||
config.automation ??= {} as Config['automation'];
|
||||
|
||||
@@ -2,12 +2,13 @@ import { randomUUID } from 'crypto';
|
||||
import type { SessionManager } from '../session/manager.js';
|
||||
import type { ModelClient } from '../models/types.js';
|
||||
import type { ModelRouter, ModelTier } from '../models/router.js';
|
||||
import { createClientFromConfig } from '../daemon/models.js';
|
||||
import type { Config, ModelConfig, ModelProvider } from '../config/index.js';
|
||||
import type { ToolRegistry } from '../tools/registry.js';
|
||||
import type { ToolExecutor } from '../tools/executor.js';
|
||||
import { AgentOrchestrator, type DelegationConfig } from '../backends/native/orchestrator.js';
|
||||
import type { ToolUseEvent } from '../backends/native/agent.js';
|
||||
import type { MemoryStore } from '../memory/store.js';
|
||||
import type { Config } from '../config/index.js';
|
||||
import { summarizeSessionOnEnd, type SessionEndSummaryConfig } from '../session/endSummary.js';
|
||||
|
||||
export interface SessionBridgeConfig {
|
||||
@@ -284,6 +285,7 @@ export class SessionBridge {
|
||||
tool_summarisation: config?.agents.delegation.tool_summarisation ?? 'fast',
|
||||
complex_reasoning: config?.agents.delegation.complex_reasoning ?? 'complex',
|
||||
};
|
||||
const backgroundModelOverrides = this.buildBackgroundModelOverrides();
|
||||
|
||||
agent = new AgentOrchestrator({
|
||||
modelRouter: this.config.modelClient as ModelRouter,
|
||||
@@ -293,6 +295,7 @@ export class SessionBridge {
|
||||
toolExecutor: this.config.toolExecutor,
|
||||
primaryTier,
|
||||
delegation: delegationConfig,
|
||||
backgroundModelOverrides,
|
||||
maxDelegationDepth: config?.agents.max_delegation_depth ?? 3,
|
||||
maxIterations: config?.agents.max_iterations,
|
||||
compaction: config?.compaction.enabled ? {
|
||||
@@ -337,4 +340,77 @@ export class SessionBridge {
|
||||
}
|
||||
return agent;
|
||||
}
|
||||
|
||||
private buildBackgroundModelOverrides(): Partial<Record<keyof DelegationConfig, {
|
||||
client: ModelClient;
|
||||
label: string;
|
||||
fallbackTier: ModelTier;
|
||||
}>> {
|
||||
const runtimeConfig = this.config.config;
|
||||
const overrides: Partial<Record<keyof DelegationConfig, {
|
||||
client: ModelClient;
|
||||
label: string;
|
||||
fallbackTier: ModelTier;
|
||||
}>> = {};
|
||||
if (!runtimeConfig) {
|
||||
return overrides;
|
||||
}
|
||||
|
||||
const providerConfigs = this.buildProviderConfigMap(runtimeConfig);
|
||||
const configured = runtimeConfig.agents?.background_models ?? {};
|
||||
const tasks: Array<keyof DelegationConfig> = [
|
||||
'compaction',
|
||||
'memory_extraction',
|
||||
'classification',
|
||||
'tool_summarisation',
|
||||
'complex_reasoning',
|
||||
];
|
||||
|
||||
for (const task of tasks) {
|
||||
const entry = configured[task];
|
||||
if (!entry || entry.enabled === false) {
|
||||
continue;
|
||||
}
|
||||
const template = providerConfigs[entry.provider];
|
||||
try {
|
||||
const client = createClientFromConfig(
|
||||
template
|
||||
? { ...template, provider: entry.provider, model: entry.model }
|
||||
: { provider: entry.provider, model: entry.model },
|
||||
);
|
||||
overrides[task] = {
|
||||
client,
|
||||
label: `${entry.provider}/${entry.model}`,
|
||||
fallbackTier: entry.fallback_tier,
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[Flynn:gateway] Failed to initialize background model override for ${task} ` +
|
||||
`(${entry.provider}/${entry.model}): ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return overrides;
|
||||
}
|
||||
|
||||
private buildProviderConfigMap(config: Config): Partial<Record<ModelProvider, ModelConfig>> {
|
||||
const providerConfigs: Partial<Record<ModelProvider, ModelConfig>> = {};
|
||||
const modelConfigs: ModelConfig[] = [
|
||||
config.models.default,
|
||||
...(config.models.fast ? [config.models.fast] : []),
|
||||
...(config.models.complex ? [config.models.complex] : []),
|
||||
...(config.models.local ? [config.models.local] : []),
|
||||
...Object.values(config.models.local_providers ?? {}),
|
||||
];
|
||||
|
||||
for (const modelConfig of modelConfigs) {
|
||||
providerConfigs[modelConfig.provider] = modelConfig;
|
||||
if (modelConfig.fallback) {
|
||||
providerConfigs[modelConfig.fallback.provider] = modelConfig.fallback;
|
||||
}
|
||||
}
|
||||
|
||||
return providerConfigs;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user