Skip to content

Commit 573fc9a

Browse files
committed
feat: add HTTP endpoint support to expose agents as REST APIs
1 parent 91b2996 commit 573fc9a

File tree

6 files changed

+579
-3
lines changed

6 files changed

+579
-3
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,5 @@ dist
139139
.yarn/install-state.gz
140140
.pnp.*
141141
.DS_Store
142-
bun.lock
142+
bun.lock
143+
package-lock.json

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- **📡 Real-time Streaming** for responsive user experiences
1818
- **🔗 MCP Protocol** for enterprise-grade integrations
1919
- **📝 Intelligent Logging** with contextual insights
20+
- **🌐 HTTP Endpoints** to expose agents as REST APIs
2021

2122
**From startup MVPs to enterprise solutions** - RiLiGar Agents SDK scales with you.
2223

@@ -29,7 +30,7 @@ npm install @riligar/agents-sdk
2930
## ⚡ Quick Example
3031

3132
```javascript
32-
import { Agent, Logger, model, tool } from '@riligar/agents-sdk';
33+
import { Agent, Endpoint, Logger, model, tool } from '@riligar/agents-sdk';
3334

3435
// 1. Configure your API Key
3536
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY;
@@ -76,6 +77,19 @@ const agent = new Agent({
7677
const result = agent.run('Greet John');
7778
const text = await result.text;
7879
console.log(text); // Agent response
80+
81+
// 6. Expose as HTTP API (optional)
82+
const endpoint = new Endpoint(agent, { port: 3001 });
83+
await endpoint.start(); // Agent available at http://localhost:3001
84+
85+
// 7. Use from any JavaScript client
86+
const response = await fetch('http://localhost:3001/process', {
87+
method: 'POST',
88+
headers: { 'Content-Type': 'application/json' },
89+
body: JSON.stringify({ task: 'Greet Sarah' }),
90+
});
91+
const data = await response.json();
92+
console.log(data.result.text); // "Hello, Sarah! 👋"
7993
```
8094

8195
## 🎯 Key Features
@@ -88,6 +102,7 @@ console.log(text); // Agent response
88102
| **🛡️ Guardrails** | Input and output validation | `guardrails: { input: [...], output: [...] }` |
89103
| **🔄 Handoffs** | Transfer between agents | `{ type: 'agent', agent: otherAgent }` |
90104
| **📡 Streaming** | Real-time responses | `result.textStream` |
105+
| **🌐 HTTP API** | Expose agents as REST endpoints | `new Endpoint(agent, { port: 3001 })` |
91106

92107
## 🌐 Supported Models
93108

examples/endpoint.js

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
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+
}

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@
3232
],
3333
"license": "Apache-2.0",
3434
"dependencies": {
35+
"@elysiajs/cors": "^1.3.3",
36+
"@elysiajs/server-timing": "^1.3.0",
3537
"@modelcontextprotocol/sdk": "^1.17.5",
38+
"elysia": "^1.3.21",
3639
"pkce-challenge": "^4.1.0"
3740
},
3841
"config": {

0 commit comments

Comments
 (0)