A composable prompt formatting library with runtime parameter substitution and conditional logic.
- Component-based architecture: Build prompts from composable components (role, goal, input, output, context, persona, tone, few-shots, guardrails, constraints, tasks, steps)
- Runtime parameter substitution: Use template strings with
${paramName}syntax - Conditional logic: Include/exclude components based on runtime conditions
- Fluent API: Chain methods for intuitive prompt building
- Type-safe: Full TypeScript support
npm install promptfmtimport { PromptBuilder, createCondition } from 'promptfmt';
const prompt = new PromptBuilder()
.role('You are a wise numerology guide')
.goal('Generate a numerology interpretation for ${userName}')
.input({
name: '${userName}',
birthday: '${birthday}',
numberType: '${numberType}',
numberValue: '${numberValue}'
})
.persona((params) => {
if (params.age > 10) {
return 'The Steady Anchor persona';
}
return 'The Clear Mirror persona';
})
.output('Write ${maxSentences} sentences')
.constraints('Do not include predictions')
.build({
userName: 'John',
birthday: '1990-01-01',
numberType: 'lifePath',
numberValue: 5,
age: 34,
maxSentences: 10
});Defines the AI's role/identity.
builder.role('You are a helpful assistant');Defines the objective of the prompt.
builder.goal('Answer user questions');Defines input data/parameters. Can accept strings or objects.
// String format
builder.input('Question: ${question}');
// Object format (auto-formatted with "- key: value")
builder.input({
name: '${userName}',
age: '${age}',
metadata: { key: 'value' } // Non-string values are JSON.stringify'd
});
// Results in:
// "- name: John
// - age: 25
// - metadata: {\"key\":\"value\"}"Defines expected output format/requirements.
builder.output('Write ${maxSentences} sentences');Provides background information.
builder.context('Today is ${date}');Defines character/personality traits.
builder.persona('The Steady Anchor persona');Defines communication style.
builder.tone('Warm and supportive');Provides example inputs/outputs. Can accept an array of strings which will be automatically formatted as "Example 1:", "Example 2:", etc.
// Using array (auto-formatted)
builder.fewShots([
'Input: Hello\nOutput: Hi there!',
'Input: How are you?\nOutput: I am doing well, thank you!'
]);
// Results in: "Example 1:\nInput: Hello\nOutput: Hi there!\n\nExample 2:\n..."
// Using string (manual formatting)
builder.fewShots('Example 1: ...');
// Using function that returns array
builder.fewShots((params) => {
return ['Example one', 'Example two'];
});Defines safety/behavior boundaries. Can accept an array of strings which will be automatically prefixed with "-".
// Using array (auto-prefixed)
builder.guardrails([
'Do not provide medical advice',
'Do not share personal information',
'Always verify facts'
]);
// Results in: "- Do not provide medical advice\n- Do not share personal information\n- Always verify facts"
// Using string (manual formatting)
builder.guardrails('Do not provide medical advice');
// Using function that returns array
builder.guardrails((params) => {
return ['Rule one', 'Rule two'];
});Defines limitations/rules. Can accept an array of strings which will be automatically prefixed with "-".
// Using array (auto-prefixed)
builder.constraints([
'Maximum 500 words',
'Response time under 2 minutes',
'Use simple language'
]);
// Results in: "- Maximum 500 words\n- Response time under 2 minutes\n- Use simple language"
// Using string (manual formatting)
builder.constraints('Maximum 500 words');
// Using function that returns array
builder.constraints((params) => {
return ['Constraint one', 'Constraint two'];
});Defines list of tasks to perform. Can accept an array of strings which will be automatically prefixed with "1.", "2.", etc.
// Using array (auto-prefixed)
builder.tasks([
'Analyze the input',
'Generate response',
'Validate output'
]);
// Results in: "1. Analyze the input\n2. Generate response\n3. Validate output"
// Using string (manual formatting)
builder.tasks('1. Analyze the input\n2. Generate response');
// Using function that returns array
builder.tasks((params) => {
return ['Task one', 'Task two'];
});Defines sequential steps to follow. Can accept an array of strings which will be automatically prefixed with "Step 1:", "Step 2:", etc.
// Using array (auto-prefixed)
builder.steps([
'Understand the requirements',
'Break down into tasks',
'Execute each task'
]);
// Results in: "Step 1: Understand the requirements\nStep 2: Break down into tasks\nStep 3: Execute each task"
// Using string (manual formatting)
builder.steps('Step 1: ...\nStep 2: ...');
// Using function that returns array
builder.steps((params) => {
return ['Step one', 'Step two'];
});Use ${paramName} syntax in template strings:
builder.role('Hello ${name}, you are ${age} years old');
const result = builder.build({ name: 'John', age: 25 });
// Result: "Hello John, you are 25 years old"Missing Parameters: If a parameter is missing, null, or undefined, the placeholder is kept as-is (allows for optional parameters):
builder.role('Hello ${name}');
const result = builder.build({}); // No params provided
// Result: "Hello ${name}" (placeholder preserved)Non-string Values: Non-string values are automatically converted to strings:
builder.role('Age: ${age}, Active: ${active}');
const result = builder.build({ age: 25, active: true });
// Result: "Age: 25, Active: true"You can also use functions for dynamic content:
builder.role((params) => {
return `Hello ${params.name}, you are ${params.age} years old`;
});Include/exclude components based on runtime conditions:
import { createCondition, RoleComponent, GoalComponent } from 'promptfmt';
builder.goal('Goal A', {
condition: createCondition(
(params) => params.age > 18,
new GoalComponent('Adult Goal'),
new GoalComponent('Child Goal')
)
});
// If age > 18, includes "Adult Goal", otherwise includes "Child Goal"
const result = builder.build({ age: 25 });Multiple Components in Conditions: You can include multiple components in the then or else clauses:
builder.role('Base role', {
condition: createCondition(
(params) => params.userType === 'premium',
[
new RoleComponent('Premium role'),
new ContextComponent('Premium context')
],
new RoleComponent('Standard role')
)
});Conditional Logic Without Else: If no else clause is provided and the condition is false, the component is excluded:
builder.goal('Optional goal', {
condition: createCondition(
(params) => params.includeGoal === true,
new GoalComponent('Optional goal')
// No else clause - component excluded if condition is false
)
});Control the order of components:
builder.goal('Goal', { order: 2 });
builder.role('Role', { order: 1 });
builder.input('Input', { order: 3 });
// Components will appear in order: Role, Goal, InputOrdering Rules:
- Components with lower order numbers appear first
- Components without an explicit order come after ordered components, maintaining their insertion order
- If no order is specified, components appear in the order they were added
Add custom labels to components:
builder.role('You are a helper', { label: 'System Role' });All component classes are available for direct instantiation:
import {
RoleComponent,
GoalComponent,
InputComponent,
OutputComponent,
ContextComponent,
PersonaComponent,
ToneComponent,
FewShotsComponent,
GuardrailsComponent,
ConstraintsComponent,
TasksComponent,
StepsComponent,
BaseComponent
} from 'promptfmt';
const customComponent = new RoleComponent('Custom role');
builder.addComponent(customComponent);The BaseComponent class provides a base for all components and includes a clone method:
import { BaseComponent, ComponentType } from 'promptfmt';
const component = new BaseComponent(ComponentType.ROLE, 'You are a helper');
const cloned = component.clone({ label: 'Custom Label' });
// Creates a copy with updated propertiesbuilder.addComponents([
new RoleComponent('Role 1'),
new GoalComponent('Goal 1'),
]);builder.clear();Empty components (with no content after parameter substitution) are automatically skipped during rendering.
Main class for building prompts.
role(content, options?)- Add role componentgoal(content, options?)- Add goal componentinput(content, options?)- Add input componentoutput(content, options?)- Add output componentcontext(content, options?)- Add context componentpersona(content, options?)- Add persona componenttone(content, options?)- Add tone componentfewShots(content, options?)- Add few-shots componentguardrails(content, options?)- Add guardrails componentconstraints(content, options?)- Add constraints componenttasks(content, options?)- Add tasks componentsteps(content, options?)- Add steps componentaddComponent(component)- Add custom componentaddComponents(components)- Add multiple componentsgetComponents()- Get all componentsclear()- Clear all componentsbuild(params?)- Build final prompt string.paramsis optional (defaults to{}). Missing parameters in template strings will keep their placeholders.
import {
substitute,
resolveContent,
extractParameters,
validateParameters
} from 'promptfmt';
// Substitute parameters in a template string
substitute('Hello ${name}', { name: 'John' }); // "Hello John"
// Resolve content (handles strings, template strings, or functions)
resolveContent('Hello ${name}', { name: 'John' }); // "Hello John"
resolveContent((params) => `Hello ${params.name}`, { name: 'John' }); // "Hello John"
// Extract parameter names from a template
extractParameters('Hello ${name}, age ${age}'); // ["name", "age"]
// Validate parameters (returns missing parameter names)
validateParameters('Hello ${name}', { name: 'John' }); // [] (no missing params)
validateParameters('Hello ${name}', {}); // ["name"] (missing params)
// Validate with strict mode (throws error if params missing)
validateParameters('Hello ${name}', {}, true);
// Throws: Error("Missing required parameters: name")import {
createCondition,
evaluateCondition,
filterComponentsByCondition
} from 'promptfmt';
import { RoleComponent, PromptComponent } from 'promptfmt';
// Create a condition
const condition = createCondition(
(params) => params.age > 18,
new RoleComponent('Adult'),
new RoleComponent('Child')
);
// Evaluate a condition and get components to include
const components = evaluateCondition(condition, { age: 25 });
// Returns: [RoleComponent('Adult')]
// Filter an array of components based on their conditions
const allComponents: PromptComponent[] = [/* ... */];
const activeComponents = filterComponentsByCondition(allComponents, { age: 25 });
// Returns only components that pass their conditionsimport {
renderComponent,
renderComponents,
RenderOptions
} from 'promptfmt';
import { RoleComponent } from 'promptfmt';
// Render a single component
const component = new RoleComponent('You are a helper');
const rendered = renderComponent(component, {});
// Returns: "Role\nYou are a helper"
// Render multiple components
const components = [
new RoleComponent('You are a helper'),
new GoalComponent('Help users')
];
const prompt = renderComponents(components, {}, {
separator: '\n\n', // Default: '\n\n'
includeLabels: true, // Default: true
skipEmpty: true // Default: true
});
// Custom render options
const options: RenderOptions = {
separator: '\n---\n',
includeLabels: false,
labelFormatter: (component) => `[${component.type.toUpperCase()}]`,
skipEmpty: false
};Default Component Labels: When using renderComponent or renderComponents, default labels are automatically applied if no custom label is provided:
role→ "Role"goal→ "Goal"input→ "Input"output→ "Output"context→ "Context"persona→ "Persona"tone→ "Tone"few-shots→ "Examples"guardrails→ "Guardrails"constraints→ "Constraints"tasks→ "Tasks"steps→ "Steps"
Note: PromptBuilder.build() does not use these default labels - it only includes labels if explicitly set via the label option.
All TypeScript types are exported for use in your code:
import type {
PromptComponent,
ComponentType,
ComponentOptions,
ParameterMap,
ContentValue,
TemplateString,
Condition,
ConditionFunction,
RenderOptions
} from 'promptfmt';PromptComponent- Base interface for all componentsComponentType- Enum of all component types (ROLE,GOAL,INPUT, etc.)ComponentOptions- Options for adding components (condition, order, label)ParameterMap- Type for parameter objects (Record<string, any>)ContentValue- Content type:string | TemplateString | ((params: ParameterMap) => string)TemplateString- Type alias for template stringsCondition- Conditional logic structure withif,then, and optionalelseConditionFunction- Function type:(params: ParameterMap) => booleanRenderOptions- Options for rendering components (separator, includeLabels, labelFormatter, skipEmpty)
See the examples/ directory for comprehensive examples demonstrating:
- Basic usage
- Parameter substitution
- Conditional logic
- Component ordering
- Custom labels
- Dynamic content with functions
- All component types
- Complex real-world scenarios
Run examples with:
npm run build
npx ts-node -r tsconfig-paths/register examples/run-all.tsMIT License - see LICENSE file for details.