SDK Implementation Examples
This guide provides practical examples of implementing the PromptCompose SDK in various frameworks and use cases.
Basic Usage Examples
Simple Prompt Resolution
import { PromptCompose } from '@promptcompose/sdk';
const promptCompose = new PromptCompose(
process.env.PROMPT_COMPOSE_API_KEY!,
process.env.PROMPT_COMPOSE_PROJECT_ID!
);
async function getWelcomeMessage(userName: string): Promise<string> {
try {
await promptCompose.init();
const result = await promptCompose.resolvePrompt('welcome-prompt', undefined, {
userName: userName,
currentDate: new Date().toLocaleDateString()
});
return result.content;
} catch (error) {
console.error('Failed to get welcome message:', error);
return `Welcome, ${userName}!`; // Fallback
}
}
// Usage
const message = await getWelcomeMessage('John Doe');
console.log(message);
A/B Testing Example
async function getPersonalizedMessage(userId: string, userName: string) {
const result = await promptCompose.resolvePrompt('personalized-greeting', {
config: {
abTesting: {
sessionId: `user-${userId}`
}
}
}, {
userName: userName
});
// Track which variant was served
if (result.variant) {
console.log(`User ${userId} saw variant: ${result.variant.name}`);
// Store for conversion tracking
return {
content: result.content,
testId: result.abTest?.publicId,
variantId: result.variant.publicId
};
}
return { content: result.content };
}
// Report conversion when user performs desired action
async function reportSuccess(testId: string, variantId: string, userId: string) {
await promptCompose.reportABResult(testId, {
variantId: variantId,
status: 'success',
sessionId: `user-${userId}`
});
}
Framework Integration Examples
React Application
Custom Hook
// hooks/usePromptCompose.ts
import { useState, useEffect, useCallback } from 'react';
import { PromptCompose } from '@promptcompose/sdk';
interface UsePromptComposeReturn {
resolvePrompt: (id: string, variables?: any) => Promise<string>;
loading: boolean;
error: string | null;
}
export function usePromptCompose(): UsePromptComposeReturn {
const [sdk, setSdk] = useState<PromptCompose | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const initSDK = async () => {
try {
const promptCompose = new PromptCompose(
process.env.REACT_APP_PROMPT_COMPOSE_API_KEY!,
process.env.REACT_APP_PROMPT_COMPOSE_PROJECT_ID!
);
await promptCompose.init();
setSdk(promptCompose);
} catch (err) {
setError(err instanceof Error ? err.message : 'Initialization failed');
}
};
initSDK();
}, []);
const resolvePrompt = useCallback(async (
promptId: string,
variables?: any
): Promise<string> => {
if (!sdk) throw new Error('SDK not initialized');
setLoading(true);
setError(null);
try {
const result = await sdk.resolvePrompt(promptId, undefined, variables);
return result.content;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
}, [sdk]);
return { resolvePrompt, loading, error };
}
Component Usage
// components/WelcomeMessage.tsx
import React, { useEffect, useState } from 'react';
import { usePromptCompose } from '../hooks/usePromptCompose';
interface WelcomeMessageProps {
userId: string;
userName: string;
}
export function WelcomeMessage({ userId, userName }: WelcomeMessageProps) {
const { resolvePrompt, loading, error } = usePromptCompose();
const [message, setMessage] = useState<string>('');
useEffect(() => {
const fetchMessage = async () => {
try {
const content = await resolvePrompt('welcome-message', {
userId,
userName,
timestamp: Date.now()
});
setMessage(content);
} catch (error) {
console.error('Failed to fetch welcome message:', error);
setMessage(`Welcome, ${userName}!`);
}
};
fetchMessage();
}, [userId, userName, resolvePrompt]);
if (loading) {
return <div className="loading">Loading message...</div>;
}
if (error) {
return <div className="error">Failed to load message</div>;
}
return (
<div className="welcome-message">
{message}
</div>
);
}
Context Provider
// context/PromptComposeContext.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { PromptCompose } from '@promptcompose/sdk';
interface PromptComposeContextType {
sdk: PromptCompose | null;
isReady: boolean;
error: string | null;
}
const PromptComposeContext = createContext<PromptComposeContextType>({
sdk: null,
isReady: false,
error: null
});
export function PromptComposeProvider({ children }: { children: React.ReactNode }) {
const [sdk, setSdk] = useState<PromptCompose | null>(null);
const [isReady, setIsReady] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const initSDK = async () => {
try {
const promptCompose = new PromptCompose(
process.env.REACT_APP_PROMPT_COMPOSE_API_KEY!,
process.env.REACT_APP_PROMPT_COMPOSE_PROJECT_ID!
);
await promptCompose.init();
setSdk(promptCompose);
setIsReady(true);
} catch (err) {
setError(err instanceof Error ? err.message : 'Initialization failed');
}
};
initSDK();
}, []);
return (
<PromptComposeContext.Provider value={{ sdk, isReady, error }}>
{children}
</PromptComposeContext.Provider>
);
}
export function usePromptComposeContext() {
const context = useContext(PromptComposeContext);
if (!context) {
throw new Error('usePromptComposeContext must be used within PromptComposeProvider');
}
return context;
}
Next.js Integration
API Route
// pages/api/prompts/[id].ts
import { NextApiRequest, NextApiResponse } from 'next';
import { PromptCompose } from '@promptcompose/sdk';
const promptCompose = new PromptCompose(
process.env.PROMPT_COMPOSE_API_KEY!,
process.env.PROMPT_COMPOSE_PROJECT_ID!
);
// Initialize once when the API route is first loaded
let initialized = false;
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!initialized) {
await promptCompose.init();
initialized = true;
}
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const { id } = req.query;
const { variables, config, sessionId } = req.body;
try {
const result = await promptCompose.resolvePrompt(
id as string,
config || {
config: {
abTesting: { sessionId }
}
},
variables
);
res.status(200).json({
content: result.content,
variant: result.variant?.name,
test: result.abTest?.name
});
} catch (error) {
console.error('Prompt resolution failed:', error);
res.status(500).json({
error: error instanceof Error ? error.message : 'Unknown error'
});
}
}
Client-side Usage
// lib/prompts.ts
export async function resolvePrompt(
promptId: string,
variables?: any,
sessionId?: string
) {
const response = await fetch(`/api/prompts/${promptId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
variables,
sessionId
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
// Usage in a component
import { useEffect, useState } from 'react';
import { resolvePrompt } from '../lib/prompts';
export function DynamicContent({ userId, contentType }: any) {
const [content, setContent] = useState('');
useEffect(() => {
const fetchContent = async () => {
try {
const result = await resolvePrompt('dynamic-content', {
contentType,
userId
}, userId);
setContent(result.content);
} catch (error) {
console.error('Failed to load content:', error);
setContent('Default content');
}
};
fetchContent();
}, [userId, contentType]);
return <div dangerouslySetInnerHTML={{ __html: content }} />;
}
App Router (Next.js 13+)
// app/api/prompts/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { PromptCompose } from '@promptcompose/sdk';
const promptCompose = new PromptCompose(
process.env.PROMPT_COMPOSE_API_KEY!,
process.env.PROMPT_COMPOSE_PROJECT_ID!
);
export async function POST(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
await promptCompose.init();
const body = await request.json();
const { variables, sessionId } = body;
const result = await promptCompose.resolvePrompt(params.id, {
config: {
abTesting: { sessionId }
}
}, variables);
return NextResponse.json({
content: result.content,
variant: result.variant?.name,
test: result.abTest?.name
});
} catch (error) {
console.error('Prompt resolution failed:', error);
return NextResponse.json(
{ error: 'Failed to resolve prompt' },
{ status: 500 }
);
}
}
Express.js Backend
Middleware
// middleware/promptCompose.ts
import { Request, Response, NextFunction } from 'express';
import { PromptCompose } from '@promptcompose/sdk';
interface PromptComposeRequest extends Request {
promptCompose?: {
resolve: (promptId: string, variables?: any) => Promise<any>;
reportConversion: (testId: string, variantId: string, success: boolean) => Promise<void>;
};
}
export function promptComposeMiddleware(
promptCompose: PromptCompose
) {
return async (req: PromptComposeRequest, res: Response, next: NextFunction) => {
const userId = req.user?.id || req.sessionID;
req.promptCompose = {
resolve: async (promptId: string, variables?: any) => {
return await promptCompose.resolvePrompt(promptId, {
config: {
abTesting: { sessionId: `user-${userId}` }
}
}, variables);
},
reportConversion: async (testId: string, variantId: string, success: boolean) => {
return await promptCompose.reportABResult(testId, {
variantId,
status: success ? 'success' : 'fail',
sessionId: `user-${userId}`
});
}
};
next();
};
}
Server Setup
// server.ts
import express from 'express';
import { PromptCompose } from '@promptcompose/sdk';
import { promptComposeMiddleware } from './middleware/promptCompose';
const app = express();
app.use(express.json());
// Initialize PromptCompose SDK
const promptCompose = new PromptCompose(
process.env.PROMPT_COMPOSE_API_KEY!,
process.env.PROMPT_COMPOSE_PROJECT_ID!
);
// Initialize SDK on server startup
promptCompose.init().then(() => {
console.log('PromptCompose SDK initialized');
}).catch(console.error);
// Apply middleware
app.use(promptComposeMiddleware(promptCompose));
// Email generation endpoint
app.post('/api/emails/welcome', async (req: any, res) => {
try {
const { userEmail, userName } = req.body;
const result = await req.promptCompose.resolve('welcome-email', {
userName,
userEmail,
companyName: 'Your Company'
});
// Send the email (using your email service)
await sendEmail({
to: userEmail,
subject: 'Welcome!',
html: result.content
});
res.json({
success: true,
variant: result.variant?.name
});
} catch (error) {
console.error('Failed to send welcome email:', error);
res.status(500).json({ error: 'Failed to send email' });
}
});
// Conversion tracking endpoint
app.post('/api/conversions/:testId/:variantId', async (req: any, res) => {
try {
const { testId, variantId } = req.params;
const { success } = req.body;
await req.promptCompose.reportConversion(testId, variantId, success);
res.json({ success: true });
} catch (error) {
console.error('Failed to report conversion:', error);
res.status(500).json({ error: 'Failed to report conversion' });
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Advanced Use Cases
E-commerce Product Recommendations
// services/ProductRecommendationService.ts
import { PromptCompose } from '@promptcompose/sdk';
interface Product {
id: string;
name: string;
price: number;
category: string;
}
interface User {
id: string;
name: string;
purchaseHistory: string[];
preferences: string[];
}
class ProductRecommendationService {
private promptCompose: PromptCompose;
constructor(apiKey: string, projectId: string) {
this.promptCompose = new PromptCompose(apiKey, projectId);
}
async getRecommendationMessage(
user: User,
products: Product[]
): Promise<string> {
const result = await this.promptCompose.resolvePrompt('product-recommendation', {
config: {
abTesting: { sessionId: `user-${user.id}` }
}
}, {
userName: user.name,
recentPurchases: user.purchaseHistory.slice(-3).join(', '),
preferences: user.preferences.join(', '),
recommendedProducts: products.map(p => `${p.name} ($${p.price})`).join(', '),
productCount: products.length
});
return result.content;
}
async trackPurchaseConversion(
user: User,
testId: string,
variantId: string,
purchasedProductId: string
) {
await this.promptCompose.reportABResult(testId, {
variantId,
status: 'success',
sessionId: `user-${user.id}`
});
console.log(`Purchase conversion tracked for user ${user.id}`);
}
}
// Usage
const recommendationService = new ProductRecommendationService(
process.env.PROMPT_COMPOSE_API_KEY!,
process.env.PROMPT_COMPOSE_PROJECT_ID!
);
const user = {
id: '123',
name: 'John Doe',
purchaseHistory: ['laptop', 'mouse', 'keyboard'],
preferences: ['electronics', 'productivity']
};
const products = [
{ id: '1', name: 'Monitor', price: 299, category: 'electronics' },
{ id: '2', name: 'Webcam', price: 89, category: 'electronics' }
];
const message = await recommendationService.getRecommendationMessage(user, products);
Customer Support Automation
// services/SupportService.ts
interface SupportTicket {
id: string;
userId: string;
category: 'billing' | 'technical' | 'general';
priority: 'low' | 'medium' | 'high' | 'urgent';
description: string;
userTier: 'free' | 'pro' | 'enterprise';
}
class SupportService {
private promptCompose: PromptCompose;
constructor(apiKey: string, projectId: string) {
this.promptCompose = new PromptCompose(apiKey, projectId);
}
async generateResponse(ticket: SupportTicket): Promise<{
response: string;
shouldEscalate: boolean;
}> {
// Choose prompt based on category
const promptId = this.getPromptForCategory(ticket.category);
const result = await this.promptCompose.resolvePrompt(promptId, {
config: {
abTesting: { sessionId: `ticket-${ticket.id}` }
}
}, {
ticketId: ticket.id,
category: ticket.category,
priority: ticket.priority,
description: ticket.description,
userTier: ticket.userTier,
currentTime: new Date().toLocaleString()
});
// Determine if escalation is needed based on priority
const shouldEscalate = ticket.priority === 'urgent' ||
ticket.userTier === 'enterprise';
return {
response: result.content,
shouldEscalate
};
}
async trackResolutionSuccess(
ticketId: string,
testId: string,
variantId: string,
resolved: boolean
) {
await this.promptCompose.reportABResult(testId, {
variantId,
status: resolved ? 'success' : 'fail',
sessionId: `ticket-${ticketId}`
});
}
private getPromptForCategory(category: string): string {
const prompts = {
'billing': 'support-billing-response',
'technical': 'support-technical-response',
'general': 'support-general-response'
};
return prompts[category] || prompts['general'];
}
}
Content Personalization
// services/ContentPersonalizationService.ts
interface UserProfile {
id: string;
interests: string[];
readingLevel: 'beginner' | 'intermediate' | 'advanced';
preferredTone: 'formal' | 'casual' | 'friendly';
location: string;
timezone: string;
}
class ContentPersonalizationService {
private promptCompose: PromptCompose;
constructor(apiKey: string, projectId: string) {
this.promptCompose = new PromptCompose(apiKey, projectId);
}
async generatePersonalizedContent(
contentType: string,
topic: string,
user: UserProfile
): Promise<string> {
const promptId = `content-${contentType}`;
const result = await this.promptCompose.resolvePrompt(promptId, {
config: {
abTesting: { sessionId: `user-${user.id}` }
}
}, {
topic: topic,
interests: user.interests.join(', '),
readingLevel: user.readingLevel,
tone: user.preferredTone,
location: user.location,
localTime: new Date().toLocaleString('en-US', {
timeZone: user.timezone
})
});
return result.content;
}
async trackEngagement(
user: UserProfile,
testId: string,
variantId: string,
engagementType: 'view' | 'click' | 'share' | 'complete'
) {
const successTypes = ['click', 'share', 'complete'];
const success = successTypes.includes(engagementType);
await this.promptCompose.reportABResult(testId, {
variantId,
status: success ? 'success' : 'fail',
sessionId: `user-${user.id}`
});
}
}
// Usage
const personalizationService = new ContentPersonalizationService(
process.env.PROMPT_COMPOSE_API_KEY!,
process.env.PROMPT_COMPOSE_PROJECT_ID!
);
const userProfile = {
id: 'user123',
interests: ['technology', 'productivity', 'business'],
readingLevel: 'intermediate',
preferredTone: 'casual',
location: 'New York',
timezone: 'America/New_York'
};
const blogContent = await personalizationService.generatePersonalizedContent(
'blog-post',
'artificial intelligence trends',
userProfile
);
Testing and Development
Unit Testing with Jest
// __tests__/promptService.test.ts
import { PromptCompose } from '@promptcompose/sdk';
import { PromptService } from '../services/PromptService';
// Mock the SDK
jest.mock('@promptcompose/sdk');
describe('PromptService', () => {
let promptService: PromptService;
let mockPromptCompose: jest.Mocked<PromptCompose>;
beforeEach(() => {
mockPromptCompose = {
init: jest.fn().mockResolvedValue({ message: 'Connected' }),
resolvePrompt: jest.fn(),
reportABResult: jest.fn()
} as any;
(PromptCompose as jest.MockedClass<typeof PromptCompose>)
.mockImplementation(() => mockPromptCompose);
promptService = new PromptService('test-key', 'test-project');
});
test('should resolve prompt successfully', async () => {
const mockResult = {
content: 'Hello, John!',
variant: { name: 'friendly', publicId: 'var-123' },
abTest: { name: 'greeting-test', publicId: 'test-123' }
};
mockPromptCompose.resolvePrompt.mockResolvedValue(mockResult);
const result = await promptService.getGreeting('123', 'John');
expect(result).toBe('Hello, John!');
expect(mockPromptCompose.resolvePrompt).toHaveBeenCalledWith(
'greeting-prompt',
expect.objectContaining({
config: expect.objectContaining({
abTesting: expect.objectContaining({
sessionId: 'user-123'
})
})
}),
{ userName: 'John' }
);
});
test('should handle API errors gracefully', async () => {
mockPromptCompose.resolvePrompt.mockRejectedValue(
new Error('API Error')
);
const result = await promptService.getGreeting('123', 'John');
expect(result).toBe('Hello, John!'); // Fallback content
});
test('should report conversion successfully', async () => {
mockPromptCompose.reportABResult.mockResolvedValue({});
await promptService.reportConversion('test-123', 'var-123', '123', true);
expect(mockPromptCompose.reportABResult).toHaveBeenCalledWith(
'test-123',
expect.objectContaining({
variantId: 'var-123',
status: 'success',
sessionId: 'user-123'
})
);
});
});
Mock Service for Development
// services/MockPromptService.ts (for development)
export class MockPromptService {
private responses = new Map<string, string>();
constructor() {
// Setup mock responses
this.responses.set('welcome-prompt', 'Welcome, {{userName}}! We\'re glad you\'re here.');
this.responses.set('product-recommendation', 'Based on your interests in {{preferences}}, we recommend: {{recommendedProducts}}');
}
async resolvePrompt(promptId: string, config?: any, variables?: any) {
const template = this.responses.get(promptId) || 'Default response';
// Simple variable interpolation
let content = template;
if (variables) {
Object.entries(variables).forEach(([key, value]) => {
content = content.replace(new RegExp(`{{${key}}}`, 'g'), String(value));
});
}
// Simulate A/B testing
const variants = ['A', 'B'];
const randomVariant = variants[Math.floor(Math.random() * variants.length)];
return {
content,
variant: { name: `Variant ${randomVariant}`, publicId: `var-${randomVariant}` },
abTest: { name: `${promptId}-test`, publicId: `test-${promptId}` }
};
}
async reportABResult(testId: string, result: any) {
console.log(`Mock: Reported conversion for test ${testId}:`, result);
return result;
}
addMockResponse(promptId: string, content: string) {
this.responses.set(promptId, content);
}
}
Environment-Specific Service Factory
// services/PromptServiceFactory.ts
import { PromptCompose } from '@promptcompose/sdk';
import { MockPromptService } from './MockPromptService';
export class PromptServiceFactory {
static create() {
if (process.env.NODE_ENV === 'test') {
return new MockPromptService();
}
if (process.env.NODE_ENV === 'development' && process.env.USE_MOCK_PROMPTS === 'true') {
return new MockPromptService();
}
return new PromptCompose(
process.env.PROMPT_COMPOSE_API_KEY!,
process.env.PROMPT_COMPOSE_PROJECT_ID!,
{ debug: process.env.NODE_ENV !== 'production' }
);
}
}
// Usage
const promptService = PromptServiceFactory.create();
Performance Examples
Connection Pooling
// services/PromptConnectionPool.ts
class PromptConnectionPool {
private pool: PromptCompose[] = [];
private poolSize = 5;
private currentIndex = 0;
constructor(apiKey: string, projectId: string) {
// Create connection pool
for (let i = 0; i < this.poolSize; i++) {
const instance = new PromptCompose(apiKey, projectId);
instance.init(); // Initialize in background
this.pool.push(instance);
}
}
getInstance(): PromptCompose {
const instance = this.pool[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.poolSize;
return instance;
}
async resolvePrompt(promptId: string, config?: any, variables?: any) {
const instance = this.getInstance();
return await instance.resolvePrompt(promptId, config, variables);
}
}
Request Queuing
// services/QueuedPromptService.ts
interface QueuedRequest {
promptId: string;
config?: any;
variables?: any;
resolve: (result: any) => void;
reject: (error: any) => void;
priority: number;
}
class QueuedPromptService {
private promptCompose: PromptCompose;
private queue: QueuedRequest[] = [];
private processing = false;
private maxConcurrent = 3;
private activeRequests = 0;
constructor(apiKey: string, projectId: string) {
this.promptCompose = new PromptCompose(apiKey, projectId);
this.promptCompose.init();
}
async resolvePrompt(
promptId: string,
config?: any,
variables?: any,
priority = 0
): Promise<any> {
return new Promise((resolve, reject) => {
this.queue.push({
promptId,
config,
variables,
resolve,
reject,
priority
});
// Sort by priority (higher priority first)
this.queue.sort((a, b) => b.priority - a.priority);
this.processQueue();
});
}
private async processQueue() {
if (this.processing || this.activeRequests >= this.maxConcurrent) {
return;
}
this.processing = true;
while (this.queue.length > 0 && this.activeRequests < this.maxConcurrent) {
const request = this.queue.shift()!;
this.activeRequests++;
this.processRequest(request);
}
this.processing = false;
}
private async processRequest(request: QueuedRequest) {
try {
const result = await this.promptCompose.resolvePrompt(
request.promptId,
request.config,
request.variables
);
request.resolve(result);
} catch (error) {
request.reject(error);
} finally {
this.activeRequests--;
this.processQueue(); // Continue processing queue
}
}
}
These examples demonstrate practical implementation patterns for various scenarios. Choose the patterns that best fit your application’s architecture and requirements.
For more advanced examples and patterns, see: