import { apiError } from "@/lib/api"; import { startChatRun } from "@/lib/chat-runs"; import { createChatRunWithMessages, getActiveChatRun, getConversation, getConversationContinuation, getDefaultConversation, getSkill, listMessages, } from "@/lib/db"; import { hasDeepSeekApiKey } from "@/lib/deepseek"; import { pinSkillsToSnapshots, snapshotsForSkills, } from "@/lib/skill-snapshots"; import { classifyContinuationReply } from "@/lib/skill-continuation"; import type { ChatMessage, Skill } from "@/lib/types"; import { createId } from "@/lib/utils"; import { chatRequestSchema } from "@/lib/validation"; export const runtime = "nodejs"; export const maxDuration = 120; export async function GET() { try { const conversation = getDefaultConversation(); const continuation = getConversationContinuation(conversation.id); return Response.json({ conversation, messages: listMessages(conversation.id), activeRun: getActiveChatRun(conversation.id), retainedSkillIds: continuation.skillIds, continuation, apiConfigured: hasDeepSeekApiKey(), }); } catch (error) { return apiError(error, "无法读取聊天记录"); } } export async function POST(request: Request) { try { const input = chatRequestSchema.parse(await request.json()); if (!getConversation(input.conversationId)) { return Response.json({ error: "会话不存在" }, { status: 404 }); } const existingMessages = listMessages(input.conversationId); const continuation = getConversationContinuation( input.conversationId, ); const currentSelectedSkills = [...new Set(input.selectedSkillIds)] .map((id) => getSkill(id)) .filter( (skill): skill is Skill => skill?.status === "active", ); const replyKind = classifyContinuationReply(input.message); const continuesExistingTask = !["complete", "abandoned"].includes(continuation.state) && replyKind !== "new_topic" && replyKind !== "decline"; const retainedSnapshots = continuesExistingTask ? continuation.skillSnapshots : []; const selectedSkills = pinSkillsToSnapshots( currentSelectedSkills, retainedSnapshots, ); const skillSnapshots = snapshotsForSkills( selectedSkills, retainedSnapshots, ); const userMessageId = createId("message"); const assistantMessageId = createId("message"); const runId = createId("run"); const now = new Date().toISOString(); const creation = createChatRunWithMessages({ conversationId: input.conversationId, userMessage: { id: userMessageId, role: "user", content: input.message, selectedSkillIds: selectedSkills.map((skill) => skill.id), usedSkillIds: [], skillSnapshots, skillExecutions: [], status: "complete", createdAt: now, }, assistantMessage: { id: assistantMessageId, role: "assistant", content: "", selectedSkillIds: selectedSkills.map((skill) => skill.id), usedSkillIds: [], skillSnapshots, skillExecutions: [], status: "streaming", createdAt: new Date(Date.now() + 1).toISOString(), }, run: { id: runId, clientRequestId: input.clientRequestId, userMessageId, assistantMessageId, }, }); if (creation.outcome === "existing") { return Response.json({ run: creation.run }); } if (creation.outcome === "active_conflict") { return Response.json( { error: "当前已有一轮回答正在进行", run: creation.run }, { status: 409 }, ); } const run = creation.run; const history = existingMessages .filter((message) => message.status !== "error") .slice(-20) .map((message) => ({ role: message.role, content: message.content, })) satisfies Array<{ role: ChatMessage["role"]; content: string; }>; startChatRun({ runId, conversationId: input.conversationId, userMessage: input.message, selectedSkills, skillSnapshots, continuation, history, }); return Response.json({ run }, { status: 202 }); } catch (error) { return apiError(error, "无法发送消息"); } }