|
| 1 | +/** |
| 2 | + * Example: Using Endpoint class to expose agents as HTTP APIs |
| 3 | + * |
| 4 | + * This example shows how to create agents and expose them as HTTP endpoints |
| 5 | + * that can be consumed by the @riligar/orchestrator-sdk |
| 6 | + */ |
| 7 | + |
| 8 | +import { Agent, Endpoint, Logger, model, tool } from '../src/index.js'; |
| 9 | + |
| 10 | +// Create logger for better debugging |
| 11 | +const logger = new Logger(); |
| 12 | + |
| 13 | +// Define tool definitions for the build function |
| 14 | +const toolDefinitions = [ |
| 15 | + { |
| 16 | + type: 'local', |
| 17 | + id: 'leadQualifier', |
| 18 | + description: 'Qualifies leads based on criteria', |
| 19 | + parameters: { |
| 20 | + type: 'object', |
| 21 | + properties: { |
| 22 | + email: { type: 'string', description: 'Lead email' }, |
| 23 | + company: { type: 'string', description: 'Lead company' }, |
| 24 | + budget: { type: 'number', description: 'Lead budget' }, |
| 25 | + }, |
| 26 | + required: ['email'], |
| 27 | + }, |
| 28 | + executeFn: async ({ email, company, budget }) => { |
| 29 | + // Simulate lead qualification logic |
| 30 | + const score = Math.floor(Math.random() * 100); |
| 31 | + const qualified = score > 50; |
| 32 | + |
| 33 | + logger.info(`Lead ${email} scored ${score} - ${qualified ? 'QUALIFIED' : 'NOT QUALIFIED'}`); |
| 34 | + |
| 35 | + return { |
| 36 | + qualified, |
| 37 | + score, |
| 38 | + reasons: qualified ? ['Good engagement score', 'Proper budget'] : ['Low engagement'], |
| 39 | + nextSteps: qualified ? 'Schedule demo' : 'Nurture campaign', |
| 40 | + }; |
| 41 | + }, |
| 42 | + }, |
| 43 | + { |
| 44 | + type: 'local', |
| 45 | + id: 'leadScorer', |
| 46 | + description: 'Scores leads using advanced algorithms', |
| 47 | + parameters: { |
| 48 | + type: 'object', |
| 49 | + properties: { |
| 50 | + leadData: { type: 'object', description: 'Complete lead information' }, |
| 51 | + }, |
| 52 | + required: ['leadData'], |
| 53 | + }, |
| 54 | + executeFn: async ({ leadData }) => { |
| 55 | + // Simulate scoring algorithm |
| 56 | + const factors = ['engagement', 'fit', 'intent', 'budget']; |
| 57 | + const scores = factors.reduce((acc, factor) => { |
| 58 | + acc[factor] = Math.floor(Math.random() * 100); |
| 59 | + return acc; |
| 60 | + }, {}); |
| 61 | + |
| 62 | + const finalScore = Object.values(scores).reduce((sum, score) => sum + score, 0) / factors.length; |
| 63 | + |
| 64 | + return { |
| 65 | + finalScore: Math.round(finalScore), |
| 66 | + breakdown: scores, |
| 67 | + recommendation: finalScore > 70 ? 'hot' : finalScore > 40 ? 'warm' : 'cold', |
| 68 | + }; |
| 69 | + }, |
| 70 | + }, |
| 71 | + { |
| 72 | + type: 'local', |
| 73 | + id: 'visitorTracker', |
| 74 | + description: 'Tracks visitor behavior and engagement', |
| 75 | + parameters: { |
| 76 | + type: 'object', |
| 77 | + properties: { |
| 78 | + visitorId: { type: 'string', description: 'Unique visitor identifier' }, |
| 79 | + action: { type: 'string', description: 'Action performed by visitor' }, |
| 80 | + metadata: { type: 'object', description: 'Additional tracking data' }, |
| 81 | + }, |
| 82 | + required: ['visitorId', 'action'], |
| 83 | + }, |
| 84 | + executeFn: async ({ visitorId, action, metadata = {} }) => { |
| 85 | + // Simulate visitor tracking |
| 86 | + const timestamp = new Date().toISOString(); |
| 87 | + |
| 88 | + logger.info(`Visitor ${visitorId} performed: ${action}`); |
| 89 | + |
| 90 | + return { |
| 91 | + tracked: true, |
| 92 | + visitorId, |
| 93 | + action, |
| 94 | + timestamp, |
| 95 | + sessionData: { |
| 96 | + pageViews: Math.floor(Math.random() * 10) + 1, |
| 97 | + timeSpent: Math.floor(Math.random() * 300) + 60, // 1-5 minutes |
| 98 | + source: metadata.source || 'direct', |
| 99 | + }, |
| 100 | + }; |
| 101 | + }, |
| 102 | + }, |
| 103 | +]; |
| 104 | + |
| 105 | +// Create different agent examples |
| 106 | +async function createLeadCaptureAgent() { |
| 107 | + // Build the tools using the proper API |
| 108 | + const tools = await tool.build(toolDefinitions, { logger }); |
| 109 | + |
| 110 | + const agent = new Agent({ |
| 111 | + name: 'lead-capture', |
| 112 | + instructions: ` |
| 113 | + You are a lead capture specialist agent. Your role is to: |
| 114 | + 1. Qualify incoming leads based on their information |
| 115 | + 2. Score leads using various criteria |
| 116 | + 3. Track visitor behavior on the website |
| 117 | + |
| 118 | + Always be helpful and provide detailed analysis when processing leads. |
| 119 | + Use the available tools to gather comprehensive information. |
| 120 | + `, |
| 121 | + model: model.create({ provider: 'openrouter', model: 'openai/gpt-4o-mini' }), |
| 122 | + tools: { |
| 123 | + leadQualifier: tools.leadQualifier, |
| 124 | + leadScorer: tools.leadScorer, |
| 125 | + visitorTracker: tools.visitorTracker, |
| 126 | + }, |
| 127 | + logger, |
| 128 | + }); |
| 129 | + |
| 130 | + // Add capabilities for orchestrator discovery |
| 131 | + agent.capabilities = ['lead.qualify', 'lead.score', 'visitor.track']; |
| 132 | + |
| 133 | + return agent; |
| 134 | +} |
| 135 | + |
| 136 | +async function createCustomerSupportAgent() { |
| 137 | + // Build the tools using the proper API (just the visitor tracker for support) |
| 138 | + const supportToolDefs = toolDefinitions.filter(tool => tool.id === 'visitorTracker'); |
| 139 | + const tools = await tool.build(supportToolDefs, { logger }); |
| 140 | + |
| 141 | + const agent = new Agent({ |
| 142 | + name: 'customer-support', |
| 143 | + instructions: ` |
| 144 | + You are a customer support agent specialized in: |
| 145 | + 1. Handling customer inquiries and complaints |
| 146 | + 2. Providing technical assistance |
| 147 | + 3. Escalating complex issues when needed |
| 148 | + |
| 149 | + Always be patient, helpful, and solution-oriented. |
| 150 | + Gather all necessary information before providing assistance. |
| 151 | + `, |
| 152 | + model: model.create({ provider: 'openrouter', model: 'openai/gpt-4o-mini' }), |
| 153 | + tools: { |
| 154 | + visitorTracker: tools.visitorTracker, // Support agents can also track interactions |
| 155 | + }, |
| 156 | + logger, |
| 157 | + }); |
| 158 | + |
| 159 | + agent.capabilities = ['support.technical', 'support.billing', 'customer.assist']; |
| 160 | + |
| 161 | + return agent; |
| 162 | +} |
| 163 | + |
| 164 | +async function main() { |
| 165 | + try { |
| 166 | + console.log('🚀 Starting agent endpoint examples...\n'); |
| 167 | + |
| 168 | + // Create agents |
| 169 | + const leadCaptureAgent = await createLeadCaptureAgent(); |
| 170 | + const customerSupportAgent = await createCustomerSupportAgent(); |
| 171 | + |
| 172 | + // Create endpoints for each agent |
| 173 | + const leadEndpoint = new Endpoint(leadCaptureAgent, { |
| 174 | + port: 3001, |
| 175 | + host: 'localhost', |
| 176 | + cors: true, |
| 177 | + timeout: 30000, |
| 178 | + }); |
| 179 | + |
| 180 | + const supportEndpoint = new Endpoint(customerSupportAgent, { |
| 181 | + port: 3002, |
| 182 | + host: 'localhost', |
| 183 | + cors: true, |
| 184 | + timeout: 45000, // Longer timeout for support interactions |
| 185 | + }); |
| 186 | + |
| 187 | + // Start the endpoints |
| 188 | + const leadInfo = await leadEndpoint.start(); |
| 189 | + const supportInfo = await supportEndpoint.start(); |
| 190 | + |
| 191 | + console.log('\n📡 Endpoints are running:'); |
| 192 | + console.log(` • Lead Capture Agent: ${leadInfo.url}`); |
| 193 | + console.log(` • Customer Support Agent: ${supportInfo.url}\n`); |
| 194 | + |
| 195 | + console.log('🧪 Test the endpoints:'); |
| 196 | + console.log(''); |
| 197 | + console.log('# Test lead qualification:'); |
| 198 | + console.log(`curl -X POST ${leadInfo.url}/process \\`); |
| 199 | + console.log(` -H "Content-Type: application/json" \\`); |
| 200 | + console.log(` -d '{"task": "Qualify this lead: email=john@company.com, company=TechCorp, budget=50000"}'`); |
| 201 | + console.log(''); |
| 202 | + console.log('# Check health status:'); |
| 203 | + console.log(`curl ${leadInfo.url}/health`); |
| 204 | + console.log(''); |
| 205 | + console.log('# Get capabilities:'); |
| 206 | + console.log(`curl ${leadInfo.url}/capabilities`); |
| 207 | + console.log(''); |
| 208 | + console.log('# Test customer support:'); |
| 209 | + console.log(`curl -X POST ${supportInfo.url}/process \\`); |
| 210 | + console.log(` -H "Content-Type: application/json" \\`); |
| 211 | + console.log(` -d '{"task": "Help customer with login issues", "context": {"customerId": "cust_123"}}'`); |
| 212 | + |
| 213 | + console.log('\n✅ All agents are ready for orchestration!'); |
| 214 | + console.log('\n💡 These endpoints are compatible with @riligar/orchestrator-sdk'); |
| 215 | + console.log(' The orchestrator can automatically discover and use these agents.\n'); |
| 216 | + |
| 217 | + // Graceful shutdown |
| 218 | + process.on('SIGINT', async () => { |
| 219 | + console.log('\n🛑 Shutting down endpoints...'); |
| 220 | + await leadEndpoint.stop(); |
| 221 | + await supportEndpoint.stop(); |
| 222 | + console.log('✅ All endpoints stopped gracefully'); |
| 223 | + process.exit(0); |
| 224 | + }); |
| 225 | + |
| 226 | + // Keep the process running |
| 227 | + console.log('Press Ctrl+C to stop all endpoints'); |
| 228 | + } catch (error) { |
| 229 | + console.error('❌ Error starting endpoints:', error.message); |
| 230 | + process.exit(1); |
| 231 | + } |
| 232 | +} |
| 233 | + |
| 234 | +// Example of testing the endpoints programmatically |
| 235 | +async function testEndpoints() { |
| 236 | + const testUrl = 'http://localhost:3001'; |
| 237 | + |
| 238 | + try { |
| 239 | + console.log('\n🧪 Running automated endpoint tests...\n'); |
| 240 | + |
| 241 | + // Test health endpoint |
| 242 | + console.log('Testing health endpoint...'); |
| 243 | + const healthResponse = await fetch(`${testUrl}/health`); |
| 244 | + const healthData = await healthResponse.json(); |
| 245 | + console.log('✅ Health check:', healthData.status); |
| 246 | + |
| 247 | + // Test capabilities endpoint |
| 248 | + console.log('Testing capabilities endpoint...'); |
| 249 | + const capabilitiesResponse = await fetch(`${testUrl}/capabilities`); |
| 250 | + const capabilitiesData = await capabilitiesResponse.json(); |
| 251 | + console.log('✅ Capabilities:', capabilitiesData.capabilities); |
| 252 | + |
| 253 | + // Test process endpoint |
| 254 | + console.log('Testing process endpoint...'); |
| 255 | + const processResponse = await fetch(`${testUrl}/process`, { |
| 256 | + method: 'POST', |
| 257 | + headers: { 'Content-Type': 'application/json' }, |
| 258 | + body: JSON.stringify({ |
| 259 | + task: 'Analyze this lead: email=test@example.com, company=ExampleCorp', |
| 260 | + context: { source: 'website', campaign: 'summer2024' }, |
| 261 | + }), |
| 262 | + }); |
| 263 | + const processData = await processResponse.json(); |
| 264 | + console.log('✅ Process result:', processData.success ? 'Success' : 'Failed'); |
| 265 | + |
| 266 | + console.log('\n✅ All endpoint tests passed!\n'); |
| 267 | + } catch (error) { |
| 268 | + console.error('❌ Endpoint test failed:', error.message); |
| 269 | + } |
| 270 | +} |
| 271 | + |
| 272 | +// Export for use in other examples |
| 273 | +export { createLeadCaptureAgent, createCustomerSupportAgent, testEndpoints }; |
| 274 | + |
| 275 | +// Run if this file is executed directly |
| 276 | +if (import.meta.url === `file://${process.argv[1]}`) { |
| 277 | + main(); |
| 278 | +} |
0 commit comments