File
Blob: src/worker/lib/ai/prompts.ts
| 1 | import type { AiChatMessage } from "@/worker/lib/ai/types"; |
| 2 | |
| 3 | export interface RewritePromptInput { |
| 4 | action: "proofread" | "formal" | "casual" | "simplify" | "expand"; |
| 5 | selectedText: string; |
| 6 | parentBlock: string; |
| 7 | beforeBlock: string; |
| 8 | afterBlock: string; |
| 9 | pageTitle: string; |
| 10 | } |
| 11 | |
| 12 | export interface GeneratePromptInput { |
| 13 | intent: "continue" | "explain" | "brainstorm"; |
| 14 | beforeBlock: string; |
| 15 | afterBlock: string; |
| 16 | pageTitle: string; |
| 17 | } |
| 18 | |
| 19 | export interface AskHistoryTurn { |
| 20 | role: "user" | "assistant"; |
| 21 | content: string; |
| 22 | } |
| 23 | |
| 24 | export function buildRewriteMessages(data: RewritePromptInput): AiChatMessage[] { |
| 25 | const instructions: Record<RewritePromptInput["action"], string> = { |
| 26 | proofread: "Proofread the selection. Fix grammar, spelling, and punctuation. Preserve meaning and voice.", |
| 27 | formal: "Rewrite the selection in a more formal, professional tone.", |
| 28 | casual: "Rewrite the selection in a more casual, conversational tone.", |
| 29 | simplify: "Simplify the selection so it is easier to read without losing information.", |
| 30 | expand: "Expand the selection with more detail and supporting sentences while preserving voice.", |
| 31 | }; |
| 32 | |
| 33 | const system = [ |
| 34 | "You are a writing assistant embedded in a document editor.", |
| 35 | instructions[data.action], |
| 36 | "Return only the rewritten replacement for the selection. Do not include the surrounding paragraphs, labels, quotes, or explanations.", |
| 37 | ].join(" "); |
| 38 | |
| 39 | const parts: string[] = []; |
| 40 | if (data.pageTitle) parts.push(`Page title: ${data.pageTitle}`); |
| 41 | if (data.beforeBlock) parts.push(`Previous block: ${data.beforeBlock}`); |
| 42 | if (data.parentBlock) parts.push(`Current block: ${data.parentBlock}`); |
| 43 | if (data.afterBlock) parts.push(`Next block: ${data.afterBlock}`); |
| 44 | parts.push(`Selection to rewrite:\n${data.selectedText}`); |
| 45 | |
| 46 | return [ |
| 47 | { role: "system", content: system }, |
| 48 | { role: "user", content: parts.join("\n\n") }, |
| 49 | ]; |
| 50 | } |
| 51 | |
| 52 | export function buildGenerateMessages(data: GeneratePromptInput): AiChatMessage[] { |
| 53 | const instructions: Record<GeneratePromptInput["intent"], string> = { |
| 54 | continue: |
| 55 | "Continue writing the document from the cursor. Match the existing voice, tense, and style. Produce 1-3 paragraphs.", |
| 56 | explain: "Write a clear explanation that fits naturally at the cursor, grounded in the surrounding context.", |
| 57 | brainstorm: "Brainstorm a concise bulleted list of ideas that fit the current context.", |
| 58 | }; |
| 59 | |
| 60 | const system = [ |
| 61 | "You are a writing assistant embedded in a document editor.", |
| 62 | instructions[data.intent], |
| 63 | "Return only the inserted text, with no preamble or labels.", |
| 64 | ].join(" "); |
| 65 | |
| 66 | const parts: string[] = []; |
| 67 | if (data.pageTitle) parts.push(`Page title: ${data.pageTitle}`); |
| 68 | if (data.beforeBlock) parts.push(`Text before the cursor:\n${data.beforeBlock}`); |
| 69 | if (data.afterBlock) parts.push(`Text after the cursor:\n${data.afterBlock}`); |
| 70 | parts.push("Write the continuation at the cursor."); |
| 71 | |
| 72 | return [ |
| 73 | { role: "system", content: system }, |
| 74 | { role: "user", content: parts.join("\n\n") }, |
| 75 | ]; |
| 76 | } |
| 77 | |
| 78 | export function buildAskMessages( |
| 79 | pageTitle: string, |
| 80 | pageContext: string, |
| 81 | question: string, |
| 82 | history: AskHistoryTurn[], |
| 83 | ): AiChatMessage[] { |
| 84 | const system = [ |
| 85 | "You are a helpful assistant answering questions about a document.", |
| 86 | "Use only the provided page content to answer. If the page does not contain the answer, say so.", |
| 87 | pageTitle ? `Page title: ${pageTitle}` : "", |
| 88 | pageContext ? `Page content:\n${pageContext}` : "Page content is empty.", |
| 89 | ] |
| 90 | .filter(Boolean) |
| 91 | .join("\n\n"); |
| 92 | |
| 93 | const messages: AiChatMessage[] = [{ role: "system", content: system }]; |
| 94 | for (const turn of history) { |
| 95 | messages.push({ role: turn.role, content: turn.content }); |
| 96 | } |
| 97 | messages.push({ role: "user", content: question }); |
| 98 | return messages; |
| 99 | } |