promptforge
Version:
Adaptive Prompt Intelligence & Orchestration SDK - Manage, optimize, and serve prompts for LLMs with versioning, feedback loops, and multi-provider support
285 lines • 9.38 kB
JavaScript
import { v4 as uuidv4 } from 'uuid';
import { PromptMetadataSchema, PromptTemplateSchema, } from '../types.js';
export class PromptRegistry {
prompts;
versions;
templates;
nameToIdMap;
config;
constructor(config = {}) {
this.prompts = new Map();
this.versions = new Map();
this.templates = new Map();
this.nameToIdMap = new Map();
this.config = {
storageBackend: 'memory',
enableVersioning: true,
maxVersionsPerPrompt: 50,
...config,
};
}
/**
* Create a new prompt with metadata and optional template
*/
async createPrompt(request) {
const promptId = uuidv4();
const now = new Date();
// Check if prompt name already exists
if (this.nameToIdMap.has(request.name)) {
throw new Error(`Prompt with name "${request.name}" already exists`);
}
const metadata = {
id: promptId,
name: request.name,
description: request.description,
owner: request.owner,
tags: request.tags || [],
createdAt: now,
updatedAt: now,
version: 1,
isActive: true,
usageCount: 0,
provider: request.provider,
};
// Validate metadata
const validatedMetadata = PromptMetadataSchema.parse(metadata);
// Store prompt metadata
this.prompts.set(promptId, validatedMetadata);
this.nameToIdMap.set(request.name, promptId);
// Create first version
const version = {
id: uuidv4(),
promptId,
version: 1,
content: request.content,
metadata: validatedMetadata,
};
// Store version
this.versions.set(promptId, [version]);
// Create template if provided
if (request.template) {
const template = {
id: uuidv4(),
promptId,
content: request.content,
variables: request.template.variables || this.extractVariables(request.content),
systemPrompt: request.template.systemPrompt,
format: request.template.format || 'text',
};
const validatedTemplate = PromptTemplateSchema.parse(template);
this.templates.set(promptId, validatedTemplate);
version.template = validatedTemplate;
}
return validatedMetadata;
}
/**
* Update an existing prompt and create a new version
*/
async updatePrompt(promptId, content, metadata) {
const currentMetadata = this.prompts.get(promptId);
if (!currentMetadata) {
throw new Error(`Prompt with ID "${promptId}" not found`);
}
const versions = this.versions.get(promptId) || [];
const newVersionNumber = currentMetadata.version + 1;
// Check max versions limit
if (versions.length >= this.config.maxVersionsPerPrompt) {
// Remove oldest version
versions.shift();
}
const now = new Date();
const updatedMetadata = {
...currentMetadata,
...metadata,
version: newVersionNumber,
updatedAt: now,
parentVersion: currentMetadata.version,
};
// Update stored metadata
this.prompts.set(promptId, updatedMetadata);
// Create new version
const newVersion = {
id: uuidv4(),
promptId,
version: newVersionNumber,
content,
metadata: updatedMetadata,
};
// Update template if exists
const template = this.templates.get(promptId);
if (template) {
const updatedTemplate = {
...template,
content,
variables: this.extractVariables(content),
};
this.templates.set(promptId, updatedTemplate);
newVersion.template = updatedTemplate;
}
versions.push(newVersion);
this.versions.set(promptId, versions);
return newVersion;
}
/**
* Get prompt metadata by ID or name
*/
async getPrompt(idOrName) {
// Try by ID first
let prompt = this.prompts.get(idOrName);
// If not found, try by name
if (!prompt) {
const id = this.nameToIdMap.get(idOrName);
if (id) {
prompt = this.prompts.get(id);
}
}
return prompt;
}
/**
* Get specific version of a prompt
*/
async getPromptVersion(promptId, version) {
const versions = this.versions.get(promptId);
if (!versions || versions.length === 0) {
return undefined;
}
if (version === undefined) {
// Return latest version
return versions[versions.length - 1];
}
return versions.find(v => v.version === version);
}
/**
* Get all versions of a prompt
*/
async getPromptVersions(promptId) {
return this.versions.get(promptId) || [];
}
/**
* Get prompt template
*/
async getTemplate(promptId) {
return this.templates.get(promptId);
}
/**
* List all prompts with optional filtering
*/
async listPrompts(filter) {
let prompts = Array.from(this.prompts.values());
if (filter) {
prompts = prompts.filter(p => {
if (filter.owner && p.owner !== filter.owner)
return false;
if (filter.isActive !== undefined && p.isActive !== filter.isActive)
return false;
if (filter.tags && filter.tags.length > 0) {
const hasAllTags = filter.tags.every(tag => p.tags.includes(tag));
if (!hasAllTags)
return false;
}
return true;
});
}
return prompts;
}
/**
* Delete a prompt and all its versions
*/
async deletePrompt(promptId) {
const metadata = this.prompts.get(promptId);
if (!metadata) {
return false;
}
// Remove from name map
this.nameToIdMap.delete(metadata.name);
// Remove metadata
this.prompts.delete(promptId);
// Remove versions
this.versions.delete(promptId);
// Remove template
this.templates.delete(promptId);
return true;
}
/**
* Deactivate a prompt (soft delete)
*/
async deactivatePrompt(promptId) {
const metadata = this.prompts.get(promptId);
if (!metadata) {
return false;
}
metadata.isActive = false;
metadata.updatedAt = new Date();
this.prompts.set(promptId, metadata);
return true;
}
/**
* Increment usage count for a prompt
*/
async incrementUsage(promptId) {
const metadata = this.prompts.get(promptId);
if (metadata) {
metadata.usageCount += 1;
this.prompts.set(promptId, metadata);
}
}
/**
* Update average score for a prompt
*/
async updateScore(promptId, score) {
const metadata = this.prompts.get(promptId);
if (metadata) {
if (metadata.averageScore === undefined) {
metadata.averageScore = score;
}
else {
// Calculate running average
const totalScore = metadata.averageScore * metadata.usageCount;
metadata.averageScore = (totalScore + score) / (metadata.usageCount + 1);
}
this.prompts.set(promptId, metadata);
}
}
/**
* Search prompts by content similarity (semantic search)
*/
async searchPrompts(query, limit = 10) {
const prompts = Array.from(this.prompts.values());
// Simple text search for now (will be enhanced with embeddings)
const results = prompts.filter(p => {
const searchText = `${p.name} ${p.description || ''} ${p.tags.join(' ')}`.toLowerCase();
return searchText.includes(query.toLowerCase());
});
return results.slice(0, limit);
}
/**
* Extract template variables from content
*/
extractVariables(content) {
const variablePattern = /\{\{(\w+)\}\}/g;
const variables = [];
let match;
while ((match = variablePattern.exec(content)) !== null) {
if (!variables.includes(match[1])) {
variables.push(match[1]);
}
}
return variables;
}
/**
* Get registry statistics
*/
async getStats() {
const prompts = Array.from(this.prompts.values());
const activePrompts = prompts.filter(p => p.isActive);
const totalVersions = Array.from(this.versions.values()).reduce((sum, versions) => sum + versions.length, 0);
const totalUsage = prompts.reduce((sum, p) => sum + p.usageCount, 0);
return {
totalPrompts: prompts.length,
activePrompts: activePrompts.length,
totalVersions,
totalUsage,
};
}
}
//# sourceMappingURL=registry.js.map