You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

84 lines
2.8 KiB

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<void> {
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));
}
}