import { generateText } from "ai"; import { type LanguageModel } from "ai"; import { resolveModelForUser, resolveModelAny, toLanguageModel } from "#server/service/llm/model-resolver"; import { updateSession } from "./session"; import type { AgentRow } from "./agent"; import log4js from "logger"; const logger = log4js.getLogger("APP"); const FIRST_LINE_MAX_LENGTH = 50; export type TitleStrategy = "llm" | "first-line" | "none"; export function truncateFirstLine(text: string): string { const firstLine = text.split("\n")[0]?.trim() ?? ""; if (firstLine.length <= FIRST_LINE_MAX_LENGTH) return firstLine; return firstLine.slice(0, FIRST_LINE_MAX_LENGTH) + "…"; } export async function generateSessionTitle(params: { sessionId: string; agent: AgentRow; firstUserMessage: string; firstAssistantMessage: string; userId: number | null; }): Promise { const { sessionId, agent, firstUserMessage, firstAssistantMessage, userId } = params; const strategy: TitleStrategy = (agent.titleStrategy as TitleStrategy) ?? "llm"; if (strategy === "none") { return; } if (strategy === "first-line") { const title = truncateFirstLine(firstUserMessage); if (title) { await updateSession(sessionId, { title }); logger.info("[AGENT-TITLE] first-line sessionId=%s title=%s", sessionId, title); } return; } try { const titleModelId = agent.titleModelId ?? agent.defaultModelId; if (!titleModelId) { logger.warn("[AGENT-TITLE] no titleModelId/defaultModelId for agent=%s, fallback to first-line", agent.slug); const title = truncateFirstLine(firstUserMessage); if (title) await updateSession(sessionId, { title }); return; } const resolved = await resolveModelForUser(titleModelId, userId); if (!resolved) { logger.warn("[AGENT-TITLE] titleModelId=%d not resolvable, fallback to first-line", titleModelId); const title = truncateFirstLine(firstUserMessage); if (title) await updateSession(sessionId, { title }); return; } const languageModel: LanguageModel = toLanguageModel(resolved); const prompt = `请根据以下对话生成一个简短的标题(不超过20个字,不要加引号、不要加书名号): 用户: ${firstUserMessage.slice(0, 500)} 助手: ${firstAssistantMessage.slice(0, 500)} 标题:`; const result = await generateText({ model: languageModel, prompt, maxOutputTokens: 200, }); const title = result.text.trim().slice(0, 100); if (title) { await updateSession(sessionId, { title }); logger.info("[AGENT-TITLE] llm sessionId=%s title=%s", sessionId, title); } } catch (err) { logger.error("[AGENT-TITLE] sessionId=%s error: %s", sessionId, err instanceof Error ? err.message : String(err)); } }