112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
import type { ChatMessage } from "../llm/types";
|
||
import { getSummaryPrompt, getSystemPrompt } from "../llm/prompts";
|
||
import { assemblePaperChatMessages } from "../llm/history";
|
||
import {
|
||
buildMetadataBlock,
|
||
extractPdfContext,
|
||
type ExtractResult,
|
||
} from "./extractor";
|
||
|
||
export interface BuiltContext {
|
||
messages: ChatMessage[];
|
||
extract: ExtractResult;
|
||
warning?: string;
|
||
ready: boolean;
|
||
}
|
||
|
||
export async function buildChatMessages(options: {
|
||
item: Zotero.Item;
|
||
history: ChatMessage[];
|
||
userText: string;
|
||
selection?: string;
|
||
mode?: "chat" | "summary";
|
||
}): Promise<BuiltContext> {
|
||
const extract = await extractPdfContext(options.item);
|
||
const warnings: string[] = [];
|
||
|
||
if (!extract.text) {
|
||
return {
|
||
messages: [],
|
||
extract,
|
||
warning:
|
||
extract.error ||
|
||
"未能提取 PDF 文本(可能是扫描版或尚无文本层)。请检查附件后重试。",
|
||
ready: false,
|
||
};
|
||
}
|
||
|
||
const rawInfo =
|
||
extract.rawLength && extract.rawLength !== extract.text.length
|
||
? `(原文 ${extract.rawLength} → 提交 ${extract.text.length})`
|
||
: "";
|
||
|
||
if (extract.truncated) {
|
||
warnings.push(
|
||
`已智能截断并优先保留正文${rawInfo},来源 ${extract.source}${
|
||
extract.pageCount ? ` · ${extract.pageCount} 页` : ""
|
||
}。`,
|
||
);
|
||
} else {
|
||
warnings.push(
|
||
`已载入论文文本 ${extract.text.length} 字符(${extract.source}${
|
||
extract.pageCount ? ` · ${extract.pageCount} 页` : ""
|
||
})。`,
|
||
);
|
||
}
|
||
|
||
const meta = extract.parentItem
|
||
? buildMetadataBlock(extract.parentItem)
|
||
: "";
|
||
|
||
const paperBlock = [
|
||
meta ? `【论文元数据】\n${meta}` : "",
|
||
`【论文正文】\n${extract.text}`,
|
||
]
|
||
.filter(Boolean)
|
||
.join("\n\n");
|
||
|
||
let taskBlock: string;
|
||
if (options.mode === "summary") {
|
||
taskBlock = `【任务】
|
||
请直接基于本会话开头已提供的【论文正文】完成结构化总结。
|
||
严禁回复「请提供论文」「文本不完整无法总结」等推脱话术;信息不足时也请基于已有正文尽力总结,并简要注明局限。
|
||
|
||
${getSummaryPrompt()}`;
|
||
} else {
|
||
let question = options.userText.trim();
|
||
if (options.selection?.trim()) {
|
||
question = `【选中原文】\n${options.selection.trim()}\n\n【问题】\n${question}`;
|
||
} else {
|
||
question = `【问题】\n${question}`;
|
||
}
|
||
taskBlock = `【任务】
|
||
请直接基于本会话开头已提供的论文材料回答。
|
||
严禁要求用户再次提供论文全文。
|
||
|
||
${question}`;
|
||
}
|
||
|
||
const system = `${getSystemPrompt()}
|
||
|
||
硬性规则(必须遵守):
|
||
1. 论文材料已在本会话首条用户消息中提供(【论文正文】);后续每一轮提问仍以该材料为准。
|
||
2. 绝对禁止说「请提供论文文本」「只有引用/片段无法总结」之类的话。
|
||
3. 若正文有截断标记,仍须基于已给出的章节作答,并可选一句说明「部分中间内容经抽样」。
|
||
4. 结合此前对话继续深入回答,不要假装这是全新的、没有论文的会话。
|
||
5. 不要把参考文献列表误判为「没有正文」。`;
|
||
|
||
const messages = assemblePaperChatMessages({
|
||
system,
|
||
paperBlock,
|
||
taskBlock,
|
||
history: options.history,
|
||
});
|
||
|
||
return {
|
||
messages,
|
||
extract,
|
||
warning: warnings.length ? warnings.join(" ") : undefined,
|
||
ready: true,
|
||
};
|
||
}
|