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:
+75
-5
@@ -1,6 +1,7 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import type { Message } from '../models/types.js';
|
||||
import type { PairingStore, ApprovedSender } from '../channels/pairing.js';
|
||||
import type { HistoryMetadata } from './indexer.js';
|
||||
|
||||
/** Parse a duration string like '30d', '7d', '12h' to milliseconds. Returns null if invalid or '0'. */
|
||||
export function parseDuration(s: string): number | null {
|
||||
@@ -44,13 +45,18 @@ export class SessionStore {
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_config_session ON session_config(session_id);
|
||||
`);
|
||||
|
||||
const messageColumns = this.db.prepare('PRAGMA table_info(messages)').all() as Array<{ name: string }>;
|
||||
if (!messageColumns.some(column => column.name === 'metadata')) {
|
||||
this.db.exec('ALTER TABLE messages ADD COLUMN metadata TEXT');
|
||||
}
|
||||
}
|
||||
|
||||
addMessage(sessionId: string, message: Message): void {
|
||||
addMessage(sessionId: string, message: Message, metadata?: HistoryMetadata): void {
|
||||
const stmt = this.db.prepare(
|
||||
'INSERT INTO messages (session_id, role, content) VALUES (?, ?, ?)',
|
||||
'INSERT INTO messages (session_id, role, content, metadata) VALUES (?, ?, ?, ?)',
|
||||
);
|
||||
stmt.run(sessionId, message.role, message.content);
|
||||
stmt.run(sessionId, message.role, message.content, metadata ? JSON.stringify(metadata) : null);
|
||||
}
|
||||
|
||||
getMessages(sessionId: string): Message[] {
|
||||
@@ -75,10 +81,10 @@ export class SessionStore {
|
||||
this.db.prepare('DELETE FROM messages WHERE session_id = ?').run(sessionId);
|
||||
// Re-insert in order
|
||||
const insert = this.db.prepare(
|
||||
'INSERT INTO messages (session_id, role, content) VALUES (?, ?, ?)',
|
||||
'INSERT INTO messages (session_id, role, content, metadata) VALUES (?, ?, ?, ?)',
|
||||
);
|
||||
for (const msg of messages) {
|
||||
insert.run(sessionId, msg.role, msg.content);
|
||||
insert.run(sessionId, msg.role, msg.content, null);
|
||||
}
|
||||
});
|
||||
transaction();
|
||||
@@ -194,4 +200,68 @@ export class SessionStore {
|
||||
close(): void {
|
||||
this.db.close();
|
||||
}
|
||||
|
||||
getMessagesWithMetadata(sessionId: string): Array<{
|
||||
id: number;
|
||||
sessionId: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
createdAt: number;
|
||||
metadata: HistoryMetadata | null;
|
||||
}> {
|
||||
const stmt = this.db.prepare(
|
||||
'SELECT id, session_id, role, content, created_at, metadata FROM messages WHERE session_id = ? ORDER BY id ASC',
|
||||
);
|
||||
const rows = stmt.all(sessionId) as Array<{
|
||||
id: number;
|
||||
session_id: string;
|
||||
role: string;
|
||||
content: string;
|
||||
created_at: number;
|
||||
metadata: string | null;
|
||||
}>;
|
||||
|
||||
return rows.map(row => ({
|
||||
id: row.id,
|
||||
sessionId: row.session_id,
|
||||
role: row.role as 'user' | 'assistant',
|
||||
content: row.content,
|
||||
createdAt: row.created_at,
|
||||
metadata: row.metadata ? JSON.parse(row.metadata) as HistoryMetadata : null,
|
||||
}));
|
||||
}
|
||||
|
||||
getAllMessagesWithMetadata(): Array<{
|
||||
id: number;
|
||||
sessionId: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
createdAt: number;
|
||||
metadata: HistoryMetadata | null;
|
||||
}> {
|
||||
const stmt = this.db.prepare(
|
||||
'SELECT id, session_id, role, content, created_at, metadata FROM messages ORDER BY id ASC',
|
||||
);
|
||||
const rows = stmt.all() as Array<{
|
||||
id: number;
|
||||
session_id: string;
|
||||
role: string;
|
||||
content: string;
|
||||
created_at: number;
|
||||
metadata: string | null;
|
||||
}>;
|
||||
|
||||
return rows.map(row => ({
|
||||
id: row.id,
|
||||
sessionId: row.session_id,
|
||||
role: row.role as 'user' | 'assistant',
|
||||
content: row.content,
|
||||
createdAt: row.created_at,
|
||||
metadata: row.metadata ? JSON.parse(row.metadata) as HistoryMetadata : null,
|
||||
}));
|
||||
}
|
||||
|
||||
updateMessageMetadata(messageId: number, metadata: HistoryMetadata): void {
|
||||
this.db.prepare('UPDATE messages SET metadata = ? WHERE id = ?').run(JSON.stringify(metadata), messageId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user