UNPKG

pit-manager

Version:

Centralized prompt management system for Human Behavior AI agents

180 lines (179 loc) 6.17 kB
"use strict"; /** * Simplified prompt loading and rendering for TypeScript */ Object.defineProperty(exports, "__esModule", { value: true }); exports.prompts = prompts; exports.getCurrentPromptFile = getCurrentPromptFile; exports.getParentExecutionId = getParentExecutionId; exports.clearPromptContext = clearPromptContext; const fs = require("fs"); const path = require("path"); const yaml = require("js-yaml"); // Global state for tracking current prompt file let currentPromptFile; // Global state for tracking parent execution ID from ModelResponse let parentExecutionId; /** * Load and render a prompt template with positional variables. * * Variables are matched to {{variable}} placeholders in order of appearance. * * @param templateName - Name of the template file (with or without .md extension) * @param variables - List of values to substitute in order of appearance * @returns Rendered prompt string * * @example * // If template has {{role}} and {{task}} * const prompt = prompts("assistant.md", ["expert", "analyze data"]); */ function prompts(templateName, variables) { // Find .pit directory const pitDir = findPitDirectory(); if (!pitDir) { throw new Error("No .pit directory found. Run 'pit init' first."); } // Ensure template name has .md extension if (!templateName.endsWith('.md')) { templateName += '.md'; } // Load template file const templatePath = path.join(pitDir, 'prompts', templateName); if (!fs.existsSync(templatePath)) { throw new Error(`Template not found: ${templatePath}`); } const content = fs.readFileSync(templatePath, 'utf-8'); // Parse frontmatter and template const { templateContent } = parseTemplate(content); // Extract variables in order of appearance const variableNames = extractVariables(templateContent); // Create variable mapping if (variables.length !== variableNames.length) { throw new Error(`Expected ${variableNames.length} variables ${JSON.stringify(variableNames)}, ` + `but got ${variables.length} values`); } // Process variables and detect ModelResponse objects const processedVariables = []; parentExecutionId = undefined; // Reset parent ID for (const variable of variables) { // Check if this is a ModelResponse object if (variable && typeof variable === 'object' && '_isPitResponse' in variable && variable._isPitResponse) { // Extract content from ModelResponse processedVariables.push(variable.content); // Store the execution ID for chaining (use the first one found) if (!parentExecutionId) { parentExecutionId = variable.executionId; } } else { processedVariables.push(variable); } } const variableMap = {}; variableNames.forEach((name, index) => { variableMap[name] = processedVariables[index]; }); // Render template const rendered = renderTemplate(templateContent, variableMap); // Track current prompt file for execution tracking currentPromptFile = templateName; return rendered; } /** * Find the .pit directory by searching up from current directory */ function findPitDirectory() { let current = process.cwd(); while (current !== path.dirname(current)) { const pitDir = path.join(current, '.pit'); if (fs.existsSync(pitDir) && fs.statSync(pitDir).isDirectory()) { return pitDir; } current = path.dirname(current); } // Check current directory one more time const pitDir = path.join(process.cwd(), '.pit'); if (fs.existsSync(pitDir) && fs.statSync(pitDir).isDirectory()) { return pitDir; } return null; } /** * Parse template content into frontmatter and body */ function parseTemplate(content) { if (content.startsWith('---')) { // Find the second --- to properly extract frontmatter const firstDelimiter = 0; const secondDelimiter = content.indexOf('---', firstDelimiter + 3); if (secondDelimiter !== -1) { const frontmatterStr = content.substring(firstDelimiter + 3, secondDelimiter).trim(); const templateContent = content.substring(secondDelimiter + 3).trim(); // Parse YAML frontmatter let frontmatter = {}; try { frontmatter = yaml.load(frontmatterStr) || {}; } catch (e) { // Ignore YAML errors } return { frontmatter, templateContent }; } } return { frontmatter: {}, templateContent: content }; } /** * Extract variable names from template in order of appearance */ function extractVariables(template) { // Find all {{variable}} patterns const pattern = /\{\{(\w+)\}\}/g; const matches = [...template.matchAll(pattern)]; // Remove duplicates while preserving order const seen = new Set(); const uniqueVars = []; for (const match of matches) { const varName = match[1]; if (!seen.has(varName)) { seen.add(varName); uniqueVars.push(varName); } } return uniqueVars; } /** * Render template by replacing variables */ function renderTemplate(template, variables) { let rendered = template; for (const [varName, value] of Object.entries(variables)) { const pattern = new RegExp(`\\{\\{${escapeRegExp(varName)}\\}\\}`, 'g'); rendered = rendered.replace(pattern, String(value)); } return rendered; } /** * Escape special regex characters */ function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } /** * Get the current prompt file being used */ function getCurrentPromptFile() { return currentPromptFile; } /** * Get the parent execution ID if a ModelResponse was passed to prompts() */ function getParentExecutionId() { return parentExecutionId; } /** * Clear the prompt context (used after model execution) */ function clearPromptContext() { currentPromptFile = undefined; parentExecutionId = undefined; }