feat(session): persist model tier overrides per session
Store per-session config in SQLite and route /model and /reset through command fast-paths so channel sessions keep independent model selection across reconnects and restarts.
This commit is contained in:
@@ -9,6 +9,9 @@ const mockSession = {
|
||||
getHistory: vi.fn(() => []),
|
||||
clear: vi.fn(),
|
||||
replaceHistory: vi.fn(),
|
||||
getConfig: vi.fn((_key: string) => undefined as string | undefined),
|
||||
setConfig: vi.fn(),
|
||||
deleteConfig: vi.fn(),
|
||||
};
|
||||
|
||||
const mockSessionManager = {
|
||||
@@ -48,9 +51,21 @@ function createBridge(): SessionBridge {
|
||||
});
|
||||
}
|
||||
|
||||
function createBridgeWithConfig(config: SessionBridgeConfig['config']): SessionBridge {
|
||||
return new SessionBridge({
|
||||
sessionManager: mockSessionManager as unknown as SessionBridgeConfig['sessionManager'],
|
||||
modelClient: mockModelClient,
|
||||
systemPrompt: 'test prompt',
|
||||
toolRegistry: mockToolRegistry as unknown as SessionBridgeConfig['toolRegistry'],
|
||||
toolExecutor: mockToolExecutor as unknown as SessionBridgeConfig['toolExecutor'],
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
describe('SessionBridge', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockSession.getConfig.mockImplementation((_key: string) => undefined);
|
||||
});
|
||||
|
||||
it('connect assigns a connection ID', () => {
|
||||
@@ -142,4 +157,78 @@ describe('SessionBridge', () => {
|
||||
expect(bridge.getAgent('conn-2')).toBeDefined();
|
||||
expect(bridge.connectionCount).toBe(1);
|
||||
});
|
||||
|
||||
it('loads model tier from per-session config when creating a session agent', () => {
|
||||
mockSession.getConfig.mockImplementation((key: string) => (key === 'modelTier' ? 'local' : undefined));
|
||||
|
||||
const bridge = createBridgeWithConfig({
|
||||
agents: {
|
||||
primary_tier: 'default',
|
||||
delegation: {
|
||||
compaction: 'fast',
|
||||
memory_extraction: 'fast',
|
||||
classification: 'fast',
|
||||
tool_summarisation: 'fast',
|
||||
complex_reasoning: 'complex',
|
||||
},
|
||||
max_delegation_depth: 3,
|
||||
},
|
||||
compaction: { enabled: false },
|
||||
models: { default: { provider: 'anthropic', model: 'claude-3-haiku' } },
|
||||
} as any);
|
||||
|
||||
bridge.connect('conn-tier');
|
||||
const agent = bridge.getAgent('conn-tier');
|
||||
expect(agent?.getModelTier()).toBe('local');
|
||||
});
|
||||
|
||||
it('keeps different sessions isolated by persisted model tier', () => {
|
||||
const sessionById: Record<string, any> = {};
|
||||
const localSessionManager = {
|
||||
...mockSessionManager,
|
||||
getSession: vi.fn((frontend: string, sessionId: string) => {
|
||||
const fullId = `${frontend}:${sessionId}`;
|
||||
if (!sessionById[fullId]) {
|
||||
const tier = fullId === 'ws:ws:conn-a' ? 'fast' : 'complex';
|
||||
sessionById[fullId] = {
|
||||
...mockSession,
|
||||
id: fullId,
|
||||
getConfig: vi.fn((key: string) => (key === 'modelTier' ? tier : undefined)),
|
||||
};
|
||||
}
|
||||
return sessionById[fullId];
|
||||
}),
|
||||
};
|
||||
|
||||
const bridge = new SessionBridge({
|
||||
sessionManager: localSessionManager as unknown as SessionBridgeConfig['sessionManager'],
|
||||
modelClient: mockModelClient,
|
||||
systemPrompt: 'test prompt',
|
||||
toolRegistry: mockToolRegistry as unknown as SessionBridgeConfig['toolRegistry'],
|
||||
toolExecutor: mockToolExecutor as unknown as SessionBridgeConfig['toolExecutor'],
|
||||
config: {
|
||||
agents: {
|
||||
primary_tier: 'default',
|
||||
delegation: {
|
||||
compaction: 'fast',
|
||||
memory_extraction: 'fast',
|
||||
classification: 'fast',
|
||||
tool_summarisation: 'fast',
|
||||
complex_reasoning: 'complex',
|
||||
},
|
||||
max_delegation_depth: 3,
|
||||
},
|
||||
compaction: { enabled: false },
|
||||
models: { default: { provider: 'anthropic', model: 'claude-3-haiku' } },
|
||||
} as any,
|
||||
});
|
||||
|
||||
bridge.connect('conn-a');
|
||||
bridge.connect('conn-b');
|
||||
const agentA = bridge.getAgent('conn-a');
|
||||
const agentB = bridge.getAgent('conn-b');
|
||||
|
||||
expect(agentA?.getModelTier()).toBe('fast');
|
||||
expect(agentB?.getModelTier()).toBe('complex');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user