Files
brachnha-insight/src/lib/llm/prompt-engine.test.ts
Max 3fbbb1a93b Initial commit: Brachnha Insight project setup
- Next.js 14+ with App Router and TypeScript
- Tailwind CSS and ShadCN UI styling
- Zustand state management
- Dexie.js for IndexedDB (local-first data)
- Auth.js v5 for authentication
- BMAD framework integration

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-26 12:28:43 +07:00

431 lines
16 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { generateTeacherPrompt, generateGhostwriterPrompt } from './prompt-engine';
import type { ChatMessage } from '../db';
describe('Prompt Engine - Teacher Agent', () => {
describe('generateTeacherPrompt - Venting Intent', () => {
it('should generate empathetic venting prompt with user input', () => {
const userInput = "I'm so frustrated with this bug";
const chatHistory: ChatMessage[] = [];
const intent = 'venting';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result).toContain(userInput);
expect(result.toLowerCase()).toContain('empath');
expect(result.toLowerCase()).toContain('validate');
});
it('should include probing question instruction for venting', () => {
const userInput = "This keeps breaking";
const chatHistory: ChatMessage[] = [];
const intent = 'venting';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result.toLowerCase()).toContain('probing');
expect(result.toLowerCase()).toContain('question');
});
it('should be supportive and encouraging for venting', () => {
const userInput = "I don't understand why this isn't working";
const chatHistory: ChatMessage[] = [];
const intent = 'venting';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result.toLowerCase()).toContain('supportive');
expect(result.toLowerCase()).toContain('encourag');
});
it('should specify concise response (2-3 sentences) for venting', () => {
const userInput = "Stuck on this problem";
const chatHistory: ChatMessage[] = [];
const intent = 'venting';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result).toContain('2-3 sentences');
});
it('should include chat history when available for venting', () => {
const userInput = "Still having issues";
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Having trouble with React hooks', timestamp: Date.now() },
{ role: 'assistant', content: 'What specific hook are you struggling with?', timestamp: Date.now() },
];
const intent = 'venting';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result).toContain('Previous context');
expect(result).toContain('React hooks');
});
});
describe('generateTeacherPrompt - Insight Intent', () => {
it('should generate curious insight prompt with user input', () => {
const userInput = "I finally understand closures";
const chatHistory: ChatMessage[] = [];
const intent = 'insight';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result).toContain(userInput);
expect(result.toLowerCase()).toContain('curious');
expect(result.toLowerCase()).toContain('celebrate');
});
it('should include deepening question instruction for insight', () => {
const userInput = "The key was using useMemo";
const chatHistory: ChatMessage[] = [];
const intent = 'insight';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result.toLowerCase()).toContain('deepen');
expect(result.toLowerCase()).toContain('expand');
});
it('should be encouraging for insight', () => {
const userInput = "It just clicked";
const chatHistory: ChatMessage[] = [];
const intent = 'insight';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result.toLowerCase()).toContain('encourag');
});
it('should specify concise response (2-3 sentences) for insight', () => {
const userInput = "Solved the problem";
const chatHistory: ChatMessage[] = [];
const intent = 'insight';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result).toContain('2-3 sentences');
});
it('should include chat history when available for insight', () => {
const userInput = "Got it working now";
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Trying to fix the async issue', timestamp: Date.now() },
{ role: 'assistant', content: 'Have you tried using await?', timestamp: Date.now() },
];
const intent = 'insight';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result).toContain('Previous context');
expect(result).toContain('async');
});
});
describe('generateTeacherPrompt - Edge Cases', () => {
it('should handle empty chat history gracefully', () => {
const userInput = "Help me debug";
const chatHistory: ChatMessage[] = [];
const intent = 'venting';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result).toContain('Previous context:');
expect(result).toContain('(No previous messages)');
});
it('should handle very long user input', () => {
const userInput = "I".repeat(1000) + " frustrated";
const chatHistory: ChatMessage[] = [];
const intent = 'venting';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result.length).toBeGreaterThan(0);
expect(result).toContain('...');
});
it('should handle multiple messages in chat history', () => {
const userInput = "Still confused";
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'First question', timestamp: Date.now() },
{ role: 'assistant', content: 'First answer', timestamp: Date.now() },
{ role: 'user', content: 'Second question', timestamp: Date.now() },
{ role: 'assistant', content: 'Second answer', timestamp: Date.now() },
];
const intent = 'venting';
const result = generateTeacherPrompt(userInput, chatHistory, intent);
expect(result).toContain('First question');
expect(result).toContain('Second question');
});
});
describe('generateTeacherPrompt - Format and Structure', () => {
it('should return non-empty string', () => {
const result = generateTeacherPrompt("Test", [], 'venting');
expect(result).toBeTruthy();
expect(result.length).toBeGreaterThan(0);
});
it('should include system role definition', () => {
const result = generateTeacherPrompt("Test", [], 'venting');
expect(result).toContain('You are');
});
it('should be clearly formatted for LLM consumption', () => {
const result = generateTeacherPrompt("Test input", [], 'insight');
expect(result).toMatch(/user input|your role|response/i);
});
});
});
describe('Prompt Engine - Ghostwriter Agent', () => {
describe('generateGhostwriterPrompt - Basic Functionality', () => {
it('should generate Ghostwriter prompt with chat history', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'I struggled with dependency injection today', timestamp: Date.now() },
{ role: 'assistant', content: 'What aspect was challenging?', timestamp: Date.now() },
{ role: 'user', content: 'Understanding how to inject dependencies without tight coupling', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
expect(result).toBeTruthy();
expect(result.length).toBeGreaterThan(0);
});
it('should include Ghostwriter Agent role definition', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'I learned something cool about React', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'insight');
expect(result).toContain('Ghostwriter');
expect(result.toLowerCase()).toContain('agent');
});
it('should include intent context in prompt', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Frustrated with debugging', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
expect(result).toContain('venting');
});
it('should handle insight intent context', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Finally understand closures', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'insight');
expect(result).toContain('insight');
});
it('should handle unknown intent gracefully', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Some message', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, undefined);
expect(result).toContain('unknown');
});
});
describe('generateGhostwriterPrompt - Output Format', () => {
it('should specify Markdown output format', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Learned about React hooks', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'insight');
expect(result.toLowerCase()).toContain('markdown');
expect(result.toLowerCase()).toContain('title');
expect(result.toLowerCase()).toContain('body');
expect(result.toLowerCase()).toContain('tags');
});
it('should include code block format specification', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Struggled with async/await', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
expect(result).toContain('```');
});
it('should specify professional LinkedIn-style tone', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Had a breakthrough', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'insight');
expect(result.toLowerCase()).toContain('professional');
expect(result.toLowerCase()).toContain('linkedin');
});
});
describe('generateGhostwriterPrompt - Content Requirements', () => {
it('should include constraint to avoid hallucinations', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Fixed a bug', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'insight');
expect(result.toLowerCase()).toContain('hallucinat');
});
it('should specify grounded in user input requirement', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Learned TypeScript', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'insight');
expect(result.toLowerCase()).toContain('grounded');
expect(result.toLowerCase()).toContain('user input');
});
it('should include transformation focus instruction', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'This was hard but I learned a lot', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
expect(result.toLowerCase()).toContain('transformation');
});
it('should specify word count constraint', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Understanding React now', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'insight');
expect(result).toMatch(/\d{2,3}\s*words/);
});
});
describe('generateGhostwriterPrompt - Edge Cases', () => {
it('should handle empty chat history gracefully', () => {
const chatHistory: ChatMessage[] = [];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
expect(result).toBeTruthy();
expect(result.length).toBeGreaterThan(0);
});
it('should handle very long chat history', () => {
const longHistory: ChatMessage[] = Array.from({ length: 50 }, (_, i) => ({
role: i % 2 === 0 ? 'user' : 'assistant',
content: `Message ${i} with some content to make it longer`,
timestamp: Date.now() + i * 1000,
}));
const result = generateGhostwriterPrompt(longHistory, 'venting');
expect(result).toBeTruthy();
// Should truncate to avoid token limits
expect(result.length).toBeLessThan(10000);
});
it('should handle short chat history', () => {
const shortHistory: ChatMessage[] = [
{ role: 'user', content: 'Hi', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(shortHistory, 'venting');
expect(result).toBeTruthy();
});
it('should handle chat history with only user messages', () => {
const userOnlyHistory: ChatMessage[] = [
{ role: 'user', content: 'First thought', timestamp: Date.now() },
{ role: 'user', content: 'Second thought', timestamp: Date.now() + 1000 },
{ role: 'user', content: 'Third thought', timestamp: Date.now() + 2000 },
];
const result = generateGhostwriterPrompt(userOnlyHistory, 'venting');
expect(result).toBeTruthy();
});
});
describe('generateGhostwriterPrompt - Intent-Specific Behavior', () => {
it('should emphasize reframing struggle for venting intent', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'This error is driving me crazy', timestamp: Date.now() },
{ role: 'assistant', content: 'What error are you seeing?', timestamp: Date.now() },
{ role: 'user', content: 'Cannot read property of undefined', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
expect(result.toLowerCase()).toContain('struggle');
expect(result.toLowerCase()).toContain('lesson');
});
it('should emphasize articulating breakthrough for insight intent', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'I finally got it!', timestamp: Date.now() },
{ role: 'assistant', content: 'What did you discover?', timestamp: Date.now() },
{ role: 'user', content: 'The key was understanding the render cycle', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'insight');
expect(result.toLowerCase()).toContain('breakthrough');
expect(result.toLowerCase()).toContain('articulat');
});
});
describe('generateGhostwriterPrompt - Structure and Formatting', () => {
it('should have clear section headers', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Test content', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
expect(result).toMatch(/context|requirements|output format/i);
});
it('should be properly formatted for LLM consumption', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'Test message', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
// Should have numbered lists or clear structure
expect(result).toMatch(/\d+\./);
});
it('should include chat history formatted properly', () => {
const chatHistory: ChatMessage[] = [
{ role: 'user', content: 'User message here', timestamp: Date.now() },
{ role: 'assistant', content: 'Teacher response here', timestamp: Date.now() },
];
const result = generateGhostwriterPrompt(chatHistory, 'venting');
expect(result).toContain('Chat History');
expect(result).toContain('User message here');
expect(result).toContain('Teacher response here');
});
});
});