import { askDocDoe } from "@/lib/api/brain"; import { getPublicDemoMode } from "@/lib/env"; import { hasLiveSession } from "@/lib/api/mode"; import type { BrainAskMode, BrainAskResponse, DocumentChunk, StudentLevel } from "@/lib/api/types"; export type DocDoeAnswer = { title: string; summary: string; points: string[]; examTip: string; citations: string[]; evidenceLabel?: string | null; }; type AskDocDoeInput = { documentId: string; sourceTitle: string; sourceIds?: string[]; sourceTitles?: string[]; question: string; language?: string; level?: StudentLevel; mode?: string; }; function createFallbackAnswer(input: AskDocDoeInput, chunks: DocumentChunk[]): DocDoeAnswer { const bestChunk = chunks[0]?.chunk_text; return { title: `Study insight: ${input.question.slice(0, 42)}${input.question.length > 42 ? "..." : ""}`, summary: bestChunk ? `From ${input.sourceTitle}: ${bestChunk}` : `Choose a ready source and DocDoe will answer from your material. For now, focus on the core idea and turn it into short exam points.`, points: chunks.slice(0, 3).map((chunk) => chunk.heading || chunk.chunk_text).filter(Boolean), examTip: "Exam tip: write the definition first, then add keywords, then finish with one clean example or diagram if needed.", citations: chunks.length > 0 ? chunks.map((chunk) => chunk.heading || input.sourceTitle).slice(0, 3) : [input.sourceTitle], }; } function isExamPrompt(question: string) { const text = question.toLowerCase(); return text.includes("mark") || text.includes("exam") || text.includes("answer") || text.includes("pyq"); } function askModeFromPrompt(question: string, explicitMode?: string): BrainAskMode { if (explicitMode === "explain_simple" || explicitMode === "exam_answer" || explicitMode === "pyq_pattern" || explicitMode === "summarize" || explicitMode === "diagram_help") { return explicitMode; } const text = question.toLowerCase(); if (text.includes("pyq") || text.includes("pattern")) return "pyq_pattern"; if (isExamPrompt(question)) return "exam_answer"; if (text.includes("summar") || text.includes("notes") || text.includes("points")) return "summarize"; if (text.includes("diagram")) return "diagram_help"; return "explain_simple"; } function brainAnswerToDocDoeAnswer(input: AskDocDoeInput, response: BrainAskResponse): DocDoeAnswer { const sectionPoints = response.sections .flatMap((section) => section.body.split(/\n+/).map((line) => line.replace(/^[-*\d.\s]+/, "").trim())) .filter(Boolean); const sourceTitleById = new Map(); input.sourceIds?.forEach((id, index) => { const title = input.sourceTitles?.[index]; if (title) sourceTitleById.set(id, title); }); const citations = response.citations .map((citation) => citation.source_title || (citation.source_id ? sourceTitleById.get(citation.source_id) : null) || citation.heading || citation.snippet) .filter((citation): citation is string => Boolean(citation)); return { title: response.sections[0]?.title || `DocDoe answer: ${input.question.slice(0, 42)}${input.question.length > 42 ? "..." : ""}`, summary: response.answer, points: sectionPoints.length > 0 ? sectionPoints.slice(0, 6) : response.suggested_actions.slice(0, 4), examTip: response.trust_note || "Use keywords from your source and keep the answer point-wise.", citations: citations.length > 0 ? Array.from(new Set(citations)).slice(0, 6) : input.sourceTitles?.slice(0, 5) ?? [input.sourceTitle], evidenceLabel: response.evidence_label ?? null, }; } export async function askDocDoeFromSource(input: AskDocDoeInput): Promise { const sourceIds = input.sourceIds?.length ? input.sourceIds : [input.documentId]; if (getPublicDemoMode() === "live" && !hasLiveSession()) { // Only block if no session at all — local sources still work via fallback if (sourceIds.length === 0) { throw new Error("Sign in to use real DocDoe answers."); } } if (input.question.toLowerCase().includes("pyq") && !input.sourceTitle.toLowerCase().includes("pyq")) { const fallback = createFallbackAnswer(input, []); return { ...fallback, title: `Likely questions from this material`, summary: "No question papers uploaded yet. Here are exam-style questions from your uploaded material instead.", points: [ "Upload a previous question paper to unlock PYQ pattern analysis.", ...fallback.points.slice(0, 3), ], evidenceLabel: "Based on uploaded material only", }; } const response = await askDocDoe({ question: input.question, mode: askModeFromPrompt(input.question, input.mode), source_id: input.documentId, source_ids: sourceIds, level: input.level ?? "intermediate", language_preference: input.language ?? "English", }); return brainAnswerToDocDoeAnswer(input, response); }