@framers/agentos 0.1.113 → 0.1.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -5
- package/dist/api/AgentOS.d.ts +45 -12
- package/dist/api/AgentOS.d.ts.map +1 -1
- package/dist/api/AgentOS.js +225 -78
- package/dist/api/AgentOS.js.map +1 -1
- package/dist/api/AgentOSOrchestrator.d.ts +8 -0
- package/dist/api/AgentOSOrchestrator.d.ts.map +1 -1
- package/dist/api/AgentOSOrchestrator.js +350 -59
- package/dist/api/AgentOSOrchestrator.js.map +1 -1
- package/dist/api/StreamChunkEmitter.d.ts.map +1 -1
- package/dist/api/StreamChunkEmitter.js +2 -0
- package/dist/api/StreamChunkEmitter.js.map +1 -1
- package/dist/api/agency.d.ts.map +1 -1
- package/dist/api/agency.js +47 -1
- package/dist/api/agency.js.map +1 -1
- package/dist/api/agent.d.ts +18 -5
- package/dist/api/agent.d.ts.map +1 -1
- package/dist/api/agent.js +48 -9
- package/dist/api/agent.js.map +1 -1
- package/dist/api/agentExport.d.ts +202 -0
- package/dist/api/agentExport.d.ts.map +1 -0
- package/dist/api/agentExport.js +323 -0
- package/dist/api/agentExport.js.map +1 -0
- package/dist/api/editImage.d.ts +119 -0
- package/dist/api/editImage.d.ts.map +1 -0
- package/dist/api/editImage.js +150 -0
- package/dist/api/editImage.js.map +1 -0
- package/dist/api/embedText.d.ts +137 -0
- package/dist/api/embedText.d.ts.map +1 -0
- package/dist/api/embedText.js +229 -0
- package/dist/api/embedText.js.map +1 -0
- package/dist/api/externalToolRegistry.d.ts +44 -0
- package/dist/api/externalToolRegistry.d.ts.map +1 -0
- package/dist/api/externalToolRegistry.js +245 -0
- package/dist/api/externalToolRegistry.js.map +1 -0
- package/dist/api/generateImage.d.ts +1 -1
- package/dist/api/generateImage.d.ts.map +1 -1
- package/dist/api/generateImage.js +17 -13
- package/dist/api/generateImage.js.map +1 -1
- package/dist/api/generateObject.d.ts +185 -0
- package/dist/api/generateObject.d.ts.map +1 -0
- package/dist/api/generateObject.js +249 -0
- package/dist/api/generateObject.js.map +1 -0
- package/dist/api/generateText.d.ts +13 -3
- package/dist/api/generateText.d.ts.map +1 -1
- package/dist/api/generateText.js +20 -5
- package/dist/api/generateText.js.map +1 -1
- package/dist/api/interfaces/IAgentOS.d.ts +29 -1
- package/dist/api/interfaces/IAgentOS.d.ts.map +1 -1
- package/dist/api/model.d.ts +7 -7
- package/dist/api/model.d.ts.map +1 -1
- package/dist/api/model.js +22 -16
- package/dist/api/model.js.map +1 -1
- package/dist/api/processRequestWithExternalTools.d.ts +26 -0
- package/dist/api/processRequestWithExternalTools.d.ts.map +1 -0
- package/dist/api/processRequestWithExternalTools.js +52 -0
- package/dist/api/processRequestWithExternalTools.js.map +1 -0
- package/dist/api/processRequestWithRegisteredTools.d.ts +56 -0
- package/dist/api/processRequestWithRegisteredTools.d.ts.map +1 -0
- package/dist/api/processRequestWithRegisteredTools.js +125 -0
- package/dist/api/processRequestWithRegisteredTools.js.map +1 -0
- package/dist/api/provider-defaults.d.ts.map +1 -1
- package/dist/api/provider-defaults.js +28 -0
- package/dist/api/provider-defaults.js.map +1 -1
- package/dist/api/resumeExternalToolRequestWithRegisteredTools.d.ts +71 -0
- package/dist/api/resumeExternalToolRequestWithRegisteredTools.d.ts.map +1 -0
- package/dist/api/resumeExternalToolRequestWithRegisteredTools.js +159 -0
- package/dist/api/resumeExternalToolRequestWithRegisteredTools.js.map +1 -0
- package/dist/api/strategies/agentGraphBuilder.d.ts +170 -0
- package/dist/api/strategies/agentGraphBuilder.d.ts.map +1 -0
- package/dist/api/strategies/agentGraphBuilder.js +299 -0
- package/dist/api/strategies/agentGraphBuilder.js.map +1 -0
- package/dist/api/strategies/graphCompiler.d.ts +84 -0
- package/dist/api/strategies/graphCompiler.d.ts.map +1 -0
- package/dist/api/strategies/graphCompiler.js +617 -0
- package/dist/api/strategies/graphCompiler.js.map +1 -0
- package/dist/api/strategies/hierarchical.d.ts.map +1 -1
- package/dist/api/strategies/hierarchical.js +2 -1
- package/dist/api/strategies/hierarchical.js.map +1 -1
- package/dist/api/strategies/index.d.ts +3 -0
- package/dist/api/strategies/index.d.ts.map +1 -1
- package/dist/api/strategies/index.js +2 -0
- package/dist/api/strategies/index.js.map +1 -1
- package/dist/api/strategies/shared.d.ts +1 -1
- package/dist/api/strategies/shared.d.ts.map +1 -1
- package/dist/api/strategies/shared.js +3 -2
- package/dist/api/strategies/shared.js.map +1 -1
- package/dist/api/streamObject.d.ts +166 -0
- package/dist/api/streamObject.d.ts.map +1 -0
- package/dist/api/streamObject.js +268 -0
- package/dist/api/streamObject.js.map +1 -0
- package/dist/api/streamText.d.ts +1 -1
- package/dist/api/streamText.d.ts.map +1 -1
- package/dist/api/streamText.js +26 -8
- package/dist/api/streamText.js.map +1 -1
- package/dist/api/toolAdapter.d.ts +44 -8
- package/dist/api/toolAdapter.d.ts.map +1 -1
- package/dist/api/toolAdapter.js +224 -45
- package/dist/api/toolAdapter.js.map +1 -1
- package/dist/api/types/AgentOSExternalToolRequest.d.ts +35 -0
- package/dist/api/types/AgentOSExternalToolRequest.d.ts.map +1 -0
- package/dist/api/types/AgentOSExternalToolRequest.js +2 -0
- package/dist/api/types/AgentOSExternalToolRequest.js.map +1 -0
- package/dist/api/types/AgentOSResponse.d.ts +25 -0
- package/dist/api/types/AgentOSResponse.d.ts.map +1 -1
- package/dist/api/types/AgentOSResponse.js +20 -0
- package/dist/api/types/AgentOSResponse.js.map +1 -1
- package/dist/api/types/AgentOSToolResult.d.ts +11 -0
- package/dist/api/types/AgentOSToolResult.d.ts.map +1 -0
- package/dist/api/types/AgentOSToolResult.js +2 -0
- package/dist/api/types/AgentOSToolResult.js.map +1 -0
- package/dist/api/types.d.ts +28 -4
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/types.js.map +1 -1
- package/dist/api/upscaleImage.d.ts +92 -0
- package/dist/api/upscaleImage.d.ts.map +1 -0
- package/dist/api/upscaleImage.js +133 -0
- package/dist/api/upscaleImage.js.map +1 -0
- package/dist/api/variateImage.d.ts +102 -0
- package/dist/api/variateImage.d.ts.map +1 -0
- package/dist/api/variateImage.js +154 -0
- package/dist/api/variateImage.js.map +1 -0
- package/dist/cognitive_substrate/GMI.d.ts +16 -2
- package/dist/cognitive_substrate/GMI.d.ts.map +1 -1
- package/dist/cognitive_substrate/GMI.js +188 -56
- package/dist/cognitive_substrate/GMI.js.map +1 -1
- package/dist/cognitive_substrate/IGMI.d.ts +10 -0
- package/dist/cognitive_substrate/IGMI.d.ts.map +1 -1
- package/dist/cognitive_substrate/IGMI.js.map +1 -1
- package/dist/config/AgentOSConfig.d.ts +19 -2
- package/dist/config/AgentOSConfig.d.ts.map +1 -1
- package/dist/config/AgentOSConfig.js +46 -29
- package/dist/config/AgentOSConfig.js.map +1 -1
- package/dist/core/guardrails/IGuardrailService.d.ts +1 -1
- package/dist/core/images/IImageProvider.d.ts +93 -0
- package/dist/core/images/IImageProvider.d.ts.map +1 -1
- package/dist/core/images/IImageProvider.js.map +1 -1
- package/dist/core/images/ImageOperationError.d.ts +52 -0
- package/dist/core/images/ImageOperationError.d.ts.map +1 -0
- package/dist/core/images/ImageOperationError.js +58 -0
- package/dist/core/images/ImageOperationError.js.map +1 -0
- package/dist/core/images/imageToBuffer.d.ts +41 -0
- package/dist/core/images/imageToBuffer.d.ts.map +1 -0
- package/dist/core/images/imageToBuffer.js +95 -0
- package/dist/core/images/imageToBuffer.js.map +1 -0
- package/dist/core/images/index.d.ts +4 -0
- package/dist/core/images/index.d.ts.map +1 -1
- package/dist/core/images/index.js +8 -0
- package/dist/core/images/index.js.map +1 -1
- package/dist/core/images/providers/FalImageProvider.d.ts +208 -0
- package/dist/core/images/providers/FalImageProvider.d.ts.map +1 -0
- package/dist/core/images/providers/FalImageProvider.js +301 -0
- package/dist/core/images/providers/FalImageProvider.js.map +1 -0
- package/dist/core/images/providers/FluxImageProvider.d.ts +197 -0
- package/dist/core/images/providers/FluxImageProvider.d.ts.map +1 -0
- package/dist/core/images/providers/FluxImageProvider.js +271 -0
- package/dist/core/images/providers/FluxImageProvider.js.map +1 -0
- package/dist/core/images/providers/OpenAIImageProvider.d.ts +33 -1
- package/dist/core/images/providers/OpenAIImageProvider.d.ts.map +1 -1
- package/dist/core/images/providers/OpenAIImageProvider.js +125 -0
- package/dist/core/images/providers/OpenAIImageProvider.js.map +1 -1
- package/dist/core/images/providers/ReplicateImageProvider.d.ts +26 -1
- package/dist/core/images/providers/ReplicateImageProvider.d.ts.map +1 -1
- package/dist/core/images/providers/ReplicateImageProvider.js +118 -0
- package/dist/core/images/providers/ReplicateImageProvider.js.map +1 -1
- package/dist/core/images/providers/StabilityImageProvider.d.ts +41 -1
- package/dist/core/images/providers/StabilityImageProvider.d.ts.map +1 -1
- package/dist/core/images/providers/StabilityImageProvider.js +180 -7
- package/dist/core/images/providers/StabilityImageProvider.js.map +1 -1
- package/dist/core/images/providers/StableDiffusionLocalProvider.d.ts +29 -1
- package/dist/core/images/providers/StableDiffusionLocalProvider.d.ts.map +1 -1
- package/dist/core/images/providers/StableDiffusionLocalProvider.js +124 -0
- package/dist/core/images/providers/StableDiffusionLocalProvider.js.map +1 -1
- package/dist/core/llm/IPromptEngine.d.ts +2 -2
- package/dist/core/llm/IPromptEngine.d.ts.map +1 -1
- package/dist/core/llm/IPromptEngine.js +2 -2
- package/dist/core/llm/IPromptEngine.js.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.d.ts +7 -1
- package/dist/core/llm/providers/AIModelProviderManager.d.ts.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.js +24 -0
- package/dist/core/llm/providers/AIModelProviderManager.js.map +1 -1
- package/dist/core/llm/providers/errors/AnthropicProviderError.d.ts +42 -0
- package/dist/core/llm/providers/errors/AnthropicProviderError.d.ts.map +1 -0
- package/dist/core/llm/providers/errors/AnthropicProviderError.js +45 -0
- package/dist/core/llm/providers/errors/AnthropicProviderError.js.map +1 -0
- package/dist/core/llm/providers/errors/GeminiProviderError.d.ts +45 -0
- package/dist/core/llm/providers/errors/GeminiProviderError.d.ts.map +1 -0
- package/dist/core/llm/providers/errors/GeminiProviderError.js +46 -0
- package/dist/core/llm/providers/errors/GeminiProviderError.js.map +1 -0
- package/dist/core/llm/providers/errors/OllamaProviderError.d.ts +1 -1
- package/dist/core/llm/providers/errors/OllamaProviderError.d.ts.map +1 -1
- package/dist/core/llm/providers/errors/OllamaProviderError.js +1 -1
- package/dist/core/llm/providers/errors/OllamaProviderError.js.map +1 -1
- package/dist/core/llm/providers/errors/OpenAIProviderError.d.ts +1 -1
- package/dist/core/llm/providers/errors/OpenAIProviderError.js +1 -1
- package/dist/core/llm/providers/errors/OpenRouterProviderError.d.ts +1 -1
- package/dist/core/llm/providers/errors/OpenRouterProviderError.js +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts +340 -0
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts.map +1 -0
- package/dist/core/llm/providers/implementations/AnthropicProvider.js +959 -0
- package/dist/core/llm/providers/implementations/AnthropicProvider.js.map +1 -0
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts +339 -0
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts.map +1 -0
- package/dist/core/llm/providers/implementations/GeminiProvider.js +1004 -0
- package/dist/core/llm/providers/implementations/GeminiProvider.js.map +1 -0
- package/dist/core/llm/providers/implementations/GroqProvider.d.ts +105 -0
- package/dist/core/llm/providers/implementations/GroqProvider.d.ts.map +1 -0
- package/dist/core/llm/providers/implementations/GroqProvider.js +134 -0
- package/dist/core/llm/providers/implementations/GroqProvider.js.map +1 -0
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts +105 -0
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts.map +1 -0
- package/dist/core/llm/providers/implementations/MistralProvider.js +146 -0
- package/dist/core/llm/providers/implementations/MistralProvider.js.map +1 -0
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts +107 -0
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts.map +1 -0
- package/dist/core/llm/providers/implementations/TogetherProvider.js +138 -0
- package/dist/core/llm/providers/implementations/TogetherProvider.js.map +1 -0
- package/dist/core/llm/providers/implementations/XAIProvider.d.ts +102 -0
- package/dist/core/llm/providers/implementations/XAIProvider.d.ts.map +1 -0
- package/dist/core/llm/providers/implementations/XAIProvider.js +123 -0
- package/dist/core/llm/providers/implementations/XAIProvider.js.map +1 -0
- package/dist/core/orchestration/AgentOrchestrator.d.ts.map +1 -1
- package/dist/core/orchestration/AgentOrchestrator.js +26 -5
- package/dist/core/orchestration/AgentOrchestrator.js.map +1 -1
- package/dist/core/tools/IToolOrchestrator.d.ts +2 -2
- package/dist/core/tools/IToolOrchestrator.d.ts.map +1 -1
- package/dist/core/tools/ToolExecutor.d.ts +3 -0
- package/dist/core/tools/ToolExecutor.d.ts.map +1 -1
- package/dist/core/tools/ToolExecutor.js +2 -1
- package/dist/core/tools/ToolExecutor.js.map +1 -1
- package/dist/core/tools/ToolOrchestrator.d.ts +7 -7
- package/dist/core/tools/ToolOrchestrator.d.ts.map +1 -1
- package/dist/core/tools/ToolOrchestrator.js +135 -36
- package/dist/core/tools/ToolOrchestrator.js.map +1 -1
- package/dist/core/tools/permissions/ToolPermissionManager.d.ts +6 -5
- package/dist/core/tools/permissions/ToolPermissionManager.d.ts.map +1 -1
- package/dist/core/tools/permissions/ToolPermissionManager.js +47 -21
- package/dist/core/tools/permissions/ToolPermissionManager.js.map +1 -1
- package/dist/core/vision/VisionPipeline.d.ts +437 -0
- package/dist/core/vision/VisionPipeline.d.ts.map +1 -0
- package/dist/core/vision/VisionPipeline.js +1113 -0
- package/dist/core/vision/VisionPipeline.js.map +1 -0
- package/dist/core/vision/index.d.ts +97 -0
- package/dist/core/vision/index.d.ts.map +1 -0
- package/dist/core/vision/index.js +182 -0
- package/dist/core/vision/index.js.map +1 -0
- package/dist/core/vision/providers/LLMVisionProvider.d.ts +135 -0
- package/dist/core/vision/providers/LLMVisionProvider.d.ts.map +1 -0
- package/dist/core/vision/providers/LLMVisionProvider.js +136 -0
- package/dist/core/vision/providers/LLMVisionProvider.js.map +1 -0
- package/dist/core/vision/providers/PipelineVisionProvider.d.ts +154 -0
- package/dist/core/vision/providers/PipelineVisionProvider.d.ts.map +1 -0
- package/dist/core/vision/providers/PipelineVisionProvider.js +160 -0
- package/dist/core/vision/providers/PipelineVisionProvider.js.map +1 -0
- package/dist/core/vision/types.d.ts +286 -0
- package/dist/core/vision/types.d.ts.map +1 -0
- package/dist/core/vision/types.js +24 -0
- package/dist/core/vision/types.js.map +1 -0
- package/dist/discovery/CapabilityDiscoveryEngine.d.ts +1 -1
- package/dist/discovery/CapabilityDiscoveryEngine.d.ts.map +1 -1
- package/dist/discovery/CapabilityDiscoveryEngine.js +1 -1
- package/dist/discovery/CapabilityDiscoveryEngine.js.map +1 -1
- package/dist/emergent/ComposableToolBuilder.d.ts +15 -4
- package/dist/emergent/ComposableToolBuilder.d.ts.map +1 -1
- package/dist/emergent/ComposableToolBuilder.js +29 -14
- package/dist/emergent/ComposableToolBuilder.js.map +1 -1
- package/dist/emergent/EmergentCapabilityEngine.d.ts +3 -3
- package/dist/emergent/EmergentCapabilityEngine.d.ts.map +1 -1
- package/dist/emergent/EmergentCapabilityEngine.js +15 -12
- package/dist/emergent/EmergentCapabilityEngine.js.map +1 -1
- package/dist/emergent/EmergentJudge.d.ts +20 -0
- package/dist/emergent/EmergentJudge.d.ts.map +1 -1
- package/dist/emergent/EmergentJudge.js +121 -26
- package/dist/emergent/EmergentJudge.js.map +1 -1
- package/dist/emergent/EmergentToolRegistry.d.ts +17 -0
- package/dist/emergent/EmergentToolRegistry.d.ts.map +1 -1
- package/dist/emergent/EmergentToolRegistry.js +26 -0
- package/dist/emergent/EmergentToolRegistry.js.map +1 -1
- package/dist/emergent/ForgeToolMetaTool.d.ts +1 -1
- package/dist/emergent/ForgeToolMetaTool.d.ts.map +1 -1
- package/dist/emergent/ForgeToolMetaTool.js +15 -2
- package/dist/emergent/ForgeToolMetaTool.js.map +1 -1
- package/dist/emergent/SandboxedToolForge.d.ts +2 -2
- package/dist/emergent/SandboxedToolForge.d.ts.map +1 -1
- package/dist/emergent/SandboxedToolForge.js +13 -23
- package/dist/emergent/SandboxedToolForge.js.map +1 -1
- package/dist/emergent/SkillExporter.d.ts +119 -0
- package/dist/emergent/SkillExporter.d.ts.map +1 -0
- package/dist/emergent/SkillExporter.js +344 -0
- package/dist/emergent/SkillExporter.js.map +1 -0
- package/dist/emergent/index.d.ts +1 -0
- package/dist/emergent/index.d.ts.map +1 -1
- package/dist/emergent/index.js +1 -0
- package/dist/emergent/index.js.map +1 -1
- package/dist/emergent/types.d.ts +4 -4
- package/dist/index.d.ts +30 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/memory/index.d.ts +10 -8
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +2 -0
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/ingestion/DoclingLoader.d.ts +3 -3
- package/dist/memory/ingestion/DoclingLoader.d.ts.map +1 -1
- package/dist/memory/ingestion/DoclingLoader.js +12 -8
- package/dist/memory/ingestion/DoclingLoader.js.map +1 -1
- package/dist/memory/ingestion/FolderScanner.d.ts +7 -7
- package/dist/memory/ingestion/FolderScanner.d.ts.map +1 -1
- package/dist/memory/ingestion/FolderScanner.js +6 -6
- package/dist/memory/ingestion/FolderScanner.js.map +1 -1
- package/dist/memory/ingestion/LoaderRegistry.d.ts +8 -8
- package/dist/memory/ingestion/LoaderRegistry.d.ts.map +1 -1
- package/dist/memory/ingestion/LoaderRegistry.js +9 -11
- package/dist/memory/ingestion/LoaderRegistry.js.map +1 -1
- package/dist/memory/ingestion/MultimodalAggregator.d.ts +1 -1
- package/dist/memory/ingestion/MultimodalAggregator.js +1 -1
- package/dist/memory/ingestion/OcrPdfLoader.d.ts +2 -2
- package/dist/memory/ingestion/OcrPdfLoader.d.ts.map +1 -1
- package/dist/memory/ingestion/OcrPdfLoader.js +12 -8
- package/dist/memory/ingestion/OcrPdfLoader.js.map +1 -1
- package/dist/memory/ingestion/PdfLoader.d.ts +8 -8
- package/dist/memory/ingestion/PdfLoader.d.ts.map +1 -1
- package/dist/memory/ingestion/PdfLoader.js +13 -10
- package/dist/memory/ingestion/PdfLoader.js.map +1 -1
- package/dist/memory/io/MarkdownExporter.d.ts +1 -1
- package/dist/memory/io/MarkdownExporter.d.ts.map +1 -1
- package/dist/memory/io/MarkdownExporter.js +1 -1
- package/dist/memory/io/MarkdownExporter.js.map +1 -1
- package/dist/memory/tools/MemoryAddTool.d.ts +2 -2
- package/dist/memory/tools/MemoryAddTool.d.ts.map +1 -1
- package/dist/memory/tools/MemoryAddTool.js +8 -3
- package/dist/memory/tools/MemoryAddTool.js.map +1 -1
- package/dist/memory/tools/MemorySearchTool.d.ts +3 -3
- package/dist/memory/tools/MemorySearchTool.d.ts.map +1 -1
- package/dist/memory/tools/MemorySearchTool.js +11 -9
- package/dist/memory/tools/MemorySearchTool.js.map +1 -1
- package/dist/memory/tools/scopeContext.d.ts +11 -0
- package/dist/memory/tools/scopeContext.d.ts.map +1 -0
- package/dist/memory/tools/scopeContext.js +46 -0
- package/dist/memory/tools/scopeContext.js.map +1 -0
- package/dist/orchestration/builders/AgentGraph.d.ts +12 -11
- package/dist/orchestration/builders/AgentGraph.d.ts.map +1 -1
- package/dist/orchestration/builders/AgentGraph.js +12 -11
- package/dist/orchestration/builders/AgentGraph.js.map +1 -1
- package/dist/orchestration/builders/VoiceNodeBuilder.d.ts +2 -2
- package/dist/orchestration/builders/VoiceNodeBuilder.d.ts.map +1 -1
- package/dist/orchestration/builders/VoiceNodeBuilder.js +2 -2
- package/dist/orchestration/builders/VoiceNodeBuilder.js.map +1 -1
- package/dist/orchestration/builders/WorkflowBuilder.d.ts +1 -1
- package/dist/orchestration/builders/WorkflowBuilder.d.ts.map +1 -1
- package/dist/orchestration/builders/WorkflowBuilder.js +1 -1
- package/dist/orchestration/builders/WorkflowBuilder.js.map +1 -1
- package/dist/orchestration/checkpoint/InMemoryCheckpointStore.d.ts +7 -54
- package/dist/orchestration/checkpoint/InMemoryCheckpointStore.d.ts.map +1 -1
- package/dist/orchestration/checkpoint/InMemoryCheckpointStore.js +8 -56
- package/dist/orchestration/checkpoint/InMemoryCheckpointStore.js.map +1 -1
- package/dist/orchestration/events/GraphEvent.d.ts +12 -12
- package/dist/orchestration/events/GraphEvent.d.ts.map +1 -1
- package/dist/orchestration/events/GraphEvent.js.map +1 -1
- package/dist/orchestration/runtime/GraphRuntime.d.ts.map +1 -1
- package/dist/orchestration/runtime/GraphRuntime.js +151 -1
- package/dist/orchestration/runtime/GraphRuntime.js.map +1 -1
- package/dist/orchestration/runtime/LoopController.d.ts +3 -3
- package/dist/orchestration/runtime/LoopController.d.ts.map +1 -1
- package/dist/orchestration/runtime/LoopController.js.map +1 -1
- package/dist/orchestration/runtime/StateManager.d.ts +3 -3
- package/dist/orchestration/runtime/StateManager.js +3 -3
- package/dist/orchestration/runtime/VoiceNodeExecutor.d.ts +6 -6
- package/dist/orchestration/runtime/VoiceNodeExecutor.d.ts.map +1 -1
- package/dist/orchestration/runtime/VoiceNodeExecutor.js +27 -10
- package/dist/orchestration/runtime/VoiceNodeExecutor.js.map +1 -1
- package/dist/orchestration/runtime/VoiceTransportAdapter.d.ts +5 -5
- package/dist/orchestration/runtime/VoiceTransportAdapter.js +5 -5
- package/dist/orchestration/runtime/VoiceTurnCollector.d.ts +2 -2
- package/dist/orchestration/runtime/VoiceTurnCollector.js +2 -2
- package/dist/query-router/KeywordFallback.d.ts +70 -0
- package/dist/query-router/KeywordFallback.d.ts.map +1 -0
- package/dist/query-router/KeywordFallback.js +132 -0
- package/dist/query-router/KeywordFallback.js.map +1 -0
- package/dist/query-router/QueryClassifier.d.ts +140 -0
- package/dist/query-router/QueryClassifier.d.ts.map +1 -0
- package/dist/query-router/QueryClassifier.js +223 -0
- package/dist/query-router/QueryClassifier.js.map +1 -0
- package/dist/query-router/QueryDispatcher.d.ts +139 -0
- package/dist/query-router/QueryDispatcher.d.ts.map +1 -0
- package/dist/query-router/QueryDispatcher.js +297 -0
- package/dist/query-router/QueryDispatcher.js.map +1 -0
- package/dist/query-router/QueryGenerator.d.ts +184 -0
- package/dist/query-router/QueryGenerator.d.ts.map +1 -0
- package/dist/query-router/QueryGenerator.js +241 -0
- package/dist/query-router/QueryGenerator.js.map +1 -0
- package/dist/query-router/QueryRouter.d.ts +292 -0
- package/dist/query-router/QueryRouter.d.ts.map +1 -0
- package/dist/query-router/QueryRouter.js +803 -0
- package/dist/query-router/QueryRouter.js.map +1 -0
- package/dist/query-router/TopicExtractor.d.ts +73 -0
- package/dist/query-router/TopicExtractor.d.ts.map +1 -0
- package/dist/query-router/TopicExtractor.js +95 -0
- package/dist/query-router/TopicExtractor.js.map +1 -0
- package/dist/query-router/index.d.ts +40 -0
- package/dist/query-router/index.d.ts.map +1 -0
- package/dist/query-router/index.js +46 -0
- package/dist/query-router/index.js.map +1 -0
- package/dist/query-router/types.d.ts +508 -0
- package/dist/query-router/types.d.ts.map +1 -0
- package/dist/query-router/types.js +39 -0
- package/dist/query-router/types.js.map +1 -0
- package/dist/rag/index.d.ts +5 -0
- package/dist/rag/index.d.ts.map +1 -1
- package/dist/rag/index.js +7 -0
- package/dist/rag/index.js.map +1 -1
- package/dist/rag/multimodal/LLMVisionAdapter.d.ts +43 -0
- package/dist/rag/multimodal/LLMVisionAdapter.d.ts.map +1 -0
- package/dist/rag/multimodal/LLMVisionAdapter.js +46 -0
- package/dist/rag/multimodal/LLMVisionAdapter.js.map +1 -0
- package/dist/rag/multimodal/MultimodalIndexer.d.ts +244 -0
- package/dist/rag/multimodal/MultimodalIndexer.d.ts.map +1 -0
- package/dist/rag/multimodal/MultimodalIndexer.js +411 -0
- package/dist/rag/multimodal/MultimodalIndexer.js.map +1 -0
- package/dist/rag/multimodal/MultimodalMemoryBridge.d.ts +448 -0
- package/dist/rag/multimodal/MultimodalMemoryBridge.d.ts.map +1 -0
- package/dist/rag/multimodal/MultimodalMemoryBridge.js +941 -0
- package/dist/rag/multimodal/MultimodalMemoryBridge.js.map +1 -0
- package/dist/rag/multimodal/SpeechProviderAdapter.d.ts +139 -0
- package/dist/rag/multimodal/SpeechProviderAdapter.d.ts.map +1 -0
- package/dist/rag/multimodal/SpeechProviderAdapter.js +143 -0
- package/dist/rag/multimodal/SpeechProviderAdapter.js.map +1 -0
- package/dist/rag/multimodal/createMultimodalIndexerFromResolver.d.ts +172 -0
- package/dist/rag/multimodal/createMultimodalIndexerFromResolver.d.ts.map +1 -0
- package/dist/rag/multimodal/createMultimodalIndexerFromResolver.js +152 -0
- package/dist/rag/multimodal/createMultimodalIndexerFromResolver.js.map +1 -0
- package/dist/rag/multimodal/index.d.ts +44 -0
- package/dist/rag/multimodal/index.d.ts.map +1 -0
- package/dist/rag/multimodal/index.js +42 -0
- package/dist/rag/multimodal/index.js.map +1 -0
- package/dist/rag/multimodal/types.d.ts +276 -0
- package/dist/rag/multimodal/types.d.ts.map +1 -0
- package/dist/rag/multimodal/types.js +26 -0
- package/dist/rag/multimodal/types.js.map +1 -0
- package/dist/social-posting/SocialPostManager.d.ts +3 -3
- package/dist/social-posting/SocialPostManager.d.ts.map +1 -1
- package/dist/social-posting/SocialPostManager.js +3 -5
- package/dist/social-posting/SocialPostManager.js.map +1 -1
- package/dist/speech/FallbackProxy.d.ts +6 -6
- package/dist/speech/FallbackProxy.d.ts.map +1 -1
- package/dist/speech/FallbackProxy.js +3 -3
- package/dist/speech/FallbackProxy.js.map +1 -1
- package/dist/speech/SpeechProviderResolver.d.ts +8 -8
- package/dist/speech/SpeechProviderResolver.d.ts.map +1 -1
- package/dist/speech/SpeechProviderResolver.js +22 -11
- package/dist/speech/SpeechProviderResolver.js.map +1 -1
- package/dist/speech/SpeechRuntime.d.ts +1 -5
- package/dist/speech/SpeechRuntime.d.ts.map +1 -1
- package/dist/speech/SpeechRuntime.js +17 -9
- package/dist/speech/SpeechRuntime.js.map +1 -1
- package/dist/speech/providers/AssemblyAISTTProvider.d.ts +4 -4
- package/dist/speech/providers/AssemblyAISTTProvider.js +4 -4
- package/dist/speech/providers/AzureSpeechTTSProvider.d.ts +3 -3
- package/dist/speech/providers/AzureSpeechTTSProvider.js +2 -2
- package/dist/speech/providers/AzureSpeechTTSProvider.js.map +1 -1
- package/dist/speech/providers/BuiltInAdaptiveVadProvider.d.ts +9 -9
- package/dist/speech/providers/BuiltInAdaptiveVadProvider.d.ts.map +1 -1
- package/dist/speech/providers/BuiltInAdaptiveVadProvider.js +5 -5
- package/dist/speech/providers/BuiltInAdaptiveVadProvider.js.map +1 -1
- package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts +2 -2
- package/dist/speech/providers/DeepgramBatchSTTProvider.js +2 -2
- package/dist/speech/providers/OpenAITextToSpeechProvider.d.ts +3 -3
- package/dist/speech/providers/OpenAITextToSpeechProvider.js +2 -2
- package/dist/speech/providers/OpenAIWhisperSpeechToTextProvider.d.ts +1 -1
- package/dist/speech/providers/OpenAIWhisperSpeechToTextProvider.d.ts.map +1 -1
- package/dist/speech/providers/OpenAIWhisperSpeechToTextProvider.js +1 -1
- package/dist/speech/providers/OpenAIWhisperSpeechToTextProvider.js.map +1 -1
- package/dist/voice/TelephonyStreamTransport.d.ts +6 -6
- package/dist/voice/TelephonyStreamTransport.d.ts.map +1 -1
- package/dist/voice/TelephonyStreamTransport.js +5 -5
- package/dist/voice/TelephonyStreamTransport.js.map +1 -1
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts +4 -4
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts.map +1 -1
- package/dist/voice-pipeline/AcousticEndpointDetector.js +4 -4
- package/dist/voice-pipeline/AcousticEndpointDetector.js.map +1 -1
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts +3 -3
- package/dist/voice-pipeline/HardCutBargeinHandler.js +3 -3
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts +3 -3
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts.map +1 -1
- package/dist/voice-pipeline/HeuristicEndpointDetector.js +3 -3
- package/dist/voice-pipeline/HeuristicEndpointDetector.js.map +1 -1
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts +5 -5
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js +1 -1
- package/dist/voice-pipeline/VoiceInterruptError.d.ts +6 -6
- package/dist/voice-pipeline/VoiceInterruptError.d.ts.map +1 -1
- package/dist/voice-pipeline/VoiceInterruptError.js +4 -4
- package/dist/voice-pipeline/VoiceInterruptError.js.map +1 -1
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts +9 -9
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts.map +1 -1
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js +8 -8
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js.map +1 -1
- package/dist/voice-pipeline/WebRTCStreamTransport.d.ts +421 -0
- package/dist/voice-pipeline/WebRTCStreamTransport.d.ts.map +1 -0
- package/dist/voice-pipeline/WebRTCStreamTransport.js +573 -0
- package/dist/voice-pipeline/WebRTCStreamTransport.js.map +1 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts +8 -8
- package/dist/voice-pipeline/WebSocketStreamTransport.js +5 -5
- package/dist/voice-pipeline/index.d.ts +1 -0
- package/dist/voice-pipeline/index.d.ts.map +1 -1
- package/dist/voice-pipeline/index.js +2 -0
- package/dist/voice-pipeline/index.js.map +1 -1
- package/dist/voice-pipeline/types.d.ts +43 -43
- package/dist/voice-pipeline/types.d.ts.map +1 -1
- package/package.json +19 -1
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// File: backend/agentos/core/llm/providers/implementations/GeminiProvider.ts
|
|
3
|
+
import { GeminiProviderError } from '../errors/GeminiProviderError.js';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Known model catalog
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/** Static catalog of well-known Gemini models and their metadata. */
|
|
8
|
+
const GEMINI_MODELS = [
|
|
9
|
+
{
|
|
10
|
+
modelId: 'gemini-2.5-flash',
|
|
11
|
+
providerId: 'gemini',
|
|
12
|
+
displayName: 'Gemini 2.5 Flash',
|
|
13
|
+
description: 'Fast, cost-effective model with strong reasoning and multimodal capabilities.',
|
|
14
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
15
|
+
contextWindowSize: 1048576,
|
|
16
|
+
outputTokenLimit: 65536,
|
|
17
|
+
pricePer1MTokensInput: 0.15,
|
|
18
|
+
pricePer1MTokensOutput: 0.60,
|
|
19
|
+
supportsStreaming: true,
|
|
20
|
+
status: 'active',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
modelId: 'gemini-2.5-pro',
|
|
24
|
+
providerId: 'gemini',
|
|
25
|
+
displayName: 'Gemini 2.5 Pro',
|
|
26
|
+
description: 'Most capable Gemini model for complex reasoning and analysis.',
|
|
27
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
28
|
+
contextWindowSize: 1048576,
|
|
29
|
+
outputTokenLimit: 65536,
|
|
30
|
+
pricePer1MTokensInput: 1.25,
|
|
31
|
+
pricePer1MTokensOutput: 10.00,
|
|
32
|
+
supportsStreaming: true,
|
|
33
|
+
status: 'active',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
modelId: 'gemini-2.0-flash',
|
|
37
|
+
providerId: 'gemini',
|
|
38
|
+
displayName: 'Gemini 2.0 Flash',
|
|
39
|
+
description: 'Previous-generation fast model with strong performance.',
|
|
40
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
41
|
+
contextWindowSize: 1048576,
|
|
42
|
+
outputTokenLimit: 8192,
|
|
43
|
+
pricePer1MTokensInput: 0.10,
|
|
44
|
+
pricePer1MTokensOutput: 0.40,
|
|
45
|
+
supportsStreaming: true,
|
|
46
|
+
status: 'active',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
modelId: 'gemini-1.5-pro',
|
|
50
|
+
providerId: 'gemini',
|
|
51
|
+
displayName: 'Gemini 1.5 Pro',
|
|
52
|
+
description: 'Stable model with 2M context window for long-document tasks.',
|
|
53
|
+
capabilities: ['chat', 'tool_use', 'vision_input', 'json_mode'],
|
|
54
|
+
contextWindowSize: 2097152,
|
|
55
|
+
outputTokenLimit: 8192,
|
|
56
|
+
pricePer1MTokensInput: 1.25,
|
|
57
|
+
pricePer1MTokensOutput: 5.00,
|
|
58
|
+
supportsStreaming: true,
|
|
59
|
+
status: 'active',
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Provider implementation
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
/**
|
|
66
|
+
* @class GeminiProvider
|
|
67
|
+
* @implements {IProvider}
|
|
68
|
+
*
|
|
69
|
+
* Provides native integration with Google's Gemini REST API.
|
|
70
|
+
*
|
|
71
|
+
* Handles the structural differences between Gemini's API and the OpenAI-style
|
|
72
|
+
* conventions used by IProvider: role mapping (`assistant` -> `model`), system
|
|
73
|
+
* instruction extraction, tool schema translation, and finish reason normalization.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* const provider = new GeminiProvider();
|
|
77
|
+
* await provider.initialize({ apiKey: 'AIzaSy...' });
|
|
78
|
+
* const response = await provider.generateCompletion(
|
|
79
|
+
* 'gemini-2.5-flash',
|
|
80
|
+
* [{ role: 'user', content: 'Hello!' }],
|
|
81
|
+
* { maxTokens: 1024 },
|
|
82
|
+
* );
|
|
83
|
+
*/
|
|
84
|
+
export class GeminiProvider {
|
|
85
|
+
constructor() {
|
|
86
|
+
/** @inheritdoc */
|
|
87
|
+
this.providerId = 'gemini';
|
|
88
|
+
/** @inheritdoc */
|
|
89
|
+
this.isInitialized = false;
|
|
90
|
+
}
|
|
91
|
+
// -------------------------------------------------------------------------
|
|
92
|
+
// Lifecycle
|
|
93
|
+
// -------------------------------------------------------------------------
|
|
94
|
+
/**
|
|
95
|
+
* Initialize the Gemini provider with the given configuration.
|
|
96
|
+
*
|
|
97
|
+
* Validates that an API key is present. Does NOT make a network call on
|
|
98
|
+
* startup — Gemini does not have a lightweight health/models endpoint
|
|
99
|
+
* that works without model-scoped paths.
|
|
100
|
+
*
|
|
101
|
+
* @param {GeminiProviderConfig} config - Provider configuration.
|
|
102
|
+
* @returns {Promise<void>}
|
|
103
|
+
* @throws {GeminiProviderError} If the API key is missing.
|
|
104
|
+
*/
|
|
105
|
+
async initialize(config) {
|
|
106
|
+
if (!config.apiKey) {
|
|
107
|
+
throw new GeminiProviderError('API key is required for GeminiProvider initialization. Set GEMINI_API_KEY.', 'INIT_FAILED_MISSING_API_KEY');
|
|
108
|
+
}
|
|
109
|
+
this.config = {
|
|
110
|
+
baseURL: 'https://generativelanguage.googleapis.com/v1beta',
|
|
111
|
+
maxRetries: 3,
|
|
112
|
+
requestTimeout: 60000,
|
|
113
|
+
defaultModelId: 'gemini-2.5-flash',
|
|
114
|
+
...config,
|
|
115
|
+
};
|
|
116
|
+
this.defaultModelId = this.config.defaultModelId;
|
|
117
|
+
this.isInitialized = true;
|
|
118
|
+
console.log(`GeminiProvider initialized. Default model: ${this.defaultModelId || 'Not set'}.`);
|
|
119
|
+
}
|
|
120
|
+
// -------------------------------------------------------------------------
|
|
121
|
+
// Chat completions (non-streaming)
|
|
122
|
+
// -------------------------------------------------------------------------
|
|
123
|
+
/**
|
|
124
|
+
* Generates a non-streaming chat completion via Gemini's generateContent endpoint.
|
|
125
|
+
*
|
|
126
|
+
* Extracts system messages and places them in the `systemInstruction` field,
|
|
127
|
+
* maps `assistant` role to `model`, converts tool definitions to Gemini's
|
|
128
|
+
* `functionDeclarations` format, and normalizes the response back to
|
|
129
|
+
* IProvider conventions.
|
|
130
|
+
*
|
|
131
|
+
* @param {string} modelId - The Gemini model to use (e.g., "gemini-2.5-flash").
|
|
132
|
+
* @param {ChatMessage[]} messages - Conversation messages. System-role messages are
|
|
133
|
+
* extracted and sent as the `systemInstruction` field.
|
|
134
|
+
* @param {ModelCompletionOptions} options - Completion options.
|
|
135
|
+
* @returns {Promise<ModelCompletionResponse>} A normalized completion response.
|
|
136
|
+
* @throws {GeminiProviderError} On authentication, validation, or network errors.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* const resp = await provider.generateCompletion('gemini-2.5-flash', [
|
|
140
|
+
* { role: 'system', content: 'You are a helpful assistant.' },
|
|
141
|
+
* { role: 'user', content: 'Explain quantum computing in one sentence.' },
|
|
142
|
+
* ], { maxTokens: 256 });
|
|
143
|
+
* console.log(resp.choices[0].message.content);
|
|
144
|
+
*/
|
|
145
|
+
async generateCompletion(modelId, messages, options) {
|
|
146
|
+
this.ensureInitialized();
|
|
147
|
+
const payload = this.buildRequestPayload(modelId, messages, options);
|
|
148
|
+
// Gemini uses model-scoped endpoints: /models/{model}:generateContent
|
|
149
|
+
const endpoint = `/models/${modelId}:generateContent`;
|
|
150
|
+
const apiResponse = await this.makeApiRequest(endpoint, payload);
|
|
151
|
+
return this.mapResponseToCompletion(apiResponse, modelId);
|
|
152
|
+
}
|
|
153
|
+
// -------------------------------------------------------------------------
|
|
154
|
+
// Chat completions (streaming)
|
|
155
|
+
// -------------------------------------------------------------------------
|
|
156
|
+
/**
|
|
157
|
+
* Generates a streaming chat completion via Gemini's streamGenerateContent endpoint.
|
|
158
|
+
*
|
|
159
|
+
* Gemini streaming uses SSE with `alt=sse` query parameter. Each SSE data line
|
|
160
|
+
* contains a JSON object with `candidates[].content.parts[].text` for text deltas
|
|
161
|
+
* and `candidates[].content.parts[].functionCall` for tool invocations.
|
|
162
|
+
*
|
|
163
|
+
* Normalizes all events into the IProvider streaming contract with
|
|
164
|
+
* `responseTextDelta`, `toolCallsDeltas`, and `isFinal`.
|
|
165
|
+
*
|
|
166
|
+
* @param {string} modelId - The Gemini model to use.
|
|
167
|
+
* @param {ChatMessage[]} messages - Conversation messages.
|
|
168
|
+
* @param {ModelCompletionOptions} options - Completion options.
|
|
169
|
+
* @returns {AsyncGenerator<ModelCompletionResponse>} Incremental response chunks.
|
|
170
|
+
* @throws {GeminiProviderError} On connection or stream errors.
|
|
171
|
+
*/
|
|
172
|
+
async *generateCompletionStream(modelId, messages, options) {
|
|
173
|
+
this.ensureInitialized();
|
|
174
|
+
const payload = this.buildRequestPayload(modelId, messages, options);
|
|
175
|
+
const responseId = `gemini-${modelId}-${Date.now()}`;
|
|
176
|
+
// Handle pre-aborted signals
|
|
177
|
+
const abortSignal = options.abortSignal;
|
|
178
|
+
if (abortSignal?.aborted) {
|
|
179
|
+
yield this.buildAbortChunk(modelId);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Streaming endpoint uses ?alt=sse and the API key query param
|
|
183
|
+
const endpoint = `/models/${modelId}:streamGenerateContent`;
|
|
184
|
+
const stream = await this.makeStreamRequest(endpoint, payload);
|
|
185
|
+
// Accumulators for building the complete response
|
|
186
|
+
let accumulatedContent = '';
|
|
187
|
+
let lastFinishReason = null;
|
|
188
|
+
let lastUsage;
|
|
189
|
+
/** Map from part index -> tool call accumulator */
|
|
190
|
+
const toolCallAccum = new Map();
|
|
191
|
+
let toolCallIndex = 0;
|
|
192
|
+
const abortHandler = () => { };
|
|
193
|
+
abortSignal?.addEventListener('abort', abortHandler, { once: true });
|
|
194
|
+
try {
|
|
195
|
+
for await (const rawData of this.parseSseStream(stream)) {
|
|
196
|
+
if (abortSignal?.aborted) {
|
|
197
|
+
yield this.buildAbortChunk(modelId);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
let chunk;
|
|
201
|
+
try {
|
|
202
|
+
chunk = JSON.parse(rawData);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Malformed JSON — skip
|
|
206
|
+
console.warn('GeminiProvider: Could not parse SSE event JSON:', rawData);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
// Handle API-level errors in the stream
|
|
210
|
+
if (chunk.error) {
|
|
211
|
+
yield {
|
|
212
|
+
id: responseId,
|
|
213
|
+
object: 'chat.completion.chunk',
|
|
214
|
+
created: Math.floor(Date.now() / 1000),
|
|
215
|
+
modelId,
|
|
216
|
+
choices: [],
|
|
217
|
+
error: {
|
|
218
|
+
message: chunk.error.message,
|
|
219
|
+
type: chunk.error.status,
|
|
220
|
+
code: chunk.error.code,
|
|
221
|
+
},
|
|
222
|
+
isFinal: true,
|
|
223
|
+
};
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
// Track usage from every chunk — the last one will have final totals
|
|
227
|
+
if (chunk.usageMetadata) {
|
|
228
|
+
lastUsage = chunk.usageMetadata;
|
|
229
|
+
}
|
|
230
|
+
const candidate = chunk.candidates?.[0];
|
|
231
|
+
if (!candidate)
|
|
232
|
+
continue;
|
|
233
|
+
if (candidate.finishReason) {
|
|
234
|
+
lastFinishReason = candidate.finishReason;
|
|
235
|
+
}
|
|
236
|
+
const parts = candidate.content?.parts ?? [];
|
|
237
|
+
for (const part of parts) {
|
|
238
|
+
if (part.text !== undefined) {
|
|
239
|
+
// Text delta
|
|
240
|
+
accumulatedContent += part.text;
|
|
241
|
+
yield {
|
|
242
|
+
id: responseId,
|
|
243
|
+
object: 'chat.completion.chunk',
|
|
244
|
+
created: Math.floor(Date.now() / 1000),
|
|
245
|
+
modelId,
|
|
246
|
+
choices: [{
|
|
247
|
+
index: 0,
|
|
248
|
+
message: { role: 'assistant', content: part.text },
|
|
249
|
+
finishReason: null,
|
|
250
|
+
}],
|
|
251
|
+
responseTextDelta: part.text,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
else if (part.functionCall) {
|
|
255
|
+
// Tool call — Gemini delivers function calls as complete objects,
|
|
256
|
+
// not incremental deltas. We emit them as a single delta per call.
|
|
257
|
+
const idx = toolCallIndex++;
|
|
258
|
+
toolCallAccum.set(idx, {
|
|
259
|
+
name: part.functionCall.name,
|
|
260
|
+
args: part.functionCall.args,
|
|
261
|
+
});
|
|
262
|
+
yield {
|
|
263
|
+
id: responseId,
|
|
264
|
+
object: 'chat.completion.chunk',
|
|
265
|
+
created: Math.floor(Date.now() / 1000),
|
|
266
|
+
modelId,
|
|
267
|
+
choices: [{
|
|
268
|
+
index: 0,
|
|
269
|
+
message: { role: 'assistant', content: null },
|
|
270
|
+
finishReason: null,
|
|
271
|
+
}],
|
|
272
|
+
toolCallsDeltas: [{
|
|
273
|
+
index: idx,
|
|
274
|
+
id: `call_gemini_${Date.now()}_${idx}`,
|
|
275
|
+
type: 'function',
|
|
276
|
+
function: {
|
|
277
|
+
name: part.functionCall.name,
|
|
278
|
+
// Gemini delivers complete args, so emit them as a single delta
|
|
279
|
+
arguments_delta: JSON.stringify(part.functionCall.args),
|
|
280
|
+
},
|
|
281
|
+
}],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Emit final chunk with usage and finish reason
|
|
287
|
+
const toolCalls = this.assembleToolCalls(toolCallAccum);
|
|
288
|
+
const hasToolCalls = toolCalls.length > 0;
|
|
289
|
+
const usage = this.mapUsage(lastUsage, modelId);
|
|
290
|
+
yield {
|
|
291
|
+
id: responseId,
|
|
292
|
+
object: 'chat.completion.chunk',
|
|
293
|
+
created: Math.floor(Date.now() / 1000),
|
|
294
|
+
modelId,
|
|
295
|
+
choices: [{
|
|
296
|
+
index: 0,
|
|
297
|
+
message: {
|
|
298
|
+
role: 'assistant',
|
|
299
|
+
content: accumulatedContent || null,
|
|
300
|
+
...(hasToolCalls && { tool_calls: toolCalls }),
|
|
301
|
+
},
|
|
302
|
+
finishReason: this.mapFinishReason(lastFinishReason),
|
|
303
|
+
}],
|
|
304
|
+
usage,
|
|
305
|
+
isFinal: true,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
catch (streamError) {
|
|
309
|
+
const message = streamError instanceof Error
|
|
310
|
+
? streamError.message
|
|
311
|
+
: 'Gemini stream processing error';
|
|
312
|
+
console.error(`GeminiProvider stream error for model ${modelId}:`, message);
|
|
313
|
+
yield {
|
|
314
|
+
id: responseId,
|
|
315
|
+
object: 'chat.completion.chunk',
|
|
316
|
+
created: Math.floor(Date.now() / 1000),
|
|
317
|
+
modelId,
|
|
318
|
+
choices: [],
|
|
319
|
+
isFinal: true,
|
|
320
|
+
error: { message, type: 'STREAM_PROCESSING_ERROR' },
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
abortSignal?.removeEventListener('abort', abortHandler);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// -------------------------------------------------------------------------
|
|
328
|
+
// Embeddings
|
|
329
|
+
// -------------------------------------------------------------------------
|
|
330
|
+
/**
|
|
331
|
+
* Generates embeddings using Gemini's embedding models.
|
|
332
|
+
*
|
|
333
|
+
* Uses the `models/{model}:embedContent` endpoint. Currently Gemini
|
|
334
|
+
* supports embedding one text at a time, so we batch sequentially.
|
|
335
|
+
*
|
|
336
|
+
* @param {string} modelId - Embedding model (e.g., "text-embedding-004").
|
|
337
|
+
* @param {string[]} texts - Input texts to embed.
|
|
338
|
+
* @param {ProviderEmbeddingOptions} [options] - Optional embedding parameters.
|
|
339
|
+
* @returns {Promise<ProviderEmbeddingResponse>} Embedding vectors.
|
|
340
|
+
* @throws {GeminiProviderError} On API errors.
|
|
341
|
+
*/
|
|
342
|
+
async generateEmbeddings(modelId, texts, options) {
|
|
343
|
+
this.ensureInitialized();
|
|
344
|
+
// Gemini's batch embedding endpoint
|
|
345
|
+
const endpoint = `/models/${modelId}:batchEmbedContents`;
|
|
346
|
+
const requests = texts.map(text => ({
|
|
347
|
+
model: `models/${modelId}`,
|
|
348
|
+
content: { parts: [{ text }] },
|
|
349
|
+
}));
|
|
350
|
+
const apiResponse = await this.makeApiRequest(endpoint, { requests });
|
|
351
|
+
return {
|
|
352
|
+
object: 'list',
|
|
353
|
+
data: apiResponse.embeddings.map((emb, index) => ({
|
|
354
|
+
object: 'embedding',
|
|
355
|
+
embedding: emb.values,
|
|
356
|
+
index,
|
|
357
|
+
})),
|
|
358
|
+
model: modelId,
|
|
359
|
+
usage: {
|
|
360
|
+
prompt_tokens: 0, // Gemini does not report embedding token counts
|
|
361
|
+
total_tokens: 0,
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// -------------------------------------------------------------------------
|
|
366
|
+
// Introspection
|
|
367
|
+
// -------------------------------------------------------------------------
|
|
368
|
+
/**
|
|
369
|
+
* Returns a static catalog of known Gemini models.
|
|
370
|
+
*
|
|
371
|
+
* Uses a hardcoded catalog kept up-to-date with major releases, since
|
|
372
|
+
* the Gemini models list endpoint requires iterating over all models.
|
|
373
|
+
*
|
|
374
|
+
* @param {{ capability?: string }} [filter] - Optional capability filter.
|
|
375
|
+
* @returns {Promise<ModelInfo[]>} Array of known Gemini models.
|
|
376
|
+
*/
|
|
377
|
+
async listAvailableModels(filter) {
|
|
378
|
+
this.ensureInitialized();
|
|
379
|
+
if (filter?.capability) {
|
|
380
|
+
return GEMINI_MODELS.filter(m => m.capabilities.includes(filter.capability));
|
|
381
|
+
}
|
|
382
|
+
return [...GEMINI_MODELS];
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Retrieves metadata for a specific Gemini model from the static catalog.
|
|
386
|
+
*
|
|
387
|
+
* @param {string} modelId - Model identifier (e.g., "gemini-2.5-flash").
|
|
388
|
+
* @returns {Promise<ModelInfo | undefined>} Model info or undefined if not found.
|
|
389
|
+
*/
|
|
390
|
+
async getModelInfo(modelId) {
|
|
391
|
+
this.ensureInitialized();
|
|
392
|
+
return GEMINI_MODELS.find(m => m.modelId === modelId);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Performs a lightweight health check by sending a minimal generateContent request.
|
|
396
|
+
*
|
|
397
|
+
* @returns {Promise<{ isHealthy: boolean; details?: unknown }>} Health status.
|
|
398
|
+
*/
|
|
399
|
+
async checkHealth() {
|
|
400
|
+
try {
|
|
401
|
+
const model = this.defaultModelId || 'gemini-2.5-flash';
|
|
402
|
+
await this.makeApiRequest(`/models/${model}:generateContent`, {
|
|
403
|
+
contents: [{ role: 'user', parts: [{ text: 'ping' }] }],
|
|
404
|
+
generationConfig: { maxOutputTokens: 1 },
|
|
405
|
+
});
|
|
406
|
+
return { isHealthy: true };
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
const message = error instanceof Error ? error.message : 'Health check failed';
|
|
410
|
+
return { isHealthy: false, details: { message, error } };
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/** @inheritdoc */
|
|
414
|
+
async shutdown() {
|
|
415
|
+
this.isInitialized = false;
|
|
416
|
+
console.log('GeminiProvider shutdown complete.');
|
|
417
|
+
}
|
|
418
|
+
// =========================================================================
|
|
419
|
+
// Private helpers
|
|
420
|
+
// =========================================================================
|
|
421
|
+
/**
|
|
422
|
+
* Guard that throws if the provider has not been initialized.
|
|
423
|
+
*
|
|
424
|
+
* @private
|
|
425
|
+
* @throws {GeminiProviderError} If not initialized.
|
|
426
|
+
*/
|
|
427
|
+
ensureInitialized() {
|
|
428
|
+
if (!this.isInitialized) {
|
|
429
|
+
throw new GeminiProviderError('GeminiProvider is not initialized. Call initialize() first.', 'PROVIDER_NOT_INITIALIZED');
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// -------------------------------------------------------------------------
|
|
433
|
+
// Payload construction
|
|
434
|
+
// -------------------------------------------------------------------------
|
|
435
|
+
/**
|
|
436
|
+
* Builds the Gemini API request payload from IProvider inputs.
|
|
437
|
+
*
|
|
438
|
+
* The key transformations are:
|
|
439
|
+
* 1. System messages extracted to `systemInstruction` (Gemini has no system role).
|
|
440
|
+
* 2. `assistant` role mapped to `model` (Gemini's convention).
|
|
441
|
+
* 3. Tool messages mapped to `functionResponse` parts within user turns.
|
|
442
|
+
* 4. OpenAI-style tool definitions converted to `functionDeclarations`.
|
|
443
|
+
*
|
|
444
|
+
* @param {string} _modelId - Target model (used for endpoint, not in body).
|
|
445
|
+
* @param {ChatMessage[]} messages - Conversation messages.
|
|
446
|
+
* @param {ModelCompletionOptions} options - Completion options.
|
|
447
|
+
* @returns {Record<string, unknown>} The request body for Gemini's API.
|
|
448
|
+
* @private
|
|
449
|
+
*/
|
|
450
|
+
buildRequestPayload(_modelId, messages, options) {
|
|
451
|
+
// --- Extract system messages into systemInstruction ---
|
|
452
|
+
// Gemini treats system instructions as a separate top-level field,
|
|
453
|
+
// similar to Anthropic but with a `parts` array wrapper.
|
|
454
|
+
const systemParts = [];
|
|
455
|
+
const conversationMessages = [];
|
|
456
|
+
for (const msg of messages) {
|
|
457
|
+
if (msg.role === 'system') {
|
|
458
|
+
const text = typeof msg.content === 'string'
|
|
459
|
+
? msg.content
|
|
460
|
+
: Array.isArray(msg.content)
|
|
461
|
+
? msg.content.filter(p => p.type === 'text').map(p => p.text).join('\n')
|
|
462
|
+
: '';
|
|
463
|
+
if (text)
|
|
464
|
+
systemParts.push(text);
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
conversationMessages.push(msg);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
// --- Convert messages to Gemini content format ---
|
|
471
|
+
const contents = this.convertMessages(conversationMessages);
|
|
472
|
+
const payload = {
|
|
473
|
+
contents,
|
|
474
|
+
};
|
|
475
|
+
// Include systemInstruction only if there's system content
|
|
476
|
+
if (systemParts.length > 0) {
|
|
477
|
+
payload.systemInstruction = {
|
|
478
|
+
parts: [{ text: systemParts.join('\n\n') }],
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
// --- Generation config ---
|
|
482
|
+
const generationConfig = {};
|
|
483
|
+
if (options.temperature !== undefined)
|
|
484
|
+
generationConfig.temperature = options.temperature;
|
|
485
|
+
if (options.maxTokens !== undefined)
|
|
486
|
+
generationConfig.maxOutputTokens = options.maxTokens;
|
|
487
|
+
if (options.topP !== undefined)
|
|
488
|
+
generationConfig.topP = options.topP;
|
|
489
|
+
if (options.stopSequences?.length)
|
|
490
|
+
generationConfig.stopSequences = options.stopSequences;
|
|
491
|
+
// JSON mode: Gemini uses responseMimeType to enforce JSON output
|
|
492
|
+
if (options.responseFormat?.type === 'json_object') {
|
|
493
|
+
generationConfig.responseMimeType = 'application/json';
|
|
494
|
+
}
|
|
495
|
+
// topK support via customModelParams
|
|
496
|
+
if (options.customModelParams?.topK !== undefined) {
|
|
497
|
+
generationConfig.topK = options.customModelParams.topK;
|
|
498
|
+
}
|
|
499
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
500
|
+
payload.generationConfig = generationConfig;
|
|
501
|
+
}
|
|
502
|
+
// --- Tool definitions ---
|
|
503
|
+
const tools = this.convertToolDefs(options.tools);
|
|
504
|
+
if (tools.length > 0) {
|
|
505
|
+
payload.tools = [{ functionDeclarations: tools }];
|
|
506
|
+
}
|
|
507
|
+
// Pass through custom model params (excluding ones we already handle)
|
|
508
|
+
if (options.customModelParams) {
|
|
509
|
+
const { topK, ...rest } = options.customModelParams;
|
|
510
|
+
if (Object.keys(rest).length > 0) {
|
|
511
|
+
Object.assign(payload, rest);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return payload;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Converts an array of ChatMessages to Gemini's content format.
|
|
518
|
+
*
|
|
519
|
+
* Maps IProvider roles to Gemini roles:
|
|
520
|
+
* - `user` -> `user`
|
|
521
|
+
* - `assistant` -> `model` (Gemini uses "model" instead of "assistant")
|
|
522
|
+
* - `tool` -> `user` with `functionResponse` parts
|
|
523
|
+
*
|
|
524
|
+
* @param {ChatMessage[]} messages - IProvider-format messages.
|
|
525
|
+
* @returns {GeminiContent[]} Gemini-format content array.
|
|
526
|
+
* @private
|
|
527
|
+
*/
|
|
528
|
+
convertMessages(messages) {
|
|
529
|
+
const contents = [];
|
|
530
|
+
for (const msg of messages) {
|
|
531
|
+
if (msg.role === 'assistant') {
|
|
532
|
+
// --- Assistant messages map to "model" role ---
|
|
533
|
+
const parts = [];
|
|
534
|
+
// Add text content if present
|
|
535
|
+
if (typeof msg.content === 'string' && msg.content) {
|
|
536
|
+
parts.push({ text: msg.content });
|
|
537
|
+
}
|
|
538
|
+
// Convert tool_calls to functionCall parts
|
|
539
|
+
if (msg.tool_calls?.length) {
|
|
540
|
+
for (const tc of msg.tool_calls) {
|
|
541
|
+
let parsedArgs;
|
|
542
|
+
try {
|
|
543
|
+
parsedArgs = typeof tc.function.arguments === 'string'
|
|
544
|
+
? JSON.parse(tc.function.arguments)
|
|
545
|
+
: tc.function.arguments ?? {};
|
|
546
|
+
}
|
|
547
|
+
catch {
|
|
548
|
+
parsedArgs = {};
|
|
549
|
+
}
|
|
550
|
+
parts.push({
|
|
551
|
+
functionCall: { name: tc.function.name, args: parsedArgs },
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
// Ensure at least one part — Gemini requires non-empty parts
|
|
556
|
+
if (parts.length === 0) {
|
|
557
|
+
parts.push({ text: '' });
|
|
558
|
+
}
|
|
559
|
+
contents.push({ role: 'model', parts });
|
|
560
|
+
}
|
|
561
|
+
else if (msg.role === 'tool') {
|
|
562
|
+
// --- Tool result messages become user-role functionResponse ---
|
|
563
|
+
// Gemini expects tool results as functionResponse parts in a user turn.
|
|
564
|
+
let responseData;
|
|
565
|
+
try {
|
|
566
|
+
const raw = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content ?? '');
|
|
567
|
+
responseData = typeof msg.content === 'string'
|
|
568
|
+
? JSON.parse(raw)
|
|
569
|
+
: { result: raw };
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
// If the tool result isn't valid JSON, wrap it
|
|
573
|
+
responseData = { result: typeof msg.content === 'string' ? msg.content : String(msg.content) };
|
|
574
|
+
}
|
|
575
|
+
contents.push({
|
|
576
|
+
role: 'user',
|
|
577
|
+
parts: [{
|
|
578
|
+
functionResponse: {
|
|
579
|
+
name: msg.name || 'unknown',
|
|
580
|
+
response: responseData,
|
|
581
|
+
},
|
|
582
|
+
}],
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
// --- User messages ---
|
|
587
|
+
const parts = [];
|
|
588
|
+
if (typeof msg.content === 'string') {
|
|
589
|
+
parts.push({ text: msg.content });
|
|
590
|
+
}
|
|
591
|
+
else if (Array.isArray(msg.content)) {
|
|
592
|
+
// Multimodal content — extract text parts
|
|
593
|
+
for (const part of msg.content) {
|
|
594
|
+
if (part.type === 'text') {
|
|
595
|
+
parts.push({ text: part.text });
|
|
596
|
+
}
|
|
597
|
+
// Image support could be added here via inlineData parts
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (parts.length === 0) {
|
|
601
|
+
parts.push({ text: '' });
|
|
602
|
+
}
|
|
603
|
+
contents.push({ role: 'user', parts });
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return contents;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Converts OpenAI-style tool definitions to Gemini's functionDeclarations format.
|
|
610
|
+
*
|
|
611
|
+
* OpenAI uses `{ type: 'function', function: { name, description, parameters } }`
|
|
612
|
+
* while Gemini uses `{ name, description, parameters }` inside a `functionDeclarations` array.
|
|
613
|
+
*
|
|
614
|
+
* @param {Array<Record<string, unknown>>} [tools] - OpenAI-formatted tool defs.
|
|
615
|
+
* @returns {GeminiFunctionDeclaration[]} Gemini-formatted function declarations.
|
|
616
|
+
* @private
|
|
617
|
+
*/
|
|
618
|
+
convertToolDefs(tools) {
|
|
619
|
+
if (!tools || tools.length === 0)
|
|
620
|
+
return [];
|
|
621
|
+
return tools.map(tool => {
|
|
622
|
+
// OpenAI format: { type: 'function', function: { name, description, parameters } }
|
|
623
|
+
const fn = tool?.function;
|
|
624
|
+
if (fn?.name) {
|
|
625
|
+
return {
|
|
626
|
+
name: fn.name,
|
|
627
|
+
description: (fn.description ?? ''),
|
|
628
|
+
// Gemini uses the same "parameters" field name as OpenAI, unlike Anthropic's input_schema
|
|
629
|
+
parameters: fn.parameters,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
// AgentOS ITool format: { name, description, inputSchema }
|
|
633
|
+
return {
|
|
634
|
+
name: tool.name ?? 'unknown',
|
|
635
|
+
description: tool.description ?? '',
|
|
636
|
+
parameters: tool.inputSchema ?? tool.parameters,
|
|
637
|
+
};
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
// -------------------------------------------------------------------------
|
|
641
|
+
// Response mapping
|
|
642
|
+
// -------------------------------------------------------------------------
|
|
643
|
+
/**
|
|
644
|
+
* Maps a non-streaming Gemini response to IProvider format.
|
|
645
|
+
*
|
|
646
|
+
* Extracts text from `candidates[0].content.parts`, converts `functionCall`
|
|
647
|
+
* parts to OpenAI-style `tool_calls`, and normalizes usage metadata.
|
|
648
|
+
*
|
|
649
|
+
* @param {GeminiResponse} apiResponse - Raw Gemini API response.
|
|
650
|
+
* @param {string} modelId - The model ID used for the request.
|
|
651
|
+
* @returns {ModelCompletionResponse} Normalized completion response.
|
|
652
|
+
* @private
|
|
653
|
+
*/
|
|
654
|
+
mapResponseToCompletion(apiResponse, modelId) {
|
|
655
|
+
const candidate = apiResponse.candidates?.[0];
|
|
656
|
+
const parts = candidate?.content?.parts ?? [];
|
|
657
|
+
// Collect text from all text parts
|
|
658
|
+
const textParts = parts
|
|
659
|
+
.filter(p => p.text !== undefined)
|
|
660
|
+
.map(p => p.text);
|
|
661
|
+
const fullText = textParts.join('');
|
|
662
|
+
// Collect function calls and convert to OpenAI-style tool_calls
|
|
663
|
+
const toolCalls = parts
|
|
664
|
+
.filter(p => p.functionCall)
|
|
665
|
+
.map((p, idx) => ({
|
|
666
|
+
id: `call_gemini_${Date.now()}_${idx}`,
|
|
667
|
+
type: 'function',
|
|
668
|
+
function: {
|
|
669
|
+
name: p.functionCall.name,
|
|
670
|
+
arguments: JSON.stringify(p.functionCall.args ?? {}),
|
|
671
|
+
},
|
|
672
|
+
}));
|
|
673
|
+
const hasToolCalls = toolCalls.length > 0;
|
|
674
|
+
const finishReason = this.mapFinishReason(candidate?.finishReason ?? null);
|
|
675
|
+
const usage = this.mapUsage(apiResponse.usageMetadata, modelId);
|
|
676
|
+
const choice = {
|
|
677
|
+
index: 0,
|
|
678
|
+
message: {
|
|
679
|
+
role: 'assistant',
|
|
680
|
+
content: fullText || null,
|
|
681
|
+
...(hasToolCalls && { tool_calls: toolCalls }),
|
|
682
|
+
},
|
|
683
|
+
finishReason,
|
|
684
|
+
};
|
|
685
|
+
return {
|
|
686
|
+
id: `gemini-${modelId}-${Date.now()}`,
|
|
687
|
+
object: 'chat.completion',
|
|
688
|
+
created: Math.floor(Date.now() / 1000),
|
|
689
|
+
modelId,
|
|
690
|
+
choices: [choice],
|
|
691
|
+
usage,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Maps Gemini finish reasons to IProvider-convention finish reasons.
|
|
696
|
+
*
|
|
697
|
+
* Gemini uses uppercase enum strings:
|
|
698
|
+
* - `STOP` -> `"stop"` (natural completion)
|
|
699
|
+
* - `MAX_TOKENS` -> `"length"` (hit token limit)
|
|
700
|
+
* - `SAFETY` -> `"content_filter"` (blocked by safety filters)
|
|
701
|
+
* - `RECITATION` -> `"content_filter"` (blocked by recitation check)
|
|
702
|
+
*
|
|
703
|
+
* @param {string | null} finishReason - Gemini's finish reason value.
|
|
704
|
+
* @returns {string} Normalized finish reason.
|
|
705
|
+
* @private
|
|
706
|
+
*/
|
|
707
|
+
mapFinishReason(finishReason) {
|
|
708
|
+
switch (finishReason) {
|
|
709
|
+
case 'STOP': return 'stop';
|
|
710
|
+
case 'MAX_TOKENS': return 'length';
|
|
711
|
+
case 'SAFETY': return 'content_filter';
|
|
712
|
+
case 'RECITATION': return 'content_filter';
|
|
713
|
+
default: return finishReason?.toLowerCase() ?? 'stop';
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Maps Gemini usage metadata to IProvider's ModelUsage format.
|
|
718
|
+
*
|
|
719
|
+
* @param {GeminiUsageMetadata} [meta] - Gemini usage metadata.
|
|
720
|
+
* @param {string} modelId - Model ID for cost estimation.
|
|
721
|
+
* @returns {ModelUsage} Normalized usage metrics.
|
|
722
|
+
* @private
|
|
723
|
+
*/
|
|
724
|
+
mapUsage(meta, modelId) {
|
|
725
|
+
const promptTokens = meta?.promptTokenCount ?? 0;
|
|
726
|
+
const completionTokens = meta?.candidatesTokenCount ?? 0;
|
|
727
|
+
const totalTokens = meta?.totalTokenCount ?? (promptTokens + completionTokens);
|
|
728
|
+
return {
|
|
729
|
+
promptTokens,
|
|
730
|
+
completionTokens,
|
|
731
|
+
totalTokens,
|
|
732
|
+
costUSD: this.estimateCost(promptTokens, completionTokens, modelId),
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Assembles completed tool calls from the streaming accumulator.
|
|
737
|
+
*
|
|
738
|
+
* @param {Map<number, { name: string; args: Record<string, unknown> }>} accum - Accumulated tool calls.
|
|
739
|
+
* @returns {NonNullable<ChatMessage['tool_calls']>} OpenAI-style tool_calls array.
|
|
740
|
+
* @private
|
|
741
|
+
*/
|
|
742
|
+
assembleToolCalls(accum) {
|
|
743
|
+
if (accum.size === 0)
|
|
744
|
+
return [];
|
|
745
|
+
return Array.from(accum.entries()).map(([idx, tc]) => ({
|
|
746
|
+
id: `call_gemini_${Date.now()}_${idx}`,
|
|
747
|
+
type: 'function',
|
|
748
|
+
function: {
|
|
749
|
+
name: tc.name,
|
|
750
|
+
arguments: JSON.stringify(tc.args ?? {}),
|
|
751
|
+
},
|
|
752
|
+
}));
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Estimates USD cost for a given model and token counts.
|
|
756
|
+
*
|
|
757
|
+
* Looks up pricing from the static model catalog. Returns undefined
|
|
758
|
+
* if the model is not found in the catalog.
|
|
759
|
+
*
|
|
760
|
+
* @param {number} inputTokens - Number of input tokens.
|
|
761
|
+
* @param {number} outputTokens - Number of output tokens.
|
|
762
|
+
* @param {string} modelId - Model identifier for pricing lookup.
|
|
763
|
+
* @returns {number | undefined} Estimated cost in USD.
|
|
764
|
+
* @private
|
|
765
|
+
*/
|
|
766
|
+
estimateCost(inputTokens, outputTokens, modelId) {
|
|
767
|
+
const info = GEMINI_MODELS.find(m => m.modelId === modelId);
|
|
768
|
+
if (!info?.pricePer1MTokensInput || !info?.pricePer1MTokensOutput)
|
|
769
|
+
return undefined;
|
|
770
|
+
return ((inputTokens / 1000000) * info.pricePer1MTokensInput +
|
|
771
|
+
(outputTokens / 1000000) * info.pricePer1MTokensOutput);
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Builds an abort chunk for early stream termination.
|
|
775
|
+
*
|
|
776
|
+
* @param {string} modelId - The model ID for the response.
|
|
777
|
+
* @returns {ModelCompletionResponse} A terminal chunk with abort error.
|
|
778
|
+
* @private
|
|
779
|
+
*/
|
|
780
|
+
buildAbortChunk(modelId) {
|
|
781
|
+
return {
|
|
782
|
+
id: `gemini-abort-${Date.now()}`,
|
|
783
|
+
object: 'chat.completion.chunk',
|
|
784
|
+
created: Math.floor(Date.now() / 1000),
|
|
785
|
+
modelId,
|
|
786
|
+
choices: [],
|
|
787
|
+
error: { message: 'Stream aborted by caller', type: 'abort' },
|
|
788
|
+
isFinal: true,
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
// -------------------------------------------------------------------------
|
|
792
|
+
// HTTP transport
|
|
793
|
+
// -------------------------------------------------------------------------
|
|
794
|
+
/**
|
|
795
|
+
* Makes a non-streaming API request to the Gemini API with retry logic.
|
|
796
|
+
*
|
|
797
|
+
* Authentication uses a `?key=` query parameter (Gemini's auth mechanism),
|
|
798
|
+
* NOT a header-based approach like OpenAI or Anthropic.
|
|
799
|
+
*
|
|
800
|
+
* @template T The expected response type.
|
|
801
|
+
* @param {string} endpoint - API endpoint path (e.g., "/models/gemini-2.5-flash:generateContent").
|
|
802
|
+
* @param {Record<string, unknown>} body - Request body.
|
|
803
|
+
* @returns {Promise<T>} Parsed JSON response.
|
|
804
|
+
* @throws {GeminiProviderError} On authentication, validation, rate-limit, or network errors.
|
|
805
|
+
* @private
|
|
806
|
+
*/
|
|
807
|
+
async makeApiRequest(endpoint, body) {
|
|
808
|
+
// API key is passed as query parameter — Gemini's auth convention
|
|
809
|
+
const url = `${this.config.baseURL}${endpoint}?key=${this.config.apiKey}`;
|
|
810
|
+
const headers = {
|
|
811
|
+
'Content-Type': 'application/json',
|
|
812
|
+
'User-Agent': 'AgentOS/1.0 (GeminiProvider)',
|
|
813
|
+
};
|
|
814
|
+
let lastError = new GeminiProviderError('Request failed after all retries.', 'MAX_RETRIES_REACHED');
|
|
815
|
+
for (let attempt = 0; attempt < this.config.maxRetries; attempt++) {
|
|
816
|
+
const controller = new AbortController();
|
|
817
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.requestTimeout);
|
|
818
|
+
try {
|
|
819
|
+
const response = await fetch(url, {
|
|
820
|
+
method: 'POST',
|
|
821
|
+
headers,
|
|
822
|
+
body: JSON.stringify(body),
|
|
823
|
+
signal: controller.signal,
|
|
824
|
+
});
|
|
825
|
+
clearTimeout(timeoutId);
|
|
826
|
+
if (!response.ok) {
|
|
827
|
+
const errorData = await response.json().catch(() => ({}));
|
|
828
|
+
const errorMessage = errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`;
|
|
829
|
+
const errorStatus = errorData.error?.status;
|
|
830
|
+
// Non-retryable client errors (auth, bad request, not found)
|
|
831
|
+
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 404) {
|
|
832
|
+
throw new GeminiProviderError(errorMessage, 'API_CLIENT_ERROR', response.status, errorStatus, errorData);
|
|
833
|
+
}
|
|
834
|
+
// Rate limit — respect Retry-After header
|
|
835
|
+
if (response.status === 429) {
|
|
836
|
+
lastError = new GeminiProviderError(errorMessage, 'RATE_LIMIT_EXCEEDED', 429, errorStatus, errorData);
|
|
837
|
+
const retryAfter = response.headers.get('retry-after');
|
|
838
|
+
const retryAfterMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : (2 ** attempt) * 1000;
|
|
839
|
+
await new Promise(resolve => setTimeout(resolve, retryAfterMs));
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
// Retryable server errors (5xx)
|
|
843
|
+
if (response.status >= 500) {
|
|
844
|
+
lastError = new GeminiProviderError(errorMessage, 'API_SERVER_ERROR', response.status, errorStatus, errorData);
|
|
845
|
+
await new Promise(resolve => setTimeout(resolve, (2 ** attempt) * 1000));
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
throw new GeminiProviderError(errorMessage, 'API_REQUEST_FAILED', response.status, errorStatus, errorData);
|
|
849
|
+
}
|
|
850
|
+
return (await response.json());
|
|
851
|
+
}
|
|
852
|
+
catch (error) {
|
|
853
|
+
clearTimeout(timeoutId);
|
|
854
|
+
if (error instanceof GeminiProviderError) {
|
|
855
|
+
if (error.code === 'API_CLIENT_ERROR')
|
|
856
|
+
throw error;
|
|
857
|
+
lastError = error;
|
|
858
|
+
}
|
|
859
|
+
else if (error instanceof Error && error.name === 'AbortError') {
|
|
860
|
+
lastError = new GeminiProviderError(`Request timed out after ${this.config.requestTimeout}ms.`, 'REQUEST_TIMEOUT');
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
lastError = new GeminiProviderError(error instanceof Error ? error.message : 'Network or unknown error', 'NETWORK_ERROR');
|
|
864
|
+
}
|
|
865
|
+
if (attempt === this.config.maxRetries - 1)
|
|
866
|
+
break;
|
|
867
|
+
const delay = Math.min(30000, (1000 * (2 ** attempt)) + Math.random() * 1000);
|
|
868
|
+
console.warn(`[GeminiProvider] Retry ${attempt + 1}/${this.config.maxRetries - 1} in ${(delay / 1000).toFixed(1)}s`);
|
|
869
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
throw lastError;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Makes a streaming API request and returns the raw ReadableStream.
|
|
876
|
+
*
|
|
877
|
+
* Uses the `?alt=sse` query parameter to enable SSE streaming,
|
|
878
|
+
* combined with the `?key=` query parameter for authentication.
|
|
879
|
+
*
|
|
880
|
+
* @param {string} endpoint - API endpoint (e.g., "/models/gemini-2.5-flash:streamGenerateContent").
|
|
881
|
+
* @param {Record<string, unknown>} body - Request body.
|
|
882
|
+
* @returns {Promise<ReadableStream<Uint8Array>>} The response body stream.
|
|
883
|
+
* @throws {GeminiProviderError} On connection errors.
|
|
884
|
+
* @private
|
|
885
|
+
*/
|
|
886
|
+
async makeStreamRequest(endpoint, body) {
|
|
887
|
+
// Both alt=sse and key= are query params
|
|
888
|
+
const url = `${this.config.baseURL}${endpoint}?alt=sse&key=${this.config.apiKey}`;
|
|
889
|
+
const headers = {
|
|
890
|
+
'Content-Type': 'application/json',
|
|
891
|
+
'User-Agent': 'AgentOS/1.0 (GeminiProvider)',
|
|
892
|
+
};
|
|
893
|
+
const controller = new AbortController();
|
|
894
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.requestTimeout);
|
|
895
|
+
try {
|
|
896
|
+
const response = await fetch(url, {
|
|
897
|
+
method: 'POST',
|
|
898
|
+
headers,
|
|
899
|
+
body: JSON.stringify(body),
|
|
900
|
+
signal: controller.signal,
|
|
901
|
+
});
|
|
902
|
+
clearTimeout(timeoutId);
|
|
903
|
+
if (!response.ok) {
|
|
904
|
+
const errorData = await response.json().catch(() => ({}));
|
|
905
|
+
const errorMessage = errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`;
|
|
906
|
+
throw new GeminiProviderError(errorMessage, 'STREAM_CONNECTION_FAILED', response.status, errorData.error?.status, errorData);
|
|
907
|
+
}
|
|
908
|
+
if (!response.body) {
|
|
909
|
+
throw new GeminiProviderError('Expected a stream response but body was null.', 'STREAM_BODY_NULL');
|
|
910
|
+
}
|
|
911
|
+
return response.body;
|
|
912
|
+
}
|
|
913
|
+
catch (error) {
|
|
914
|
+
clearTimeout(timeoutId);
|
|
915
|
+
if (error instanceof GeminiProviderError)
|
|
916
|
+
throw error;
|
|
917
|
+
throw new GeminiProviderError(error instanceof Error ? error.message : 'Failed to connect to Gemini stream.', 'STREAM_CONNECTION_FAILED');
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
// -------------------------------------------------------------------------
|
|
921
|
+
// SSE parsing
|
|
922
|
+
// -------------------------------------------------------------------------
|
|
923
|
+
/**
|
|
924
|
+
* Parses an SSE (Server-Sent Events) stream from Gemini.
|
|
925
|
+
*
|
|
926
|
+
* Gemini SSE events follow the standard format:
|
|
927
|
+
* ```
|
|
928
|
+
* data: <json_payload>
|
|
929
|
+
*
|
|
930
|
+
* data: <json_payload>
|
|
931
|
+
* ```
|
|
932
|
+
*
|
|
933
|
+
* This parser extracts the `data:` line content for each event and yields
|
|
934
|
+
* the raw JSON strings for the caller to parse and dispatch.
|
|
935
|
+
*
|
|
936
|
+
* @param {ReadableStream<Uint8Array>} stream - The raw SSE byte stream.
|
|
937
|
+
* @returns {AsyncGenerator<string>} Yields JSON string payloads.
|
|
938
|
+
* @private
|
|
939
|
+
*/
|
|
940
|
+
async *parseSseStream(stream) {
|
|
941
|
+
const reader = stream.getReader();
|
|
942
|
+
const decoder = new TextDecoder();
|
|
943
|
+
let buffer = '';
|
|
944
|
+
try {
|
|
945
|
+
while (true) {
|
|
946
|
+
const { done, value } = await reader.read();
|
|
947
|
+
if (done)
|
|
948
|
+
break;
|
|
949
|
+
buffer += decoder.decode(value, { stream: true });
|
|
950
|
+
// Process complete events (separated by double newlines)
|
|
951
|
+
const events = buffer.split('\n\n');
|
|
952
|
+
// Keep the last incomplete chunk in the buffer
|
|
953
|
+
buffer = events.pop() || '';
|
|
954
|
+
for (const event of events) {
|
|
955
|
+
const trimmed = event.trim();
|
|
956
|
+
if (!trimmed)
|
|
957
|
+
continue;
|
|
958
|
+
// Extract content after "data: " prefix
|
|
959
|
+
// Events may have multi-line data (though Gemini typically uses single-line)
|
|
960
|
+
const lines = trimmed.split('\n');
|
|
961
|
+
const dataLines = [];
|
|
962
|
+
for (const line of lines) {
|
|
963
|
+
if (line.startsWith('data: ')) {
|
|
964
|
+
dataLines.push(line.slice(6));
|
|
965
|
+
}
|
|
966
|
+
else if (line.startsWith('data:')) {
|
|
967
|
+
dataLines.push(line.slice(5));
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
if (dataLines.length > 0) {
|
|
971
|
+
const data = dataLines.join('\n').trim();
|
|
972
|
+
// Skip empty data or the [DONE] signal
|
|
973
|
+
if (data && data !== '[DONE]') {
|
|
974
|
+
yield data;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
// Process any remaining buffer content
|
|
980
|
+
if (buffer.trim()) {
|
|
981
|
+
const lines = buffer.trim().split('\n');
|
|
982
|
+
const dataLines = [];
|
|
983
|
+
for (const line of lines) {
|
|
984
|
+
if (line.startsWith('data: ')) {
|
|
985
|
+
dataLines.push(line.slice(6));
|
|
986
|
+
}
|
|
987
|
+
else if (line.startsWith('data:')) {
|
|
988
|
+
dataLines.push(line.slice(5));
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (dataLines.length > 0) {
|
|
992
|
+
const data = dataLines.join('\n').trim();
|
|
993
|
+
if (data && data !== '[DONE]') {
|
|
994
|
+
yield data;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
finally {
|
|
1000
|
+
reader.releaseLock();
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
//# sourceMappingURL=GeminiProvider.js.map
|