Skip to content
File

Blob: src/worker/lib/ai/prompts.ts

typescript100 lines
1import type { AiChatMessage } from "@/worker/lib/ai/types";
2 
3export 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 
12export interface GeneratePromptInput {
13 intent: "continue" | "explain" | "brainstorm";
14 beforeBlock: string;
15 afterBlock: string;
16 pageTitle: string;
17}
18 
19export interface AskHistoryTurn {
20 role: "user" | "assistant";
21 content: string;
22}
23 
24export 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 
52export 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 
78export 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}