UNPKG

openagentic

Version:

A TypeScript framework for building AI agents with self-contained tool orchestration capabilities

1 lines 751 kB
{"version":3,"sources":["../src/types.ts","../src/tools/utils.ts","../src/providers/manager.ts","../src/orchestrators/registry.ts","../src/orchestrator.ts","../src/streaming-orchestrator.ts","../src/tools/openai.ts","../src/utils/s3.ts","../src/tools/openai-image.ts","../src/tools/openai-vector-store.ts","../src/tools/gemini-image.ts","../src/tools/anthropic.ts","../src/tools/cohere.ts","../src/tools/gemini.ts","../src/tools/github.ts","../src/tools/grok.ts","../src/tools/llama.ts","../src/tools/mistral.ts","../src/tools/newsdata.ts","../src/tools/perplexity.ts","../src/tools/qrcode.ts","../src/tools/websearch.ts","../src/tools/elevenlabs.ts","../src/tools/video-generation.ts","../src/tools/gemini-tts.ts","../src/tools/inception-labs.ts","../src/tools/html-composer.ts","../src/tools/unsplash.ts","../src/tools/slack-poster.ts","../src/tools/groq.ts","../src/tools/luma-image.ts","../src/tools/index.ts","../src/orchestrators/custom-logic.ts","../src/orchestrators/multi-ai.ts","../src/orchestrators/library/code-assessment.ts","../src/orchestrators/prompt-based.ts","../src/orchestrators/library/enhanced-image-generation.ts","../src/orchestrators/library/flash-headlines.ts","../src/orchestrators/library/news-specialist.ts","../src/orchestrators/library/video-creator.ts","../src/orchestrators/index.ts","../src/index.ts"],"sourcesContent":["import { z } from 'zod';\nimport { type Tool } from 'ai';\n\n// =============================================================================\n// CORE TYPES FOR OPENAGENTIC FRAMEWORK\n// =============================================================================\n\nexport const AIModelSchema = z.object({\n provider: z.enum(['openai', 'anthropic', 'google', 'google-vertex', 'perplexity', 'xai', 'custom']),\n model: z.string(),\n apiKey: z.string().optional(),\n baseURL: z.string().optional(),\n temperature: z.number().min(0).max(2).optional().default(0.7),\n maxTokens: z.number().positive().optional(),\n topP: z.number().min(0).max(1).optional(),\n project: z.string().optional(),\n location: z.string().optional(),\n});\n\nexport type AIModel = z.infer<typeof AIModelSchema>;\n\nexport const MessageSchema = z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.string(),\n toolCallId: z.string().optional(),\n toolCalls: z.array(z.object({\n toolCallId: z.string(),\n toolName: z.string(),\n args: z.record(z.any()),\n })).optional(),\n});\n\nexport type Message = z.infer<typeof MessageSchema>;\n\n// =============================================================================\n// API KEY MAP TYPE\n// =============================================================================\n\nexport type ApiKeyMap = Record<string, string> & {\n // AWS credentials for S3 and other services\n awsAccessKeyId?: string;\n awsSecretAccessKey?: string;\n awsRegion?: string;\n awsS3Bucket?: string;\n // AWS Bedrock credentials (for Anthropic via Bedrock)\n bedrockAccessKeyId?: string;\n bedrockSecretAccessKey?: string;\n bedrockRegion?: string;\n};\n\n// =============================================================================\n// CORE MESSAGE TYPES (AI SDK COMPATIBLE)\n// =============================================================================\n\nexport interface CoreMessage {\n role: 'system' | 'user' | 'assistant' | 'tool';\n content: string | Array<any>;\n toolCallId?: string;\n toolCalls?: Array<{\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n }>;\n}\n\n// =============================================================================\n// LOGGING AND DEBUG TYPES\n// =============================================================================\n\nexport type LogLevel = 'none' | 'basic' | 'detailed';\n\nexport interface LoggingConfig {\n enableDebugLogging?: boolean;\n logLevel?: LogLevel;\n enableStepLogging?: boolean;\n enableToolLogging?: boolean;\n enableTimingLogging?: boolean;\n enableStatisticsLogging?: boolean;\n}\n\nexport interface ExecutionStats {\n totalDuration: number;\n stepsExecuted: number;\n toolCallsExecuted: number;\n tokensUsed?: number;\n averageStepDuration: number;\n averageToolCallDuration: number;\n}\n\nexport interface StepInfo {\n stepIndex: number;\n stepType: string;\n duration: number;\n toolCalls: string[];\n errors: string[];\n startTime: number;\n endTime: number;\n}\n\n// =============================================================================\n// OPENAGENTIC TOOL TYPES\n// =============================================================================\n\nexport type ToolDetails = {\n toolId: string;\n name: string;\n useCases: string[];\n logo: string;\n internal?: boolean;\n}\n\nexport type OpenAgenticTool = Tool & ToolDetails;\n\n// =============================================================================\n// EXECUTION RESULT TYPES\n// =============================================================================\n\nexport const ExecutionResultSchema = z.object({\n success: z.boolean(),\n result: z.any().optional(),\n error: z.string().optional(),\n messages: z.array(MessageSchema),\n iterations: z.number(),\n toolCallsUsed: z.array(z.string()),\n executionStats: z.object({\n totalDuration: z.number(),\n stepsExecuted: z.number(),\n toolCallsExecuted: z.number(),\n tokensUsed: z.number().optional(),\n averageStepDuration: z.number(),\n averageToolCallDuration: z.number(),\n }).optional(),\n usage: z.object({\n totalTokens: z.number(),\n promptTokens: z.number(),\n completionTokens: z.number(),\n }).optional(),\n});\n\nexport type ExecutionResult = z.infer<typeof ExecutionResultSchema>;\n\n// =============================================================================\n// ORCHESTRATOR TYPES\n// =============================================================================\n\n/**\n * Supported orchestrator types\n */\nexport type OrchestratorType = 'prompt-based' | 'custom-logic';\n\n/**\n * Context object passed to orchestrator execute methods\n */\nexport interface OrchestratorContext {\n model: AIModel;\n tools: OpenAgenticTool[];\n messages: Message[];\n iterations: number;\n maxIterations: number;\n loggingConfig: LoggingConfig;\n orchestratorParams?: Record<string, any>;\n}\n\n/**\n * Base interface for all orchestrators\n */\nexport interface BaseOrchestrator {\n /** Unique identifier for the orchestrator */\n id: string;\n \n /** Human-readable name */\n name: string;\n \n /** Description of what this orchestrator does */\n description: string;\n \n /** Type of orchestrator */\n type: OrchestratorType;\n \n /** Execute the orchestration logic */\n execute(input: string | CoreMessage[], context: OrchestratorContext): Promise<ExecutionResult>;\n \n /** Get the orchestrator name */\n getName(): string;\n \n /** Get the orchestrator description */\n getDescription(): string;\n \n /** Get the orchestrator type */\n getType(): OrchestratorType;\n \n /** Optional validation method for inputs */\n validate?(input: string | CoreMessage[], context: OrchestratorContext): Promise<boolean>;\n \n /** Optional initialization method */\n initialize?(context: OrchestratorContext): Promise<void>;\n \n /** Optional cleanup method */\n cleanup?(context: OrchestratorContext): Promise<void>;\n}\n\n/**\n * Orchestrator that uses system prompts and standard LLM interaction\n */\nexport interface PromptBasedOrchestrator extends BaseOrchestrator {\n type: 'prompt-based';\n \n /** System prompt used for orchestration */\n systemPrompt: string;\n \n /** Get the system prompt */\n getSystemPrompt(): string;\n \n /** Optional method to modify system prompt based on context */\n buildSystemPrompt?(context: OrchestratorContext): string;\n}\n\n/**\n * Orchestrator that uses custom logic instead of standard LLM flow\n */\nexport interface CustomLogicOrchestrator extends BaseOrchestrator {\n type: 'custom-logic';\n \n /** Custom logic function for orchestration */\n customLogic(input: string | CoreMessage[], context: OrchestratorContext): Promise<any>;\n \n /** Optional method to determine if custom logic should be used */\n shouldUseCustomLogic?(input: string | CoreMessage[], context: OrchestratorContext): boolean;\n}\n\n/**\n * Options for creating orchestrator-enabled agents\n */\nexport interface OrchestratorOptions {\n /** Orchestrator instance or ID to use */\n orchestrator?: string | BaseOrchestrator;\n \n /** Alternative parameter name for orchestrator ID */\n orchestratorId?: string;\n \n /** Parameters to pass to the orchestrator */\n orchestratorParams?: Record<string, any>;\n \n /** Whether to allow orchestrator to override system prompt */\n allowOrchestratorPromptOverride?: boolean;\n \n /** Whether to allow orchestrator to modify tool execution */\n allowOrchestratorToolControl?: boolean;\n}","import { type Tool } from 'ai';\nimport { tool } from 'ai';\nimport { z } from 'zod';\nimport { type OpenAgenticTool, type ToolDetails } from '../types';\nimport { createAnthropic } from '@ai-sdk/anthropic';\nimport { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';\nimport { ProviderManager } from '../providers/manager';\n\n// =============================================================================\n// LANGCHAIN TOOL COMPATIBILITY\n// =============================================================================\n\n/**\n * Check if an object is a LangChain Tool or StructuredTool\n */\nfunction isLangChainTool(tool: any): boolean {\n // Check for typical LangChain tool properties\n return (\n tool &&\n typeof tool === 'object' &&\n typeof tool.name === 'string' &&\n typeof tool.description === 'string' &&\n (typeof tool.call === 'function' || typeof tool.invoke === 'function')\n );\n}\n\n/**\n * Convert LangChain's zod schema to AI SDK compatible zod schema\n */\nfunction convertLangChainSchema(schema: any): z.ZodType<any> {\n if (!schema) {\n // If no schema, return empty object schema\n return z.object({});\n }\n \n // If it's already a Zod schema, return as-is\n if (schema._def) {\n return schema;\n }\n \n // If it's a JSON schema, try to convert to Zod\n if (typeof schema === 'object' && schema.type) {\n return convertJsonSchemaToZod(schema);\n }\n \n // Fallback to any schema\n return z.any();\n}\n\n/**\n * Convert JSON Schema to Zod schema (basic implementation)\n */\nfunction convertJsonSchemaToZod(jsonSchema: any): z.ZodType<any> {\n if (jsonSchema.type === 'object') {\n const shape: Record<string, z.ZodType<any>> = {};\n const properties = jsonSchema.properties || {};\n \n for (const [key, prop] of Object.entries(properties)) {\n shape[key] = convertJsonSchemaToZod(prop as any);\n }\n \n return z.object(shape);\n }\n \n if (jsonSchema.type === 'string') {\n let schema = z.string();\n if (jsonSchema.description) {\n schema = schema.describe(jsonSchema.description);\n }\n return schema;\n }\n \n if (jsonSchema.type === 'number') {\n return z.number();\n }\n \n if (jsonSchema.type === 'boolean') {\n return z.boolean();\n }\n \n if (jsonSchema.type === 'array') {\n const itemSchema = jsonSchema.items ? convertJsonSchemaToZod(jsonSchema.items) : z.any();\n return z.array(itemSchema);\n }\n \n return z.any();\n}\n\n/**\n * Convert a LangChain Tool to OpenAgentic format\n * @param lcTool - LangChain Tool or StructuredTool instance\n * @param opts - Optional configuration\n * @returns OpenAgentic compatible tool\n */\nexport async function convertLangchainTool(\n lcTool: any,\n opts?: {\n toolId?: string;\n useCases?: string[];\n logo?: string;\n paramsSchema?: z.ZodType<any>;\n }\n): Promise<OpenAgenticTool> {\n if (!isLangChainTool(lcTool)) {\n throw new Error('Provided tool is not a valid LangChain Tool or StructuredTool');\n }\n\n // Extract schema from LangChain tool\n let schema: z.ZodType<any>;\n \n if (opts?.paramsSchema) {\n schema = opts.paramsSchema;\n } else if (lcTool.schema) {\n schema = convertLangChainSchema(lcTool.schema);\n } else {\n // Default to empty object schema for basic tools\n schema = z.object({\n input: z.string().describe('Input text for the tool')\n });\n }\n\n // Create AI SDK tool\n const aiTool = tool({\n description: lcTool.description,\n parameters: schema,\n execute: async (args: any) => {\n try {\n let result: any;\n \n // Try invoke first (for newer LangChain tools), then fall back to call\n if (typeof lcTool.invoke === 'function') {\n // For StructuredTool, pass the args object directly\n result = await lcTool.invoke(args);\n } else if (typeof lcTool.call === 'function') {\n // For basic Tool, pass the input string or first argument\n const input = typeof args === 'object' && args.input !== undefined \n ? args.input \n : Object.keys(args).length === 1 \n ? Object.values(args)[0] \n : JSON.stringify(args);\n result = await lcTool.call(input);\n } else {\n throw new Error('LangChain tool has no callable invoke or call method');\n }\n\n // Ensure result is a string (LangChain tools typically return strings)\n if (typeof result === 'object') {\n return JSON.stringify(result);\n }\n return String(result);\n \n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`LangChain tool \"${lcTool.name}\" execution failed:`, errorMessage);\n throw new Error(`Tool execution failed: ${errorMessage}`);\n }\n }\n });\n\n // Create tool details\n const toolDetails: ToolDetails = {\n toolId: opts?.toolId || `langchain_${lcTool.name.toLowerCase().replace(/[^a-z0-9]/g, '_')}`,\n name: lcTool.name,\n useCases: opts?.useCases || [\n `LangChain ${lcTool.name} integration`,\n lcTool.description || 'Imported from LangChain'\n ],\n logo: opts?.logo || '🔗' // LangChain emoji\n };\n\n return {\n ...aiTool,\n ...toolDetails\n };\n}\n\n/**\n * Auto-detect and convert LangChain tools in a tools array\n * @param tools - Array of tools that may include LangChain tools\n * @returns Promise resolving to array with LangChain tools converted\n */\nexport async function autoConvertLangChainTools(tools: any[]): Promise<any[]> {\n if (!Array.isArray(tools)) {\n return tools;\n }\n\n const convertedTools = await Promise.all(\n tools.map(async (tool) => {\n if (isLangChainTool(tool)) {\n console.log(`🔗 Auto-converting LangChain tool: ${tool.name}`);\n return await convertLangchainTool(tool);\n }\n return tool;\n })\n );\n\n return convertedTools;\n}\n\n/**\n * Check if a tools array contains any LangChain tools\n * @param tools - Array of tools to check\n * @returns Boolean indicating if LangChain tools are present\n */\nexport function hasLangChainTools(tools: any[]): boolean {\n if (!Array.isArray(tools)) {\n return false;\n }\n return tools.some(tool => isLangChainTool(tool));\n}\n\n/**\n * Synchronous version of convertLangchainTool for constructor use\n * @param lcTool - LangChain Tool or StructuredTool instance\n * @param opts - Optional configuration\n * @returns OpenAgentic compatible tool (synchronously)\n */\nexport function convertLangchainToolSync(\n lcTool: any,\n opts?: {\n toolId?: string;\n useCases?: string[];\n logo?: string;\n paramsSchema?: z.ZodType<any>;\n }\n): OpenAgenticTool {\n if (!isLangChainTool(lcTool)) {\n throw new Error('Provided tool is not a valid LangChain Tool or StructuredTool');\n }\n\n // Extract schema from LangChain tool\n let schema: z.ZodType<any>;\n \n if (opts?.paramsSchema) {\n schema = opts.paramsSchema;\n } else if (lcTool.schema) {\n schema = convertLangChainSchema(lcTool.schema);\n } else {\n // Default to empty object schema for basic tools\n schema = z.object({\n input: z.string().describe('Input text for the tool')\n });\n }\n\n // Create AI SDK tool\n const aiTool = tool({\n description: lcTool.description,\n parameters: schema,\n execute: async (args: any) => {\n try {\n let result: any;\n \n // Try invoke first (for newer LangChain tools), then fall back to call\n if (typeof lcTool.invoke === 'function') {\n // For StructuredTool, pass the args object directly\n result = await lcTool.invoke(args);\n } else if (typeof lcTool.call === 'function') {\n // For basic Tool, pass the input string or first argument\n const input = typeof args === 'object' && args.input !== undefined \n ? args.input \n : Object.keys(args).length === 1 \n ? Object.values(args)[0] \n : JSON.stringify(args);\n result = await lcTool.call(input);\n } else {\n throw new Error('LangChain tool has no callable invoke or call method');\n }\n\n // Ensure result is a string (LangChain tools typically return strings)\n if (typeof result === 'object') {\n return JSON.stringify(result);\n }\n return String(result);\n \n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`LangChain tool \"${lcTool.name}\" execution failed:`, errorMessage);\n throw new Error(`Tool execution failed: ${errorMessage}`);\n }\n }\n });\n\n // Create tool details\n const toolDetails: ToolDetails = {\n toolId: opts?.toolId || `langchain_${lcTool.name.toLowerCase().replace(/[^a-z0-9]/g, '_')}`,\n name: lcTool.name,\n useCases: opts?.useCases || [\n `LangChain ${lcTool.name} integration`,\n lcTool.description || 'Imported from LangChain'\n ],\n logo: opts?.logo || '🔗' // LangChain emoji\n };\n\n return {\n ...aiTool,\n ...toolDetails\n };\n}\n\n// =============================================================================\n// OPENAGENTIC UTILITY FUNCTIONS\n// =============================================================================\n\n/**\n * Helper function to add a custom toolId to an AI SDK tool\n * This provides better identification and debugging capabilities\n * \n * @param tool - AI SDK tool created with tool() function, see ai's ToolParameters type for more details\n * @param toolId - Unique identifier for the tool\n * @returns Tool with toolId property for better identification\n */\nexport function toOpenAgenticTool(tool: Tool, details: ToolDetails): OpenAgenticTool {\n return {\n ...tool,\n ...details,\n }\n}\n\nexport function getModel(model: string): string {\n const bedrockCredentials = ProviderManager.getBedrockCredentials();\n if (bedrockCredentials.accessKeyId && bedrockCredentials.secretAccessKey) {\n if(model.includes('sonnet')) {\n return 'us.anthropic.claude-sonnet-4-20250514-v1:0';\n } else if(model.includes('opus')) {\n return 'us.anthropic.claude-4-opus-20250514-v1:0';\n } else {\n return model;\n }\n } else {\n return model;\n }\n}\n\nexport function getAnthropicModelInstance(model: string): any {\n const bedrockCredentials = ProviderManager.getBedrockCredentials();\n let modelInstance: any;\n let provider: any;\n if (bedrockCredentials.accessKeyId && bedrockCredentials.secretAccessKey) {\n console.log('Using Bedrock');\n provider = createAmazonBedrock({\n region: bedrockCredentials.region,\n accessKeyId: bedrockCredentials.accessKeyId,\n secretAccessKey: bedrockCredentials.secretAccessKey,\n });\n // TODO: Add support for other Bedrock model versions\n if(model.includes('sonnet')) {\n modelInstance = provider('us.anthropic.claude-sonnet-4-20250514-v1:0');\n console.log('Model: Claude Sonnet 4');\n } else if(model.includes('opus')) {\n modelInstance = provider('us.anthropic.claude-4-opus-20250514-v1:0');\n console.log('Model: Claude Opus 4');\n } else {\n throw new Error(`Model \"${model}\" not supported`);\n }\n } else {\n console.log('Using Anthropic');\n // Validate API key\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n throw new Error('ANTHROPIC_API_KEY environment variable is required');\n }\n provider = createAnthropic({\n apiKey,\n });\n modelInstance = provider(model);\n console.log('Model:', model);\n }\n return {provider, modelInstance};\n}\n","import { getAnthropicModelInstance } from '../tools/utils';\nimport type { AIModel, ApiKeyMap } from '../types';\n\n// Provider configuration with model metadata\nexport const providerConfigs = {\n openai: {\n baseURL: 'https://api.openai.com/v1',\n models: {\n 'gpt-4': { \n contextWindow: 8192, \n cost: { input: 0.03, output: 0.06 },\n description: 'Most capable GPT-4 model'\n },\n 'gpt-4-turbo': { \n contextWindow: 128000, \n cost: { input: 0.01, output: 0.03 },\n description: 'GPT-4 Turbo with larger context window'\n },\n 'gpt-4o': { \n contextWindow: 128000, \n cost: { input: 0.005, output: 0.015 },\n description: 'GPT-4 Omni - fastest and most cost-effective'\n },\n 'gpt-4o-mini': { \n contextWindow: 128000, \n cost: { input: 0.00015, output: 0.0006 },\n description: 'Smaller, faster GPT-4o variant'\n },\n 'o3': { \n contextWindow: 200000, \n cost: { input: 0.06, output: 0.24 },\n description: 'Latest reasoning model'\n },\n 'o3-mini': { \n contextWindow: 200000, \n cost: { input: 0.015, output: 0.06 },\n description: 'Smaller o3 variant with faster inference'\n },\n }\n },\n anthropic: {\n baseURL: 'https://api.anthropic.com',\n models: {\n 'claude-opus-4-20250514': { \n contextWindow: 200000, \n cost: { input: 0.015, output: 0.075 },\n description: 'Most capable Claude 4 model'\n },\n 'claude-sonnet-4-20250514': { \n contextWindow: 200000, \n cost: { input: 0.003, output: 0.015 },\n description: 'Balanced Claude 4 model for most use cases'\n },\n 'claude-3-7-sonnet-latest': { \n contextWindow: 200000, \n cost: { input: 0.003, output: 0.015 },\n description: 'Latest Claude 3.7 Sonnet model'\n },\n 'claude-3-5-sonnet-latest': { \n contextWindow: 200000, \n cost: { input: 0.003, output: 0.015 },\n description: 'Latest Claude 3.5 Sonnet model'\n },\n }\n },\n google: {\n baseURL: 'https://generativelanguage.googleapis.com/v1beta',\n models: {\n 'gemini-2.5-pro': { \n contextWindow: 2000000, \n cost: { input: 0.001, output: 0.002 },\n description: 'Latest Gemini 2.5 Pro preview model'\n },\n 'gemini-2.5-flash': { \n contextWindow: 1000000, \n cost: { input: 0.0005, output: 0.001 },\n description: 'Fast Gemini 2.5 Flash preview model'\n },\n 'gemini-1.5-pro': { \n contextWindow: 2000000, \n cost: { input: 0.00125, output: 0.005 },\n description: 'Gemini 1.5 Pro with large context window'\n },\n 'gemini-1.5-flash': { \n contextWindow: 1000000, \n cost: { input: 0.000075, output: 0.0003 },\n description: 'Fast and efficient Gemini 1.5 model'\n },\n 'gemini-2.5-flash-lite-preview-06-17': { \n contextWindow: 1000000, \n cost: { input: 0.000075, output: 0.0003 },\n description: 'Fast and efficient Gemini 2.5 Flash Lite preview model'\n },\n }\n },\n 'google-vertex': {\n baseURL: 'https://us-central1-aiplatform.googleapis.com',\n models: {\n 'gemini-2.5-pro': { \n contextWindow: 2000000, \n cost: { input: 0.001, output: 0.002 },\n description: 'Latest Gemini 2.5 Pro preview model via Vertex AI'\n },\n 'gemini-2.5-flash': { \n contextWindow: 1000000, \n cost: { input: 0.0005, output: 0.001 },\n description: 'Fast Gemini 2.5 Flash preview model via Vertex AI'\n },\n 'gemini-1.5-pro': { \n contextWindow: 2000000, \n cost: { input: 0.00125, output: 0.005 },\n description: 'Gemini 1.5 Pro via Vertex AI'\n },\n 'gemini-1.5-flash': { \n contextWindow: 1000000, \n cost: { input: 0.000075, output: 0.0003 },\n description: 'Fast Gemini 1.5 model via Vertex AI'\n },\n }\n },\n perplexity: {\n baseURL: 'https://api.perplexity.ai',\n models: {\n 'llama-3.1-sonar-small-128k-online': { \n contextWindow: 127072, \n cost: { input: 0.0002, output: 0.0002 },\n description: 'Small Llama 3.1 Sonar with online search'\n },\n 'llama-3.1-sonar-large-128k-online': { \n contextWindow: 127072, \n cost: { input: 0.001, output: 0.001 },\n description: 'Large Llama 3.1 Sonar with online search'\n },\n 'llama-3.1-sonar-huge-128k-online': { \n contextWindow: 127072, \n cost: { input: 0.005, output: 0.005 },\n description: 'Huge Llama 3.1 Sonar with online search'\n },\n }\n },\n xai: {\n baseURL: 'https://api.x.ai/v1',\n models: {\n 'grok-beta': { \n contextWindow: 131072, \n cost: { input: 0.005, output: 0.015 },\n description: 'Grok conversational AI model'\n },\n }\n },\n};\n\n// =============================================================================\n// CENTRALIZED PROVIDER MANAGER\n// =============================================================================\n\nexport class ProviderManager {\n private static userApiKeys: ApiKeyMap | undefined;\n\n /**\n * Set user-provided API keys that take precedence over environment variables\n */\n static setUserApiKeys(apiKeys: ApiKeyMap | undefined): void {\n this.userApiKeys = apiKeys;\n \n if (apiKeys && Object.keys(apiKeys).length > 0) {\n console.log('🔑 User API keys configured for providers:', Object.keys(apiKeys).length);\n }\n }\n\n /**\n * Get AWS credentials from user API keys or environment variables\n */\n static getAwsCredentials(): {\n accessKeyId?: string;\n secretAccessKey?: string;\n region?: string;\n bucketName?: string;\n } {\n const userKeys = this.userApiKeys;\n \n return {\n accessKeyId: userKeys?.awsAccessKeyId || process.env.MY_AWS_ACCESS_KEY_ID || process.env.AWS_ACCESS_KEY_ID,\n secretAccessKey: userKeys?.awsSecretAccessKey || process.env.MY_AWS_SECRET_ACCESS_KEY || process.env.AWS_SECRET_ACCESS_KEY,\n region: userKeys?.awsRegion || process.env.MY_AWS_REGION || process.env.AWS_REGION,\n bucketName: userKeys?.awsS3Bucket || process.env.MY_S3_BUCKET_NAME || process.env.S3_BUCKET_NAME,\n };\n }\n\n /**\n * Get AWS Bedrock credentials from user API keys or environment variables\n */\n static getBedrockCredentials(): {\n accessKeyId?: string;\n secretAccessKey?: string;\n region?: string;\n } {\n const userKeys = this.userApiKeys;\n \n return {\n accessKeyId: userKeys?.bedrockAccessKeyId || process.env.BEDROCK_ACCESS_KEY_ID,\n secretAccessKey: userKeys?.bedrockSecretAccessKey || process.env.BEDROCK_SECRET_ACCESS_KEY,\n region: userKeys?.bedrockRegion || process.env.BEDROCK_REGION,\n };\n }\n\n /**\n * Create a model configuration from a string or AIModel object\n * Automatically detects provider from model name if string is provided\n */\n static createModel(input: string | AIModel): AIModel {\n if (typeof input === 'string') {\n return this.autoDetectProvider(input);\n }\n return this.validateAndNormalizeModel(input);\n }\n\n /**\n * Create an AI SDK provider instance for the given model\n */\n static async createProvider(model: AIModel): Promise<any> {\n const apiKey = model.apiKey ?? this.getDefaultApiKey(model.provider); // Fix: Use nullish coalescing\n \n switch (model.provider) {\n case 'openai': {\n const { createOpenAI } = await import('@ai-sdk/openai');\n const config: any = {};\n if (apiKey !== undefined) config.apiKey = apiKey; // Fix: Explicit undefined check\n if (model.baseURL !== undefined) config.baseURL = model.baseURL;\n return createOpenAI(config);\n }\n case 'anthropic': {\n const { provider } = getAnthropicModelInstance(model.model);\n return provider;\n // const { createAnthropic } = await import('@ai-sdk/anthropic');\n // const config: any = {};\n // if (apiKey !== undefined) config.apiKey = apiKey;\n // return createAnthropic(config);\n }\n case 'google': {\n const { createGoogleGenerativeAI } = await import('@ai-sdk/google');\n const config: any = {};\n if (apiKey !== undefined) config.apiKey = apiKey;\n return createGoogleGenerativeAI(config);\n }\n case 'google-vertex': {\n const { createVertex } = await import('@ai-sdk/google-vertex');\n const config: any = {};\n if (model.project !== undefined) config.project = model.project;\n if (model.location !== undefined) config.location = model.location;\n return createVertex(config);\n }\n case 'perplexity': {\n const { createPerplexity } = await import('@ai-sdk/perplexity');\n const config: any = {};\n if (apiKey !== undefined) config.apiKey = apiKey;\n return createPerplexity(config);\n }\n case 'xai': {\n const { createXai } = await import('@ai-sdk/xai');\n const config: any = {};\n if (apiKey !== undefined) config.apiKey = apiKey;\n return createXai(config);\n }\n case 'custom': {\n if (!model.baseURL) {\n throw new Error('Custom provider requires baseURL');\n }\n const { createOpenAI } = await import('@ai-sdk/openai');\n const config: any = {\n baseURL: model.baseURL,\n };\n if (apiKey !== undefined) config.apiKey = apiKey;\n return createOpenAI(config);\n }\n default:\n throw new Error(`Unsupported provider: ${model.provider}`);\n }\n }\n\n /**\n * Create a provider for a specific provider name (for tool context)\n */\n static async createProviderByName(providerName: string, apiKey?: string): Promise<any> {\n const key = apiKey ?? this.getDefaultApiKey(providerName as AIModel['provider']); // Fix: Use nullish coalescing\n \n switch (providerName) {\n case 'openai': {\n const { createOpenAI } = await import('@ai-sdk/openai');\n if (!key) throw new Error('OpenAI API key not found');\n return createOpenAI({ apiKey: key });\n }\n case 'anthropic': {\n const { createAnthropic } = await import('@ai-sdk/anthropic');\n if (!key) throw new Error('Anthropic API key not found');\n return createAnthropic({ apiKey: key });\n }\n case 'google': {\n const { createGoogleGenerativeAI } = await import('@ai-sdk/google');\n if (!key) throw new Error('Google API key not found');\n return createGoogleGenerativeAI({ apiKey: key });\n }\n case 'perplexity': {\n const { createPerplexity } = await import('@ai-sdk/perplexity');\n if (!key) throw new Error('Perplexity API key not found');\n return createPerplexity({ apiKey: key });\n }\n case 'xai': {\n const { createXai } = await import('@ai-sdk/xai');\n if (!key) throw new Error('xAI API key not found');\n return createXai({ apiKey: key });\n }\n default:\n throw new Error(`Unsupported provider: ${providerName}`);\n }\n }\n\n /**\n * Get all available providers and their models\n */\n static getAllProviders(): Array<{ provider: string; models: string[] }> {\n return Object.entries(providerConfigs).map(([provider, config]) => ({\n provider,\n models: Object.keys(config.models),\n }));\n }\n\n /**\n * Get supported models for a provider\n */\n static getProviderModels(provider: string): string[] {\n const config = providerConfigs[provider as keyof typeof providerConfigs];\n return config ? Object.keys(config.models) : [];\n }\n\n /**\n * Check if a model is supported by a provider\n */\n static isModelSupported(provider: string, model: string): boolean {\n const models = this.getProviderModels(provider);\n return models.includes(model);\n }\n\n /**\n * Get model information (context window, cost, description)\n */\n static getModelInfo(provider: string, model: string) {\n const config = providerConfigs[provider as keyof typeof providerConfigs];\n if (!config) {\n throw new Error(`Unknown provider: ${provider}`);\n }\n \n const modelInfo = config.models[model as keyof typeof config.models];\n if (!modelInfo) {\n throw new Error(`Unknown model: ${model} for provider: ${provider}`);\n }\n \n return modelInfo;\n }\n\n // Private methods\n private static autoDetectProvider(modelName: string): AIModel {\n let provider: AIModel['provider'];\n let apiKey: string | undefined;\n\n // OpenAI models\n if (modelName.includes('gpt') || modelName.includes('o3')) {\n provider = 'openai';\n apiKey = this.getDefaultApiKey('openai');\n }\n // Anthropic models\n else if (modelName.includes('claude')) {\n provider = 'anthropic';\n apiKey = this.getDefaultApiKey('anthropic');\n }\n // Google models\n else if (modelName.includes('gemini')) {\n provider = 'google';\n apiKey = this.getDefaultApiKey('google');\n }\n // xAI models\n else if (modelName.includes('grok')) {\n provider = 'xai';\n apiKey = this.getDefaultApiKey('xai');\n }\n // Perplexity models\n else if (modelName.includes('llama') && modelName.includes('sonar')) {\n provider = 'perplexity';\n apiKey = this.getDefaultApiKey('perplexity');\n }\n // Default to OpenAI for unknown models\n else {\n provider = 'openai';\n apiKey = this.getDefaultApiKey('openai');\n console.warn(`Unknown model \"${modelName}\", defaulting to OpenAI provider`);\n }\n\n // Validate model is supported by detected provider\n if (!this.isModelSupported(provider, modelName)) {\n throw new Error(`Model \"${modelName}\" not found in ${provider} configuration`);\n }\n\n return {\n provider,\n model: modelName,\n apiKey,\n temperature: 0.7,\n };\n }\n\n private static validateAndNormalizeModel(model: AIModel): AIModel {\n // Ensure required fields are present\n if (!model.provider || !model.model) {\n throw new Error('AIModel must have provider and model fields');\n }\n\n // Create a normalized copy to avoid mutating the original\n const normalizedModel: AIModel = { ...model };\n\n // Add default API key if not provided\n if (normalizedModel.apiKey === undefined) {\n normalizedModel.apiKey = this.getDefaultApiKey(normalizedModel.provider);\n }\n\n // Add default temperature if not provided\n if (normalizedModel.temperature === undefined) {\n normalizedModel.temperature = 0.7;\n }\n\n return normalizedModel;\n }\n\n private static getDefaultApiKey(provider: AIModel['provider']): string | undefined {\n // First check user-provided API keys\n if (this.userApiKeys && provider in this.userApiKeys) {\n return this.userApiKeys[provider];\n }\n \n // Then fall back to environment variables\n switch (provider) {\n case 'openai':\n return process.env.OPENAI_API_KEY;\n case 'anthropic':\n return process.env.ANTHROPIC_API_KEY;\n case 'google':\n return process.env.GOOGLE_API_KEY;\n case 'google-vertex':\n return undefined; // Vertex uses service account auth\n case 'perplexity':\n return process.env.PERPLEXITY_API_KEY;\n case 'xai':\n return process.env.XAI_API_KEY;\n case 'custom':\n return undefined; // Custom providers handle their own auth\n default:\n return undefined;\n }\n }\n}","import type { BaseOrchestrator, OrchestratorType } from '../types';\n\n// =============================================================================\n// ORCHESTRATOR REGISTRY\n// =============================================================================\n\n/**\n * Global registry for storing orchestrator instances\n */\nconst orchestratorRegistry = new Map<string, BaseOrchestrator>();\n\n/**\n * Register an orchestrator in the global registry\n * @param orchestrator - The orchestrator to register\n * @throws {Error} If an orchestrator with the same ID already exists\n */\nexport function registerOrchestrator(orchestrator: BaseOrchestrator): void {\n if (!orchestrator || !orchestrator.id) {\n throw new Error('Orchestrator must have a valid ID');\n }\n\n if (orchestratorRegistry.has(orchestrator.id)) {\n throw new Error(`Orchestrator with ID \"${orchestrator.id}\" already exists`);\n }\n\n // Validate orchestrator structure\n validateOrchestrator(orchestrator);\n\n orchestratorRegistry.set(orchestrator.id, orchestrator);\n \n console.log(`🎭 Orchestrator registered: ${orchestrator.id} (${orchestrator.type})`);\n}\n\n/**\n * Get an orchestrator by ID\n * @param id - The orchestrator ID\n * @returns The orchestrator instance or undefined if not found\n */\nexport function getOrchestrator(id: string): BaseOrchestrator | undefined {\n if (!id || typeof id !== 'string') {\n return undefined;\n }\n\n return orchestratorRegistry.get(id);\n}\n\n/**\n * List all registered orchestrators\n * @returns Array of all registered orchestrators\n */\nexport function listOrchestrators(): BaseOrchestrator[] {\n return Array.from(orchestratorRegistry.values());\n}\n\n/**\n * Get orchestrators by type\n * @param type - The orchestrator type to filter by\n * @returns Array of orchestrators matching the type\n */\nexport function getOrchestratorsByType(type: OrchestratorType): BaseOrchestrator[] {\n return Array.from(orchestratorRegistry.values()).filter(o => o.type === type);\n}\n\n/**\n * Check if an orchestrator is registered\n * @param id - The orchestrator ID\n * @returns True if the orchestrator is registered\n */\nexport function hasOrchestrator(id: string): boolean {\n return orchestratorRegistry.has(id);\n}\n\n/**\n * Unregister an orchestrator\n * @param id - The orchestrator ID to remove\n * @returns True if the orchestrator was removed, false if it didn't exist\n */\nexport function unregisterOrchestrator(id: string): boolean {\n const removed = orchestratorRegistry.delete(id);\n \n if (removed) {\n console.log(`🎭 Orchestrator unregistered: ${id}`);\n }\n \n return removed;\n}\n\n/**\n * Clear all registered orchestrators\n */\nexport function clearOrchestratorRegistry(): void {\n const count = orchestratorRegistry.size;\n orchestratorRegistry.clear();\n \n console.log(`🎭 Orchestrator registry cleared: ${count} orchestrators removed`);\n}\n\n/**\n * Get registry statistics\n * @returns Object with registry statistics\n */\nexport function getRegistryStats(): {\n total: number;\n byType: Record<OrchestratorType, number>;\n orchestratorIds: string[];\n} {\n const orchestrators = Array.from(orchestratorRegistry.values());\n \n const byType: Record<OrchestratorType, number> = {\n 'prompt-based': 0,\n 'custom-logic': 0,\n };\n\n orchestrators.forEach(o => {\n byType[o.type]++;\n });\n\n return {\n total: orchestrators.length,\n byType,\n orchestratorIds: orchestrators.map(o => o.id),\n };\n}\n\n// =============================================================================\n// VALIDATION HELPERS\n// =============================================================================\n\n/**\n * Validate orchestrator structure and required methods\n * @param orchestrator - The orchestrator to validate\n * @throws {Error} If the orchestrator is invalid\n */\nfunction validateOrchestrator(orchestrator: BaseOrchestrator): void {\n // Required properties\n if (!orchestrator.id || typeof orchestrator.id !== 'string') {\n throw new Error('Orchestrator must have a valid string ID');\n }\n\n if (!orchestrator.name || typeof orchestrator.name !== 'string') {\n throw new Error('Orchestrator must have a valid string name');\n }\n\n if (!orchestrator.description || typeof orchestrator.description !== 'string') {\n throw new Error('Orchestrator must have a valid string description');\n }\n\n if (!orchestrator.type || !['prompt-based', 'custom-logic'].includes(orchestrator.type)) {\n throw new Error('Orchestrator must have a valid type (prompt-based or custom-logic)');\n }\n\n // Required methods\n if (typeof orchestrator.execute !== 'function') {\n throw new Error('Orchestrator must implement execute method');\n }\n\n if (typeof orchestrator.getName !== 'function') {\n throw new Error('Orchestrator must implement getName method');\n }\n\n if (typeof orchestrator.getDescription !== 'function') {\n throw new Error('Orchestrator must implement getDescription method');\n }\n\n if (typeof orchestrator.getType !== 'function') {\n throw new Error('Orchestrator must implement getType method');\n }\n\n // Type-specific validation\n if (orchestrator.type === 'prompt-based') {\n const promptOrchestrator = orchestrator as any;\n if (!promptOrchestrator.systemPrompt || typeof promptOrchestrator.systemPrompt !== 'string') {\n throw new Error('Prompt-based orchestrator must have a valid systemPrompt property');\n }\n\n if (typeof promptOrchestrator.getSystemPrompt !== 'function') {\n throw new Error('Prompt-based orchestrator must implement getSystemPrompt method');\n }\n }\n\n if (orchestrator.type === 'custom-logic') {\n const customOrchestrator = orchestrator as any;\n if (typeof customOrchestrator.customLogic !== 'function') {\n throw new Error('Custom-logic orchestrator must implement customLogic method');\n }\n }\n\n // Optional method validation\n if (orchestrator.validate && typeof orchestrator.validate !== 'function') {\n throw new Error('Orchestrator validate property must be a function if provided');\n }\n\n if (orchestrator.initialize && typeof orchestrator.initialize !== 'function') {\n throw new Error('Orchestrator initialize property must be a function if provided');\n }\n\n if (orchestrator.cleanup && typeof orchestrator.cleanup !== 'function') {\n throw new Error('Orchestrator cleanup property must be a function if provided');\n }\n}\n\n/**\n * Resolve orchestrator from string ID or instance\n * @param orchestratorInput - String ID or orchestrator instance\n * @returns The orchestrator instance or undefined if not found\n */\nexport function resolveOrchestrator(orchestratorInput: string | BaseOrchestrator | undefined): BaseOrchestrator | undefined {\n if (!orchestratorInput) {\n return undefined;\n }\n\n if (typeof orchestratorInput === 'string') {\n return getOrchestrator(orchestratorInput);\n }\n\n if (typeof orchestratorInput === 'object' && orchestratorInput.id) {\n // Validate the orchestrator object\n try {\n validateOrchestrator(orchestratorInput);\n return orchestratorInput;\n } catch (error) {\n console.warn(`🎭 Invalid orchestrator object: ${error instanceof Error ? error.message : String(error)}`);\n return undefined;\n }\n }\n\n return undefined;\n}","import { generateText } from 'ai';\nimport type { AIModel, Message, CoreMessage, ExecutionResult, OpenAgenticTool, LoggingConfig, LogLevel, ExecutionStats, StepInfo, BaseOrchestrator, OrchestratorContext, OrchestratorOptions, PromptBasedOrchestrator, CustomLogicOrchestrator } from './types';\nimport { ProviderManager } from './providers/manager';\nimport { resolveOrchestrator } from './orchestrators/registry';\nimport { getModel } from './tools/utils';\n\nexport class Orchestrator {\n private model: AIModel;\n private tools: Record<string, OpenAgenticTool> = {};\n private messages: Message[] = [];\n private iterations = 0;\n private maxIterations: number;\n private customLogic?: (input: string, context: any) => Promise<any>;\n \n // Orchestrator support\n private orchestrator?: BaseOrchestrator;\n private orchestratorOptions: OrchestratorOptions;\n \n // Logging configuration\n private loggingConfig: LoggingConfig;\n private executionStartTime = 0;\n private stepTimings: number[] = [];\n private toolCallTimings: number[] = [];\n private stepsExecuted = 0;\n private toolCallsExecuted = 0;\n\n constructor(options: {\n model: string | AIModel;\n tools?: any[];\n systemPrompt?: string;\n maxIterations?: number;\n customLogic?: (input: string, context: any) => Promise<any>;\n enableDebugLogging?: boolean;\n logLevel?: LogLevel;\n enableStepLogging?: boolean;\n enableToolLogging?: boolean;\n enableTimingLogging?: boolean;\n enableStatisticsLogging?: boolean;\n } & OrchestratorOptions) {\n // Use ProviderManager for centralized model creation\n this.model = ProviderManager.createModel(options.model);\n this.maxIterations = options.maxIterations || 10;\n this.customLogic = options.customLogic;\n \n // Store orchestrator options\n this.orchestratorOptions = {\n orchestrator: options.orchestrator,\n orchestratorId: options.orchestratorId,\n orchestratorParams: options.orchestratorParams,\n allowOrchestratorPromptOverride: options.allowOrchestratorPromptOverride ?? true,\n allowOrchestratorToolControl: options.allowOrchestratorToolControl ?? true,\n };\n \n // Resolve orchestrator if provided\n this.orchestrator = resolveOrchestrator(\n options.orchestrator || options.orchestratorId\n );\n \n // Configure logging\n this.loggingConfig = {\n enableDebugLogging: options.enableDebugLogging ?? false,\n logLevel: options.logLevel ?? 'basic',\n enableStepLogging: options.enableStepLogging ?? false,\n enableToolLogging: options.enableToolLogging ?? false,\n enableTimingLogging: options.enableTimingLogging ?? false,\n enableStatisticsLogging: options.enableStatisticsLogging ?? false,\n };\n \n // Register tools with validation and LangChain auto-conversion\n if (options.tools) {\n // Auto-convert LangChain tools if present (tree-shaking friendly)\n let processedTools = options.tools;\n \n try {\n // Dynamic import for tree-shaking - only loads if tools are present\n const { hasLangChainTools, autoConvertLangChainTools } = require('./tools/utils');\n \n if (hasLangChainTools(options.tools)) {\n // Convert synchronously since we're in constructor\n // Note: This requires the conversion to be sync or we need to make constructor async\n console.log('🔗 Detected LangChain tools, converting...');\n \n // Convert each tool individually to avoid async issues\n processedTools = options.tools.map((tool) => {\n if (tool && typeof tool === 'object' && \n typeof tool.name === 'string' && \n typeof tool.description === 'string' &&\n (typeof tool.call === 'function' || typeof tool.invoke === 'function')) {\n \n // This is a LangChain tool - convert it synchronously\n const { convertLangchainToolSync } = require('./tools/utils');\n return convertLangchainToolSync(tool);\n }\n return tool;\n });\n }\n } catch (error) {\n // If utils can't be loaded or conversion fails, continue with original tools\n console.warn('⚠️ LangChain tool conversion failed, using original tools:', error);\n processedTools = options.tools;\n }\n \n processedTools.forEach((tool, index) => {\n const toolName = tool.toolId;\n \n // Ensure toolId uniqueness\n if (this.tools[toolName]) {\n throw new Error(`Tool with name ${toolName} already exists`);\n }\n \n this.tools[toolName] = tool;\n });\n }\n \n // Add system prompt if provided (orchestrator may override this)\n if (options.systemPrompt) {\n this.messages.push({\n role: 'system',\n content: options.systemPrompt,\n });\n }\n\n this.log('🔧', 'Orchestrator initialized', {\n model: `${this.model.provider}/${this.model.model}`,\n toolsCount: Object.keys(this.tools).length,\n maxIterations: this.maxIterations,\n loggingLevel: this.loggingConfig.logLevel,\n hasCustomLogic: !!this.customLogic,\n hasOrchestrator: !!this.orchestrator,\n orchestratorId: this.orchestrator?.id,\n orchestratorType: this.orchestrator?.type,\n });\n }\n\n // Core execution method - supports both string and message array inputs\n public async execute(input: string): Promise<ExecutionResult>;\n public async execute(messages: CoreMessage[]): Promise<ExecutionResult>;\n public async execute(input: string | CoreMessage[]): Promise<ExecutionResult> {\n this.executionStartTime = Date.now();\n this.resetExecutionStats();\n \n try {\n // Validate input before logging to prevent errors\n const inputType = typeof input === 'string' ? 'string' : \n Array.isArray(input) ? 'message_array' : \n