diff --git a/src/main/ai/prompt.ts b/src/main/ai/prompt.ts index 8ad65d0..147fc8f 100644 --- a/src/main/ai/prompt.ts +++ b/src/main/ai/prompt.ts @@ -1,21 +1,26 @@ import type { ParseResult } from '../services/dueDateParser.js'; -export const PROMPT_VERSION = 3; +export const PROMPT_VERSION = 4; export function buildPrompt( rawText: string, todayKst: string, - candidates: ParseResult[] = [] + candidates: ParseResult[] = [], + vocab: string[] = [] ): string { const candidateBlock = candidates.length > 0 ? `\nDate candidates extracted by a Korean rule parser (these are HINTS — you decide which is correct, or pick null): ${candidates.map((c, i) => ` ${i + 1}. ${c.iso ?? '(ambiguous)'} — matched token: "${c.matchedToken ?? '?'}" (confidence: ${c.confidence ?? 'low'})`).join('\n')}\n` : ''; + const vocabBlock = vocab.length > 0 + ? `\nExisting vocabulary tags (most-used first): ${vocab.join(', ')}\nPrefer reusing a vocabulary tag when the meaning matches; create new tags only when the meaning is genuinely new.\n` + : ''; + return `You organize raw personal notes into structured metadata. Today's date in Korea Standard Time (KST): ${todayKst} -${candidateBlock} +${candidateBlock}${vocabBlock} Input note (raw text, may be fragmented, any language): --- ${rawText} diff --git a/tests/unit/prompt.test.ts b/tests/unit/prompt.test.ts new file mode 100644 index 0000000..e19cee6 --- /dev/null +++ b/tests/unit/prompt.test.ts @@ -0,0 +1,31 @@ +import { describe, it, expect } from 'vitest'; +import { buildPrompt, PROMPT_VERSION } from '@main/ai/prompt.js'; + +describe('prompt', () => { + it('PROMPT_VERSION is 4', () => { + expect(PROMPT_VERSION).toBe(4); + }); + + it('buildPrompt with empty vocab omits vocabulary line entirely', () => { + const out = buildPrompt('hello', '2026-05-02', [], []); + expect(out).not.toContain('vocabulary'); + expect(out).not.toContain('Prefer reusing'); + }); + + it('buildPrompt with vocab includes Prefer instruction + comma-separated list', () => { + const out = buildPrompt('hello', '2026-05-02', [], ['design', 'meeting', 'qa']); + expect(out).toContain('Existing vocabulary tags'); + expect(out).toContain('design, meeting, qa'); + expect(out).toContain('Prefer reusing'); + }); + + it('vocab block appears between candidate block and JSON rules', () => { + const out = buildPrompt('hello', '2026-05-02', [], ['design']); + const candidateIdx = out.indexOf("Today's date"); + const vocabIdx = out.indexOf('Existing vocabulary'); + const jsonRulesIdx = out.indexOf('Return a JSON object'); + expect(candidateIdx).toBeGreaterThan(-1); + expect(vocabIdx).toBeGreaterThan(candidateIdx); + expect(jsonRulesIdx).toBeGreaterThan(vocabIdx); + }); +});