Skip to main content
Glama
agent-registry.js10.9 kB
// Agent Registry - Manages all AI agents and their capabilities import OpenAI from 'openai'; import { Anthropic } from '@anthropic-ai/sdk'; class AgentRegistry { constructor() { this.agents = new Map(); this.capabilities = new Map(); this.resources = new Map(); this.initialized = false; } async initialize() { if (this.initialized) return; // Check required environment variables if (!process.env.OPENAI_API_KEY) { console.warn('⚠️ OPENAI_API_KEY not found - OpenAI agents will be unavailable'); } if (!process.env.ANTHROPIC_API_KEY) { console.warn('⚠️ ANTHROPIC_API_KEY not found - Claude agents will be unavailable'); } // Initialize AI clients if keys are available if (process.env.OPENAI_API_KEY) { this.openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); } if (process.env.ANTHROPIC_API_KEY) { this.anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }); } // Load agents from database with graceful fallback try { await this.loadAgentsFromDatabase(); } catch (error) { console.warn('⚠️ Database unavailable, using minimal agent configuration:', error.message); await this.loadMinimalAgents(); } this.initialized = true; console.log('🤖 Agent Registry initialized with', this.agents.size, 'agents'); } async loadMinimalAgents() { // Minimal fallback agent configuration when database is unavailable const minimalAgents = [ { name: 'orchestrator', provider: 'openai', role: 'Orchestrator' }, { name: 'creative_director', provider: 'anthropic', role: 'Creative Director' }, { name: 'automation_engineer', provider: 'openai', role: 'Automation Engineer' }, { name: 'docs_clerk', provider: 'system', role: 'Documentation Clerk' } ]; for (const agentConfig of minimalAgents) { if ((agentConfig.provider === 'openai' && this.openai) || (agentConfig.provider === 'anthropic' && this.anthropic) || agentConfig.provider === 'system') { const agent = { id: Date.now() + Math.random(), name: agentConfig.name, provider: agentConfig.provider, role: agentConfig.role, config: {}, capabilities: [], client: this.createAgentClient(agentConfig.provider, {}) }; this.agents.set(agentConfig.name, agent); this.resources.set(agentConfig.name, { dailyQuota: 100, // Reduced for fallback mode usedUnits: 0, windowStart: new Date(), costBudget: 10.0, costUsed: 0.0 }); } } } async loadAgentsFromDatabase() { try { const { Pool } = await import('pg'); const pool = new Pool({ connectionString: process.env.DATABASE_URL }); // Load agents and their capabilities const agentsQuery = ` SELECT a.*, json_agg( json_build_object( 'tool_name', ac.tool_name, 'throughput', ac.throughput, 'cost_rate', ac.cost_rate, 'enabled', ac.enabled ) ) as capabilities FROM agents a LEFT JOIN agent_capabilities ac ON a.id = ac.agent_id WHERE a.status = 'active' GROUP BY a.id `; const result = await pool.query(agentsQuery); for (const row of result.rows) { const agent = { id: row.id, name: row.name, provider: row.provider, role: row.role, config: row.config || {}, capabilities: row.capabilities.filter(cap => cap.tool_name) || [], client: this.createAgentClient(row.provider, row.config) }; this.agents.set(row.name, agent); // Initialize resource tracking this.resources.set(row.name, { dailyQuota: 1000, usedUnits: 0, windowStart: new Date(), costBudget: 100.0, costUsed: 0.0 }); } await pool.end(); } catch (error) { console.error('❌ Failed to load agents from database:', error); throw error; } } createAgentClient(provider, config) { switch (provider) { case 'openai': // Fallback model strategy: gpt-5 -> gpt-4o -> gpt-4 const openaiModel = config.model || process.env.OPENAI_MODEL || 'gpt-5'; return { provider: 'openai', client: this.openai, model: openaiModel, fallbacks: ['gpt-4o', 'gpt-4', 'gpt-3.5-turbo'], temperature: config.temperature || 0.2 }; case 'anthropic': const anthropicModel = config.model || process.env.ANTHROPIC_MODEL || 'claude-3-5-sonnet-20241022'; return { provider: 'anthropic', client: this.anthropic, model: anthropicModel, fallbacks: ['claude-3-haiku-20240307'], temperature: config.temperature || 0.7 }; case 'system': return { provider: 'system', client: null }; default: throw new Error(`Unknown provider: ${provider}`); } } getAgent(name) { const agent = this.agents.get(name); if (!agent) { throw new Error(`Agent '${name}' not found`); } return agent; } async callAgent(agentName, prompt, options = {}) { const agent = this.getAgent(agentName); // Check resource limits if (!this.checkResourceLimit(agentName)) { throw new Error(`Agent '${agentName}' has exceeded resource limits`); } try { let response; if (agent.client.provider === 'openai') { const messages = [{ role: 'user', content: prompt }]; if (options.systemPrompt) { messages.unshift({ role: 'system', content: options.systemPrompt }); } const completion = await agent.client.client.chat.completions.create({ model: agent.client.model, // GPT-5 is the newest OpenAI model released August 7, 2025 messages: messages, response_format: options.jsonMode ? { type: "json_object" } : undefined, max_tokens: options.maxTokens || 4096 // Note: GPT-5 doesn't support temperature parameter }); response = completion.choices[0].message.content; } else if (agent.client.provider === 'anthropic') { const messages = [{ role: 'user', content: prompt }]; const completion = await agent.client.client.messages.create({ model: agent.client.model, messages: messages, system: options.systemPrompt, max_tokens: options.maxTokens || 4096, temperature: agent.client.temperature }); response = completion.content[0].text; } else { throw new Error(`System agents cannot be called directly`); } // Track resource usage this.trackResourceUsage(agentName, 1, 0.01); // 1 request, estimated cost return { success: true, response: response, agent: agentName, provider: agent.client.provider, timestamp: new Date().toISOString() }; } catch (error) { console.error(`❌ Agent '${agentName}' call failed:`, error); return { success: false, error: error.message, agent: agentName, timestamp: new Date().toISOString() }; } } checkResourceLimit(agentName) { const resource = this.resources.get(agentName); if (!resource) return true; // Reset daily quota if new day const now = new Date(); if (now.getDate() !== resource.windowStart.getDate()) { resource.usedUnits = 0; resource.costUsed = 0.0; resource.windowStart = now; } return resource.usedUnits < resource.dailyQuota && resource.costUsed < resource.costBudget; } trackResourceUsage(agentName, units, cost) { const resource = this.resources.get(agentName); if (resource) { resource.usedUnits += units; resource.costUsed += cost; } } getAgentsByRole(role) { return Array.from(this.agents.values()).filter(agent => agent.role === role); } getAgentCapabilities(agentName) { const agent = this.getAgent(agentName); return agent.capabilities || []; } async assignAgent(agentName, tool) { const agent = this.getAgent(agentName); const capabilities = agent.capabilities; const canHandle = capabilities.some(cap => cap.tool_name === tool && cap.enabled ); if (!canHandle) { throw new Error(`Agent '${agentName}' cannot handle tool '${tool}'`); } return agent; } getResourceUsage(agentName) { return this.resources.get(agentName) || null; } getAllAgents() { return Array.from(this.agents.values()); } } // Singleton instance export const agentRegistry = new AgentRegistry();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bermingham85/mcp-puppet-pipeline'

If you have feedback or need assistance with the MCP directory API, please join our Discord server