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:
@@ -4,6 +4,7 @@ import type { MemoryStore } from '../memory/store.js';
|
||||
import { COMPACTION_SYSTEM_PROMPT, MEMORY_EXTRACTION_PROMPT } from '../backends/native/prompts.js';
|
||||
import { estimateMessageTokens } from './tokens.js';
|
||||
import { getMessageText } from '../models/media.js';
|
||||
import { selectImportantMessages } from './weighting.js';
|
||||
|
||||
export interface CompactionConfig {
|
||||
/** Percentage of context window that triggers compaction (default: 80). */
|
||||
@@ -12,6 +13,8 @@ export interface CompactionConfig {
|
||||
keepTurns: number;
|
||||
/** Maximum tokens for the compaction summary response. */
|
||||
summaryMaxTokens: number;
|
||||
/** Preserve messages at or above this importance score from compaction. */
|
||||
importanceThreshold: number;
|
||||
}
|
||||
|
||||
export interface CompactionResult {
|
||||
@@ -29,6 +32,7 @@ export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = {
|
||||
thresholdPct: 80,
|
||||
keepTurns: 4,
|
||||
summaryMaxTokens: 1024,
|
||||
importanceThreshold: 1,
|
||||
};
|
||||
|
||||
export async function compactHistory(opts: {
|
||||
@@ -56,10 +60,34 @@ export async function compactHistory(opts: {
|
||||
// Ensure toKeep starts with a user message to avoid assistant→assistant
|
||||
// after the compaction summary (which has role 'assistant').
|
||||
while (toKeep.length > 0 && toKeep[0].role === 'assistant') {
|
||||
toCompact.push(toKeep.shift()!);
|
||||
const shifted = toKeep.shift();
|
||||
if (!shifted) {
|
||||
break;
|
||||
}
|
||||
toCompact.push(shifted);
|
||||
}
|
||||
|
||||
const formattedConversation = toCompact.map((msg) => `${msg.role}: ${getMessageText(msg)}`).join('\n\n');
|
||||
const preservedImportant = selectImportantMessages(toCompact, {
|
||||
threshold: config.importanceThreshold,
|
||||
maxMessages: Math.max(1, config.keepTurns),
|
||||
});
|
||||
|
||||
const preservedSet = new Set(preservedImportant.map(item => item.index));
|
||||
const toSummarize = toCompact.filter((_, index) => !preservedSet.has(index));
|
||||
|
||||
const formattedConversation = toSummarize.map((msg) => `${msg.role}: ${getMessageText(msg)}`).join('\n\n');
|
||||
|
||||
const preservedMessages = preservedImportant.map(item => item.message);
|
||||
|
||||
if (formattedConversation.trim().length === 0) {
|
||||
const compactedMessages = [...preservedMessages, ...toKeep];
|
||||
return {
|
||||
messages: compactedMessages,
|
||||
compactedCount: messages.length - compactedMessages.length,
|
||||
tokensBefore: estimateMessageTokens(messages),
|
||||
tokensAfter: estimateMessageTokens(compactedMessages),
|
||||
};
|
||||
}
|
||||
|
||||
const tier = orchestrator.getDelegationTier('compaction');
|
||||
|
||||
@@ -99,9 +127,9 @@ export async function compactHistory(opts: {
|
||||
}
|
||||
|
||||
return {
|
||||
messages: [summaryMessage, ...toKeep],
|
||||
compactedCount: toCompact.length,
|
||||
messages: [...preservedMessages, summaryMessage, ...toKeep],
|
||||
compactedCount: toSummarize.length,
|
||||
tokensBefore: estimateMessageTokens(messages),
|
||||
tokensAfter: estimateMessageTokens([summaryMessage, ...toKeep]),
|
||||
tokensAfter: estimateMessageTokens([...preservedMessages, summaryMessage, ...toKeep]),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user