| name | prompt-engine |
| description | Template-based AI prompt engine with YAML templates, brand kit injection, input sanitization for security, and token-efficient context blocks. |
| license | MIT |
| compatibility | TypeScript/JavaScript, Python |
| metadata | {"category":"ai","time":"5h","source":"drift-masterguide"} |
AI Prompt Templating Engine
Template-based prompt building with brand consistency and security.
When to Use This Skill
- Managing AI prompts across a codebase
- Need brand consistency in generated content
- Preventing prompt injection attacks
- Optimizing token usage with compact context
Core Concepts
Prompt engineering challenges:
- Scattered prompts - Hard to maintain consistency
- Brand drift - Generated content doesn't match brand
- Injection attacks - User input can hijack prompts
- Token waste - Verbose context burns budget
Implementation
TypeScript
interface PromptTemplate {
name: string;
version: string;
basePrompt: string;
placeholders: string[];
qualityModifiers: string[];
}
interface BrandKitContext {
primaryColors: string[];
accentColors: string[];
headlineFont?: string;
bodyFont?: string;
tone?: string;
}
interface ResolvedBrandContext {
primaryColor?: string;
secondaryColor?: string;
accentColor?: string;
gradient?: string;
font?: string;
tone?: string;
intensity: 'subtle' | 'balanced' | 'strong';
}
const MAX_INPUT_LENGTH = 500;
const SANITIZE_PATTERN = /[<>{}\[\]\\|`~]/g;
const INJECTION_PATTERNS = [
/ignore\s+(previous|above|all)/i,
/disregard\s+(previous|above|all)/i,
/system\s*:/i,
/assistant\s*:/i,
/\[INST\]/i,
/<<SYS>>/i,
];
function sanitizeInput(input: string): string {
if (input.length > MAX_INPUT_LENGTH) {
input = input.slice(0, MAX_INPUT_LENGTH);
}
input = input.replace(SANITIZE_PATTERN, '');
for (const pattern of INJECTION_PATTERNS) {
if (pattern.test(input)) {
throw new Error('Potential prompt injection detected');
}
}
return input.trim();
}
function sanitizePlaceholders(placeholders: Record<string, string>): Record<string, string> {
const sanitized: Record<string, string> = {};
for (const [key, value] of Object.entries(placeholders)) {
sanitized[key] = sanitizeInput(value);
}
return sanitized;
}
class BrandContextResolver {
resolve(
brandKit: BrandKitContext,
options: {
primaryColorIndex?: number;
secondaryColorIndex?: number;
accentColorIndex?: number;
useGradient?: boolean;
intensity?: 'subtle' | 'balanced' | 'strong';
} = {}
): ResolvedBrandContext {
const primaryColor = this.resolveColor(brandKit.primaryColors, options.primaryColorIndex ?? 0);
const secondaryColor = this.resolveColor(brandKit.primaryColors, options.secondaryColorIndex ?? 1);
const accentColor = this.resolveColor(brandKit.accentColors, options.accentColorIndex ?? 0);
const gradient = options.useGradient && primaryColor && secondaryColor
? `${primaryColor}→${secondaryColor}`
: undefined;
return {
primaryColor,
secondaryColor,
accentColor,
gradient,
font: brandKit.headlineFont,
tone: brandKit.tone,
intensity: options.intensity || 'balanced',
};
}
private resolveColor(colors: string[], index: number): string | undefined {
if (!colors.length) return undefined;
return colors[Math.min(index, colors.length - 1)];
}
}
function toCompactBrandBlock(ctx: ResolvedBrandContext): string {
const parts: string[] = [];
const colors = [ctx.primaryColor, ctx.secondaryColor, ctx.accentColor].filter(Boolean);
if (colors.length) parts.push(`Colors: ${colors.join(', ')}`);
if (ctx.gradient) parts.push(`Gradient: ${ctx.gradient}`);
if (ctx.font) parts.push(`Font: ${ctx.font}`);
if (ctx.tone) parts.push(`Tone: ${ctx.tone}`);
if (!parts.length) return '';
return `[BRAND: ${ctx.intensity} - ${parts.join(' | ')}]`;
}
const templateCache = new Map<string, PromptTemplate>();
async function loadTemplate(templateName: string): Promise<PromptTemplate> {
if (templateCache.has(templateName)) {
return templateCache.get(templateName)!;
}
const normalized = templateName.replace(/\.\./g, '').replace(/[<>:"|?*]/g, '');
const content = await fs.readFile(`prompts/${normalized}.yaml`, 'utf-8');
const data = yaml.load(content) as any;
const template: PromptTemplate = {
name: data.name || templateName,
version: data.version || '1.0.0',
basePrompt: data.base_prompt,
placeholders: data.placeholders || [],
qualityModifiers: data.quality_modifiers || [],
};
for (const placeholder of template.placeholders) {
if (!template.basePrompt.includes(`{${placeholder}}`)) {
throw new Error(`Placeholder {${placeholder}} not found in template`);
}
}
templateCache.set(templateName, template);
return template;
}
const INTENSITY_MODIFIERS = {
subtle: 'subtly incorporate',
balanced: 'use',
strong: 'prominently feature',
};
class PromptEngine {
private brandResolver = new BrandContextResolver();
async buildPrompt(
templateName: string,
placeholders: Record<string, string>,
brandKit?: BrandKitContext,
brandOptions?: Parameters<BrandContextResolver['resolve']>[1]
): Promise<string> {
const sanitizedPlaceholders = sanitizePlaceholders(placeholders);
const template = await loadTemplate(templateName);
let prompt = template.basePrompt;
for (const [key, value] of Object.entries(sanitizedPlaceholders)) {
prompt = prompt.replace(new RegExp(`\\{${key}\\}`, 'g'), value);
}
if (brandKit) {
const resolved = this.brandResolver.resolve(brandKit, brandOptions);
const brandBlock = toCompactBrandBlock(resolved);
if (brandBlock) {
const modifier = INTENSITY_MODIFIERS[resolved.intensity];
prompt = `${prompt}\n\n${modifier} the following brand guidelines:\n${brandBlock}`;
}
}
if (template.qualityModifiers.length) {
prompt = `${prompt}\n\nQuality: ${template.qualityModifiers.join(', ')}`;
}
return prompt;
}
}
export const promptEngine = new PromptEngine();
Python
import re
import yaml
from dataclasses import dataclass
from typing import Dict, List, Optional
from pathlib import Path
MAX_INPUT_LENGTH = 500
SANITIZE_PATTERN = re.compile(r'[<>{}\[\]\\|`~]')
INJECTION_PATTERNS = [
re.compile(r'ignore\s+(previous|above|all)', re.I),
re.compile(r'disregard\s+(previous|above|all)', re.I),
re.compile(r'system\s*:', re.I),
re.compile(r'assistant\s*:', re.I),
re.compile(r'\[INST\]', re.I),
]
def sanitize_input(input_str: str) -> str:
if len(input_str) > MAX_INPUT_LENGTH:
input_str = input_str[:MAX_INPUT_LENGTH]
input_str = SANITIZE_PATTERN.sub('', input_str)
for pattern in INJECTION_PATTERNS:
if pattern.search(input_str):
raise ValueError("Potential prompt injection detected")
return input_str.strip()
@dataclass
class PromptTemplate:
name: str
version: str
base_prompt: str
placeholders: List[str]
quality_modifiers: []
:
primary_colors: []
accent_colors: []
headline_font: [] =
tone: [] =
:
primary_color: [] =
secondary_color: [] =
accent_color: [] =
gradient: [] =
font: [] =
tone: [] =
intensity: =
:
() -> ResolvedBrandContext:
primary = ._resolve_color(brand_kit.primary_colors, primary_index)
secondary = ._resolve_color(brand_kit.primary_colors, secondary_index)
accent = ._resolve_color(brand_kit.accent_colors, accent_index)
gradient = use_gradient primary secondary
ResolvedBrandContext(
primary_color=primary,
secondary_color=secondary,
accent_color=accent,
gradient=gradient,
font=brand_kit.headline_font,
tone=brand_kit.tone,
intensity=intensity,
)
() -> []:
colors:
colors[(index, (colors) - )]
() -> :
parts = []
colors = [c c [ctx.primary_color, ctx.secondary_color, ctx.accent_color] c]
colors:
parts.append()
ctx.gradient:
parts.append()
ctx.font:
parts.append()
ctx.tone:
parts.append()
parts:
_template_cache: [, PromptTemplate] = {}
() -> PromptTemplate:
template_name _template_cache:
_template_cache[template_name]
safe_name = template_name.replace(, ).replace(, )
path = Path() /
(path) f:
data = yaml.safe_load(f)
template = PromptTemplate(
name=data.get(, template_name),
version=data.get(, ),
base_prompt=data[],
placeholders=data.get(, []),
quality_modifiers=data.get(, []),
)
_template_cache[template_name] = template
template
INTENSITY_MODIFIERS = {
: ,
: ,
: ,
}
:
():
._brand_resolver = BrandContextResolver()
() -> :
sanitized = {k: sanitize_input(v) k, v placeholders.items()}
template = load_template(template_name)
prompt = template.base_prompt
key, value sanitized.items():
prompt = prompt.replace(, value)
brand_kit:
resolved = ._brand_resolver.resolve(
brand_kit, intensity=intensity, use_gradient=use_gradient
)
brand_block = to_compact_brand_block(resolved)
brand_block:
modifier = INTENSITY_MODIFIERS[resolved.intensity]
prompt =
template.quality_modifiers:
prompt =
prompt
prompt_engine = PromptEngine()
Template Example
name: thumbnail_gaming
version: "1.0.0"
base_prompt: |
Create a {game_name} thumbnail.
Feature {subject} with {emotion} expression.
Style: {style}
placeholders:
- game_name
- subject
- emotion
- style
quality_modifiers:
- ultra detailed
- cinematic lighting
- 8K quality
Usage Examples
const prompt = await promptEngine.buildPrompt(
'thumbnail_gaming',
{
game_name: 'Cyberpunk 2077',
subject: 'character with katana',
emotion: 'intense',
style: 'neon cyberpunk',
},
{
primaryColors: ['#FF00FF', '#00FFFF'],
accentColors: ['#FFFF00'],
headlineFont: 'Orbitron',
tone: 'edgy',
},
{ useGradient: true, intensity: 'strong' }
);
Best Practices
- Sanitize all user inputs before substitution
- Use compact brand blocks to save tokens
- Cache templates for performance
- Validate placeholders exist in templates
- Use intensity modifiers for brand prominence
Common Mistakes
- No input sanitization (injection vulnerability)
- Verbose brand context (wastes tokens)
- Hardcoded prompts (inconsistent)
- Missing placeholder validation
- No template caching (slow)
Related Patterns
- ai-generation-client - Use prompts with AI APIs
- rate-limiting - Protect AI quota
- validation-quarantine - Validate AI outputs