Files
boocode/apps/server/src/services/auto_name.ts
indifferentketchup fcc7c5a86e v2.5.0-task-model: lightweight task model services + tasks table
Task model infrastructure for cheap LLM calls (auto-naming, search
rewrite, tags, summaries) via a dedicated llama-server instance at
TASK_MODEL_URL, falling back to LLAMA_SWAP_URL with FAST_MODEL when
unset. Replaces the inline fetch in auto_name.ts with taskModelCompletion.

Adds search query rewriting: on step 0 when web tools are enabled, the
user's message is summarized into a search intent hint appended to the
system prompt, improving web_search relevance.

Schema: tasks table for provider dispatch and arena, sessions.tags column.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-27 21:44:39 +00:00

115 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { InferenceContext } from './inference/index.js';
import { taskModelCompletion } from './task-model.js';
const NAMING_SYSTEM_PROMPT =
'You name chat sessions. Reply with ONLY the title. 4 to 6 words. No quotes, no punctuation, no prefix.';
const MAX_TITLE_CHARS = 80;
function cleanTitle(raw: string): string {
let name = raw.trim();
const quotes = ['"', "'", '`', '', '', '“', '”'];
while (name.length >= 2 && quotes.includes(name[0]!) && quotes.includes(name[name.length - 1]!)) {
name = name.slice(1, -1).trim();
}
name = name.replace(/^title\s*:\s*/i, '').trim();
if (name.length > MAX_TITLE_CHARS) {
name = name.slice(0, MAX_TITLE_CHARS).trim();
}
return name;
}
// TODO: wire suggestTags after task model validation
export async function maybeAutoNameChat(
ctx: InferenceContext,
chatId: string,
sessionId: string
): Promise<void> {
const counts = await ctx.sql<{ n: number }[]>`
SELECT COUNT(*)::int AS n
FROM messages
WHERE chat_id = ${chatId}
AND role = 'assistant'
AND status = 'complete'
AND content <> ''
`;
if ((counts[0]?.n ?? 0) < 1) return;
const chatRows = await ctx.sql<
{ id: string; name: string | null; session_id: string }[]
>`
SELECT id, name, session_id FROM chats WHERE id = ${chatId}
`;
const chat = chatRows[0];
if (!chat) return;
if (chat.name !== null && chat.name !== '') return;
const assistantMsg = await ctx.sql<{ content: string }[]>`
SELECT content FROM messages
WHERE chat_id = ${chatId}
AND role = 'assistant'
AND status = 'complete'
AND content <> ''
ORDER BY created_at ASC
LIMIT 1
`;
if (!assistantMsg[0]) return;
const assistantText = assistantMsg[0].content.slice(0, 2000);
const raw = await taskModelCompletion({
system: NAMING_SYSTEM_PROMPT,
user: assistantText,
maxTokens: 30,
temperature: 0.3,
});
const name = cleanTitle(raw);
if (!name) {
ctx.log.warn({ chatId, raw }, 'auto-name: empty title from model');
return;
}
const updated = await ctx.sql<{ id: string; name: string; session_id: string; updated_at: string }[]>`
UPDATE chats
SET name = ${name}, updated_at = clock_timestamp()
WHERE id = ${chatId}
AND (name IS NULL OR name = '')
RETURNING id, name, session_id, updated_at
`;
if (updated.length === 0) return;
ctx.publish(sessionId, {
type: 'chat_renamed',
chat_id: chatId,
name,
});
ctx.publishUser({
type: 'chat_updated',
chat_id: chatId,
session_id: sessionId,
name,
updated_at: updated[0]!.updated_at,
});
ctx.log.info({ chatId, name }, 'chat auto-named');
// Propagate to the parent session if it's still on its default name.
// The WHERE guard makes the check atomic — if the user has already
// renamed (or a prior chat already propagated), this UPDATE matches
// zero rows and we do nothing. First chat wins; manual renames win.
const renamedSession = await ctx.sql<{ id: string; name: string }[]>`
UPDATE sessions
SET name = ${name}
WHERE id = ${sessionId} AND name = 'New session'
RETURNING id, name
`;
if (renamedSession.length > 0) {
ctx.publishUser({
type: 'session_renamed',
session_id: sessionId,
name,
});
ctx.log.info({ sessionId, name }, 'session auto-named from chat');
}
}