Type: Permanent (Long-Lived)
RouterAgent routes incoming requests to appropriate agent handlers based on configurable rules. It provides flexible pattern matching, priority-based routing, and load balancing capabilities for distributed agent systems.
RouterAgent is a permanent agent that:
route(request: RouteRequest): Promise<RouteResult>Route a request based on configured routing rules.
Parameters:
request - RouteRequest object containing:
path - The request path to match against rulesmethod - Optional HTTP methodheaders - Optional request headersbody - Optional request bodymetadata - Optional additional metadataReturns: RouteResult containing:
matched - Whether a rule was matchedrule - The matched routing rule (if any)targetAgent - Name of the target agenttargetMethod - Method to call on target agentprocessingTime - Time taken to find match (ms)Example:
const request: RouteRequest = {
path: '/api/users/123',
method: 'GET',
headers: { 'Content-Type': 'application/json' }
};
const result = await router.route(request);
if (result.matched) {
console.log(`Routing to ${result.targetAgent}.${result.targetMethod}`);
}
addRule(rule: RoutingRule): Promise<void>Add a new routing rule.
Parameters:
rule - RoutingRule object containing:
id - Unique rule identifiername - Human-readable rule namepattern - String or RegExp pattern to matchtargetAgent - Target agent nametargetMethod - Optional target method namepriority - Rule priority (higher evaluated first)enabled - Whether rule is activemetadata - Optional rule metadataExample:
await router.addRule({
id: 'user-api-rule',
name: 'User API Routes',
pattern: /^\/api\/users/,
targetAgent: 'USER_HANDLER',
targetMethod: 'handleRequest',
priority: 10,
enabled: true
});
removeRule(ruleId: string): Promise<boolean>Remove a routing rule.
Returns: true if removed, false if not found
Example:
const removed = await router.removeRule('user-api-rule');
if (removed) {
console.log('Rule removed successfully');
}
updateRule(ruleId: string, updates: Partial<RoutingRule>): Promise<boolean>Update an existing routing rule.
Returns: true if updated, false if not found
Example:
await router.updateRule('user-api-rule', {
priority: 15,
targetMethod: 'handleUserRequest'
});
setRuleEnabled(ruleId: string, enabled: boolean): Promise<boolean>Enable or disable a routing rule without deleting it.
Example:
// Temporarily disable a rule
await router.setRuleEnabled('user-api-rule', false);
// Re-enable later
await router.setRuleEnabled('user-api-rule', true);
getRoutes(): Promise<RoutingRule[]>Get all routing rules.
Example:
const rules = await router.getRoutes();
console.log(`Router has ${rules.length} rules configured`);
rules.forEach(rule => {
console.log(`${rule.name} (priority: ${rule.priority}): ${rule.pattern}`);
});
getRule(ruleId: string): Promise<RoutingRule | undefined>Get a specific routing rule by ID.
Example:
const rule = await router.getRule('user-api-rule');
if (rule) {
console.log(`Rule pattern: ${rule.pattern}`);
}
getStats(): Promise<RouterStats>Get routing statistics.
Returns:
totalRequests - Total number of routed requestsmatchedRequests - Number of successful matchesunmatchedRequests - Number of failed matchesrouteStats - Per-rule match countsExample:
const stats = await router.getStats();
console.log(`Total requests: ${stats.totalRequests}`);
console.log(`Match rate: ${(stats.matchedRequests / stats.totalRequests * 100).toFixed(2)}%`);
console.log(`Unmatched: ${stats.unmatchedRequests}`);
clearStats(): Promise<void>Clear all routing statistics.
Example:
await router.clearStats();
console.log('Statistics cleared');
matchesRule(request: RouteRequest, rule: RoutingRule): booleanOverride to customize rule matching logic.
Example:
protected matchesRule(request: RouteRequest, rule: RoutingRule): boolean {
// Custom matching logic
const baseMatch = super.matchesRule(request, rule);
// Add custom header matching
if (rule.metadata?.requiredHeader) {
return baseMatch && request.headers?.[rule.metadata.requiredHeader as string];
}
return baseMatch;
}
executeRoute(request: RouteRequest, rule: RoutingRule): Promise<unknown>Must be overridden to implement actual routing to target agents.
Example:
protected async executeRoute(
request: RouteRequest,
rule: RoutingRule
): Promise<unknown> {
// Get target agent instance
const agent = await getAgentByName(
this.env[rule.targetAgent],
'default'
);
// Call target method
const method = rule.targetMethod || 'handleRequest';
return await agent[method](request);
}
import { RouterAgent, type RouteRequest, type RoutingRule } from 'common-agents';
import { getAgentByName } from 'agents';
class APIRouter extends RouterAgent<Env, RouterAgentState> {
constructor() {
super();
this.initializeRoutes();
}
private async initializeRoutes() {
// Add API routes
await this.addRule({
id: 'users',
name: 'User Service',
pattern: /^\/api\/users/,
targetAgent: 'USER_SERVICE',
targetMethod: 'handleRequest',
priority: 10,
enabled: true
});
await this.addRule({
id: 'orders',
name: 'Order Service',
pattern: /^\/api\/orders/,
targetAgent: 'ORDER_SERVICE',
targetMethod: 'handleRequest',
priority: 10,
enabled: true
});
await this.addRule({
id: 'analytics',
name: 'Analytics Service',
pattern: '/api/analytics*',
targetAgent: 'ANALYTICS_SERVICE',
targetMethod: 'processAnalytics',
priority: 5,
enabled: true
});
await this.addRule({
id: 'default',
name: 'Default Handler',
pattern: '*',
targetAgent: 'DEFAULT_SERVICE',
priority: 1,
enabled: true
});
}
// Override to actually route to agents
protected override async executeRoute(
request: RouteRequest,
rule: RoutingRule
): Promise<unknown> {
this.log(`Routing ${request.path} to ${rule.targetAgent}`);
try {
// Get target agent
const agent = await getAgentByName(
this.env[rule.targetAgent],
'default'
);
// Call target method
const method = rule.targetMethod || 'handleRequest';
return await agent[method](request);
} catch (error) {
this.log(`Routing failed: ${error}`, 'error');
throw error;
}
}
// Custom method to route and execute in one call
@callable()
async routeAndExecute(request: RouteRequest): Promise<unknown> {
const result = await this.route(request);
if (!result.matched) {
throw new Error(`No route found for path: ${request.path}`);
}
return await this.executeRoute(request, result.rule!);
}
}
// 1. Initialize router
const router = await getAgentByName(env.ROUTER, 'main');
// 2. Route incoming requests
const request: RouteRequest = {
path: '/api/users/123',
method: 'GET',
headers: { 'Authorization': 'Bearer token' },
body: null
};
const result = await router.route(request);
if (result.matched) {
console.log(`Matched rule: ${result.rule?.name}`);
console.log(`Target: ${result.targetAgent}.${result.targetMethod}`);
// Execute the route
const response = await router.routeAndExecute(request);
}
// 3. Monitor routing performance
const stats = await router.getStats();
console.log(`
Total Requests: ${stats.totalRequests}
Match Rate: ${(stats.matchedRequests / stats.totalRequests * 100).toFixed(2)}%
Unmatched: ${stats.unmatchedRequests}
`);
// 4. Dynamically update routes
await router.updateRule('users', { priority: 20 });
await router.setRuleEnabled('analytics', false); // Disable analytics temporarily
// API Gateway handler
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const router = await getAgentByName(env.ROUTER, 'main');
// Convert HTTP request to RouteRequest
const routeRequest: RouteRequest = {
path: new URL(request.url).pathname,
method: request.method,
headers: Object.fromEntries(request.headers),
body: await request.json().catch(() => null)
};
// Route and execute
try {
const result = await router.routeAndExecute(routeRequest);
return new Response(JSON.stringify(result), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 404,
headers: { 'Content-Type': 'application/json' }
});
}
}
};
class LoadBalancingRouter extends RouterAgent<Env, RouterAgentState> {
private currentIndex = 0;
protected override async executeRoute(
request: RouteRequest,
rule: RoutingRule
): Promise<unknown> {
// Load balance across multiple instances
const instances = rule.metadata?.instances as string[] || ['default'];
const instance = instances[this.currentIndex % instances.length];
this.currentIndex++;
this.log(`Load balancing to instance: ${instance}`);
const agent = await getAgentByName(
this.env[rule.targetAgent],
instance
);
const method = rule.targetMethod || 'handleRequest';
return await agent[method](request);
}
}
// Configure with multiple instances
await router.addRule({
id: 'high-traffic-service',
name: 'High Traffic Service',
pattern: /^\/api\/popular/,
targetAgent: 'POPULAR_SERVICE',
priority: 10,
enabled: true,
metadata: {
instances: ['instance-1', 'instance-2', 'instance-3']
}
});