loader.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /**
  2. * Prompt Loader - Loads prompts from markdown files
  3. *
  4. * Supports:
  5. * - Loading prompts from templates/{promptId}/ directory
  6. * - Snippet inclusion via {{snippet:name}} syntax
  7. * - Variable interpolation via {{variable}} syntax
  8. * - Caching for performance
  9. */
  10. import fs from 'fs';
  11. import path from 'path';
  12. import type { PromptId, LoadedPrompt, SnippetId } from './types';
  13. import { createLogger } from '@/lib/logger';
  14. const log = createLogger('PromptLoader');
  15. // Cache for loaded prompts and snippets
  16. const promptCache = new Map<string, LoadedPrompt>();
  17. const snippetCache = new Map<string, string>();
  18. /**
  19. * Get the prompts directory path
  20. */
  21. function getPromptsDir(): string {
  22. // In Next.js, use process.cwd() for the project root
  23. return path.join(process.cwd(), 'lib', 'generation', 'prompts');
  24. }
  25. /**
  26. * Load a snippet by ID
  27. */
  28. export function loadSnippet(snippetId: SnippetId): string {
  29. const cached = snippetCache.get(snippetId);
  30. if (cached) return cached;
  31. const snippetPath = path.join(getPromptsDir(), 'snippets', `${snippetId}.md`);
  32. try {
  33. const content = fs.readFileSync(snippetPath, 'utf-8').trim();
  34. snippetCache.set(snippetId, content);
  35. return content;
  36. } catch {
  37. log.warn(`Snippet not found: ${snippetId}`);
  38. return `{{snippet:${snippetId}}}`;
  39. }
  40. }
  41. /**
  42. * Process snippet includes in a template
  43. * Replaces {{snippet:name}} with actual snippet content
  44. */
  45. function processSnippets(template: string): string {
  46. return template.replace(/\{\{snippet:(\w[\w-]*)\}\}/g, (_, snippetId) => {
  47. return loadSnippet(snippetId as SnippetId);
  48. });
  49. }
  50. /**
  51. * Load a prompt by ID
  52. */
  53. export function loadPrompt(promptId: PromptId): LoadedPrompt | null {
  54. const cached = promptCache.get(promptId);
  55. if (cached) return cached;
  56. const promptDir = path.join(getPromptsDir(), 'templates', promptId);
  57. try {
  58. // Load system.md
  59. const systemPath = path.join(promptDir, 'system.md');
  60. let systemPrompt = fs.readFileSync(systemPath, 'utf-8').trim();
  61. systemPrompt = processSnippets(systemPrompt);
  62. // Load user.md (optional, may not exist)
  63. const userPath = path.join(promptDir, 'user.md');
  64. let userPromptTemplate = '';
  65. try {
  66. userPromptTemplate = fs.readFileSync(userPath, 'utf-8').trim();
  67. userPromptTemplate = processSnippets(userPromptTemplate);
  68. } catch {
  69. // user.md is optional
  70. }
  71. const loaded: LoadedPrompt = {
  72. id: promptId,
  73. systemPrompt,
  74. userPromptTemplate,
  75. };
  76. promptCache.set(promptId, loaded);
  77. return loaded;
  78. } catch (error) {
  79. log.error(`Failed to load prompt ${promptId}:`, error);
  80. return null;
  81. }
  82. }
  83. /**
  84. * Interpolate variables in a template
  85. * Replaces {{variable}} with values from the variables object
  86. */
  87. export function interpolateVariables(template: string, variables: Record<string, unknown>): string {
  88. return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
  89. const value = variables[key];
  90. if (value === undefined) return match;
  91. if (typeof value === 'object') return JSON.stringify(value, null, 2);
  92. return String(value);
  93. });
  94. }
  95. /**
  96. * Build a complete prompt with variables
  97. */
  98. export function buildPrompt(
  99. promptId: PromptId,
  100. variables: Record<string, unknown>,
  101. ): { system: string; user: string } | null {
  102. const prompt = loadPrompt(promptId);
  103. if (!prompt) return null;
  104. return {
  105. system: interpolateVariables(prompt.systemPrompt, variables),
  106. user: interpolateVariables(prompt.userPromptTemplate, variables),
  107. };
  108. }
  109. /**
  110. * Clear all caches (useful for development/testing)
  111. */
  112. export function clearPromptCache(): void {
  113. promptCache.clear();
  114. snippetCache.clear();
  115. }