@juspay/neurolink 9.32.0 → 9.32.1
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/CHANGELOG.md +6 -0
- package/dist/auth/anthropicOAuth.js +1 -1
- package/dist/cli/commands/proxy.js +18 -5
- package/dist/client/aiSdkAdapter.js +1 -1
- package/dist/client/index.js +137 -501
- package/dist/core/factory.js +0 -1
- package/dist/core/redisConversationMemoryManager.js +1 -1
- package/dist/features/ppt/slideGenerator.js +0 -1
- package/dist/features/ppt/utils.js +0 -1
- package/dist/lib/server/routes/claudeProxyRoutes.js +45 -9
- package/dist/mcp/elicitationProtocol.js +1 -1
- package/dist/mcp/servers/agent/directToolsServer.js +0 -1
- package/dist/providers/azureOpenai.js +1 -1
- package/dist/providers/huggingFace.js +0 -1
- package/dist/providers/openaiCompatible.js +0 -1
- package/dist/sdk/toolRegistration.js +0 -1
- package/dist/server/openapi/generator.js +1 -1
- package/dist/server/routes/claudeProxyRoutes.js +45 -9
- package/dist/types/configTypes.js +0 -5
- package/dist/types/modelTypes.js +0 -1
- package/dist/types/tools.js +0 -1
- package/dist/types/typeAliases.js +0 -1
- package/dist/types/utilities.js +1 -1
- package/dist/types/workflowTypes.js +0 -1
- package/dist/utils/providerRetry.js +0 -1
- package/dist/utils/providerUtils.js +0 -1
- package/package.json +2 -2
- package/dist/client/adapters/providerImageAdapter.js +0 -588
- package/dist/client/adapters/tts/googleTTSHandler.js +0 -344
- package/dist/client/adapters/video/directorPipeline.js +0 -516
- package/dist/client/adapters/video/ffmpegAdapter.js +0 -206
- package/dist/client/adapters/video/frameExtractor.js +0 -143
- package/dist/client/adapters/video/vertexVideoHandler.js +0 -763
- package/dist/client/adapters/video/videoAnalyzer.js +0 -238
- package/dist/client/adapters/video/videoMerger.js +0 -171
- package/dist/client/agent/directTools.js +0 -840
- package/dist/client/auth/AuthProviderFactory.js +0 -111
- package/dist/client/auth/AuthProviderRegistry.js +0 -190
- package/dist/client/auth/RequestContext.js +0 -78
- package/dist/client/auth/accountPool.js +0 -178
- package/dist/client/auth/anthropicOAuth.js +0 -974
- package/dist/client/auth/authContext.js +0 -314
- package/dist/client/auth/errors.js +0 -39
- package/dist/client/auth/index.js +0 -61
- package/dist/client/auth/middleware/AuthMiddleware.js +0 -519
- package/dist/client/auth/middleware/rateLimitByUser.js +0 -554
- package/dist/client/auth/providers/BaseAuthProvider.js +0 -723
- package/dist/client/auth/providers/CognitoProvider.js +0 -304
- package/dist/client/auth/providers/KeycloakProvider.js +0 -393
- package/dist/client/auth/providers/auth0.js +0 -274
- package/dist/client/auth/providers/betterAuth.js +0 -182
- package/dist/client/auth/providers/clerk.js +0 -317
- package/dist/client/auth/providers/custom.js +0 -112
- package/dist/client/auth/providers/firebase.js +0 -226
- package/dist/client/auth/providers/jwt.js +0 -212
- package/dist/client/auth/providers/oauth2.js +0 -303
- package/dist/client/auth/providers/supabase.js +0 -259
- package/dist/client/auth/providers/workos.js +0 -284
- package/dist/client/auth/serverBridge.js +0 -25
- package/dist/client/auth/sessionManager.js +0 -437
- package/dist/client/auth/tokenStore.js +0 -799
- package/dist/client/client/aiSdkAdapter.js +0 -487
- package/dist/client/client/auth.js +0 -473
- package/dist/client/client/errors.js +0 -552
- package/dist/client/client/httpClient.js +0 -837
- package/dist/client/client/index.js +0 -172
- package/dist/client/client/interceptors.js +0 -601
- package/dist/client/client/sseClient.js +0 -545
- package/dist/client/client/streamingClient.js +0 -917
- package/dist/client/client/wsClient.js +0 -369
- package/dist/client/config/configManager.js +0 -303
- package/dist/client/config/conversationMemory.js +0 -86
- package/dist/client/config/taskClassificationConfig.js +0 -148
- package/dist/client/constants/contextWindows.js +0 -295
- package/dist/client/constants/enums.js +0 -853
- package/dist/client/constants/index.js +0 -207
- package/dist/client/constants/performance.js +0 -389
- package/dist/client/constants/retry.js +0 -266
- package/dist/client/constants/timeouts.js +0 -182
- package/dist/client/constants/tokens.js +0 -380
- package/dist/client/constants/videoErrors.js +0 -46
- package/dist/client/context/budgetChecker.js +0 -98
- package/dist/client/context/contextCompactor.js +0 -205
- package/dist/client/context/emergencyTruncation.js +0 -88
- package/dist/client/context/errorDetection.js +0 -171
- package/dist/client/context/errors.js +0 -21
- package/dist/client/context/fileTokenBudget.js +0 -127
- package/dist/client/context/prompts/summarizationPrompt.js +0 -117
- package/dist/client/context/stages/fileReadDeduplicator.js +0 -66
- package/dist/client/context/stages/slidingWindowTruncator.js +0 -190
- package/dist/client/context/stages/structuredSummarizer.js +0 -99
- package/dist/client/context/stages/toolOutputPruner.js +0 -52
- package/dist/client/context/summarizationEngine.js +0 -136
- package/dist/client/context/toolOutputLimits.js +0 -78
- package/dist/client/context/toolPairRepair.js +0 -66
- package/dist/client/core/analytics.js +0 -88
- package/dist/client/core/baseProvider.js +0 -1385
- package/dist/client/core/constants.js +0 -140
- package/dist/client/core/conversationMemoryFactory.js +0 -141
- package/dist/client/core/conversationMemoryInitializer.js +0 -128
- package/dist/client/core/conversationMemoryManager.js +0 -344
- package/dist/client/core/dynamicModels.js +0 -358
- package/dist/client/core/evaluation.js +0 -309
- package/dist/client/core/evaluationProviders.js +0 -248
- package/dist/client/core/factory.js +0 -412
- package/dist/client/core/infrastructure/baseError.js +0 -22
- package/dist/client/core/infrastructure/baseFactory.js +0 -54
- package/dist/client/core/infrastructure/baseRegistry.js +0 -53
- package/dist/client/core/infrastructure/index.js +0 -5
- package/dist/client/core/infrastructure/retry.js +0 -20
- package/dist/client/core/infrastructure/typedEventEmitter.js +0 -23
- package/dist/client/core/modelConfiguration.js +0 -851
- package/dist/client/core/modules/GenerationHandler.js +0 -588
- package/dist/client/core/modules/MessageBuilder.js +0 -273
- package/dist/client/core/modules/StreamHandler.js +0 -185
- package/dist/client/core/modules/TelemetryHandler.js +0 -203
- package/dist/client/core/modules/ToolsManager.js +0 -499
- package/dist/client/core/modules/Utilities.js +0 -331
- package/dist/client/core/redisConversationMemoryManager.js +0 -1435
- package/dist/client/core/streamAnalytics.js +0 -131
- package/dist/client/evaluation/contextBuilder.js +0 -134
- package/dist/client/evaluation/index.js +0 -61
- package/dist/client/evaluation/prompts.js +0 -73
- package/dist/client/evaluation/ragasEvaluator.js +0 -110
- package/dist/client/evaluation/retryManager.js +0 -78
- package/dist/client/evaluation/scoring.js +0 -61
- package/dist/client/factories/providerFactory.js +0 -166
- package/dist/client/factories/providerRegistry.js +0 -166
- package/dist/client/features/ppt/constants.js +0 -896
- package/dist/client/features/ppt/contentPlanner.js +0 -529
- package/dist/client/features/ppt/presentationOrchestrator.js +0 -236
- package/dist/client/features/ppt/slideGenerator.js +0 -532
- package/dist/client/features/ppt/slideRenderers.js +0 -2383
- package/dist/client/features/ppt/slideTypeInference.js +0 -405
- package/dist/client/features/ppt/types.js +0 -13
- package/dist/client/features/ppt/utils.js +0 -443
- package/dist/client/files/fileReferenceRegistry.js +0 -1543
- package/dist/client/files/fileTools.js +0 -450
- package/dist/client/files/streamingReader.js +0 -321
- package/dist/client/files/types.js +0 -23
- package/dist/client/hitl/hitlErrors.js +0 -54
- package/dist/client/hitl/hitlManager.js +0 -460
- package/dist/client/mcp/agentExposure.js +0 -356
- package/dist/client/mcp/auth/index.js +0 -11
- package/dist/client/mcp/auth/oauthClientProvider.js +0 -325
- package/dist/client/mcp/auth/tokenStorage.js +0 -134
- package/dist/client/mcp/batching/index.js +0 -10
- package/dist/client/mcp/batching/requestBatcher.js +0 -441
- package/dist/client/mcp/caching/index.js +0 -10
- package/dist/client/mcp/caching/toolCache.js +0 -433
- package/dist/client/mcp/elicitation/elicitationManager.js +0 -376
- package/dist/client/mcp/elicitation/index.js +0 -11
- package/dist/client/mcp/elicitation/types.js +0 -10
- package/dist/client/mcp/elicitationProtocol.js +0 -375
- package/dist/client/mcp/enhancedToolDiscovery.js +0 -481
- package/dist/client/mcp/externalServerManager.js +0 -1478
- package/dist/client/mcp/factory.js +0 -161
- package/dist/client/mcp/flexibleToolValidator.js +0 -161
- package/dist/client/mcp/httpRateLimiter.js +0 -391
- package/dist/client/mcp/httpRetryHandler.js +0 -178
- package/dist/client/mcp/index.js +0 -74
- package/dist/client/mcp/mcpCircuitBreaker.js +0 -427
- package/dist/client/mcp/mcpClientFactory.js +0 -708
- package/dist/client/mcp/mcpRegistryClient.js +0 -488
- package/dist/client/mcp/mcpServerBase.js +0 -373
- package/dist/client/mcp/multiServerManager.js +0 -579
- package/dist/client/mcp/registry.js +0 -158
- package/dist/client/mcp/routing/index.js +0 -10
- package/dist/client/mcp/routing/toolRouter.js +0 -416
- package/dist/client/mcp/serverCapabilities.js +0 -502
- package/dist/client/mcp/servers/agent/directToolsServer.js +0 -150
- package/dist/client/mcp/toolAnnotations.js +0 -239
- package/dist/client/mcp/toolConverter.js +0 -258
- package/dist/client/mcp/toolDiscoveryService.js +0 -798
- package/dist/client/mcp/toolIntegration.js +0 -334
- package/dist/client/mcp/toolRegistry.js +0 -729
- package/dist/client/memory/hippocampusInitializer.js +0 -19
- package/dist/client/memory/memoryRetrievalTools.js +0 -166
- package/dist/client/middleware/builtin/analytics.js +0 -132
- package/dist/client/middleware/builtin/autoEvaluation.js +0 -203
- package/dist/client/middleware/builtin/guardrails.js +0 -109
- package/dist/client/middleware/builtin/lifecycle.js +0 -168
- package/dist/client/middleware/factory.js +0 -327
- package/dist/client/middleware/registry.js +0 -295
- package/dist/client/middleware/utils/guardrailsUtils.js +0 -396
- package/dist/client/models/anthropicModels.js +0 -527
- package/dist/client/neurolink.js +0 -8233
- package/dist/client/observability/exporterRegistry.js +0 -413
- package/dist/client/observability/exporters/arizeExporter.js +0 -138
- package/dist/client/observability/exporters/baseExporter.js +0 -190
- package/dist/client/observability/exporters/braintrustExporter.js +0 -154
- package/dist/client/observability/exporters/datadogExporter.js +0 -196
- package/dist/client/observability/exporters/laminarExporter.js +0 -302
- package/dist/client/observability/exporters/langfuseExporter.js +0 -209
- package/dist/client/observability/exporters/langsmithExporter.js +0 -143
- package/dist/client/observability/exporters/otelExporter.js +0 -164
- package/dist/client/observability/exporters/posthogExporter.js +0 -287
- package/dist/client/observability/exporters/sentryExporter.js +0 -165
- package/dist/client/observability/index.js +0 -31
- package/dist/client/observability/metricsAggregator.js +0 -556
- package/dist/client/observability/otelBridge.js +0 -131
- package/dist/client/observability/retryPolicy.js +0 -383
- package/dist/client/observability/sampling/samplers.js +0 -216
- package/dist/client/observability/spanProcessor.js +0 -303
- package/dist/client/observability/tokenTracker.js +0 -413
- package/dist/client/observability/types/exporterTypes.js +0 -5
- package/dist/client/observability/types/index.js +0 -4
- package/dist/client/observability/types/spanTypes.js +0 -92
- package/dist/client/observability/utils/safeMetadata.js +0 -25
- package/dist/client/observability/utils/spanSerializer.js +0 -292
- package/dist/client/processors/archive/ArchiveProcessor.js +0 -1308
- package/dist/client/processors/base/BaseFileProcessor.js +0 -614
- package/dist/client/processors/base/types.js +0 -82
- package/dist/client/processors/config/fileTypes.js +0 -520
- package/dist/client/processors/config/index.js +0 -92
- package/dist/client/processors/config/languageMap.js +0 -410
- package/dist/client/processors/config/mimeTypes.js +0 -363
- package/dist/client/processors/config/sizeLimits.js +0 -258
- package/dist/client/processors/document/ExcelProcessor.js +0 -590
- package/dist/client/processors/document/OpenDocumentProcessor.js +0 -212
- package/dist/client/processors/document/PptxProcessor.js +0 -157
- package/dist/client/processors/document/RtfProcessor.js +0 -361
- package/dist/client/processors/document/WordProcessor.js +0 -353
- package/dist/client/processors/errors/FileErrorCode.js +0 -255
- package/dist/client/processors/errors/errorHelpers.js +0 -386
- package/dist/client/processors/errors/errorSerializer.js +0 -507
- package/dist/client/processors/errors/index.js +0 -49
- package/dist/client/processors/markup/SvgProcessor.js +0 -240
- package/dist/client/processors/media/AudioProcessor.js +0 -707
- package/dist/client/processors/media/VideoProcessor.js +0 -1045
- package/dist/client/providers/amazonBedrock.js +0 -1512
- package/dist/client/providers/amazonSagemaker.js +0 -162
- package/dist/client/providers/anthropic.js +0 -831
- package/dist/client/providers/azureOpenai.js +0 -143
- package/dist/client/providers/googleAiStudio.js +0 -1200
- package/dist/client/providers/googleNativeGemini3.js +0 -543
- package/dist/client/providers/googleVertex.js +0 -2936
- package/dist/client/providers/huggingFace.js +0 -315
- package/dist/client/providers/litellm.js +0 -488
- package/dist/client/providers/mistral.js +0 -157
- package/dist/client/providers/ollama.js +0 -1579
- package/dist/client/providers/openAI.js +0 -627
- package/dist/client/providers/openRouter.js +0 -543
- package/dist/client/providers/openaiCompatible.js +0 -290
- package/dist/client/providers/providerTypeUtils.js +0 -46
- package/dist/client/providers/sagemaker/adaptive-semaphore.js +0 -215
- package/dist/client/providers/sagemaker/client.js +0 -472
- package/dist/client/providers/sagemaker/config.js +0 -317
- package/dist/client/providers/sagemaker/detection.js +0 -606
- package/dist/client/providers/sagemaker/error-constants.js +0 -227
- package/dist/client/providers/sagemaker/errors.js +0 -299
- package/dist/client/providers/sagemaker/language-model.js +0 -775
- package/dist/client/providers/sagemaker/parsers.js +0 -634
- package/dist/client/providers/sagemaker/streaming.js +0 -331
- package/dist/client/providers/sagemaker/structured-parser.js +0 -625
- package/dist/client/proxy/accountQuota.js +0 -162
- package/dist/client/proxy/claudeFormat.js +0 -595
- package/dist/client/proxy/modelRouter.js +0 -29
- package/dist/client/proxy/oauthFetch.js +0 -367
- package/dist/client/proxy/proxyFetch.js +0 -586
- package/dist/client/proxy/requestLogger.js +0 -207
- package/dist/client/proxy/tokenRefresh.js +0 -124
- package/dist/client/proxy/usageStats.js +0 -74
- package/dist/client/proxy/utils/noProxyUtils.js +0 -149
- package/dist/client/rag/ChunkerFactory.js +0 -320
- package/dist/client/rag/ChunkerRegistry.js +0 -421
- package/dist/client/rag/chunkers/BaseChunker.js +0 -143
- package/dist/client/rag/chunkers/CharacterChunker.js +0 -28
- package/dist/client/rag/chunkers/HTMLChunker.js +0 -38
- package/dist/client/rag/chunkers/JSONChunker.js +0 -68
- package/dist/client/rag/chunkers/LaTeXChunker.js +0 -63
- package/dist/client/rag/chunkers/MarkdownChunker.js +0 -306
- package/dist/client/rag/chunkers/RecursiveChunker.js +0 -139
- package/dist/client/rag/chunkers/SemanticMarkdownChunker.js +0 -138
- package/dist/client/rag/chunkers/SentenceChunker.js +0 -66
- package/dist/client/rag/chunkers/TokenChunker.js +0 -61
- package/dist/client/rag/chunkers/index.js +0 -15
- package/dist/client/rag/chunking/characterChunker.js +0 -142
- package/dist/client/rag/chunking/chunkerRegistry.js +0 -194
- package/dist/client/rag/chunking/htmlChunker.js +0 -247
- package/dist/client/rag/chunking/index.js +0 -17
- package/dist/client/rag/chunking/jsonChunker.js +0 -281
- package/dist/client/rag/chunking/latexChunker.js +0 -251
- package/dist/client/rag/chunking/markdownChunker.js +0 -373
- package/dist/client/rag/chunking/recursiveChunker.js +0 -148
- package/dist/client/rag/chunking/semanticChunker.js +0 -306
- package/dist/client/rag/chunking/sentenceChunker.js +0 -230
- package/dist/client/rag/chunking/tokenChunker.js +0 -183
- package/dist/client/rag/document/MDocument.js +0 -392
- package/dist/client/rag/document/index.js +0 -5
- package/dist/client/rag/document/loaders.js +0 -500
- package/dist/client/rag/errors/RAGError.js +0 -274
- package/dist/client/rag/errors/index.js +0 -6
- package/dist/client/rag/graphRag/graphRAG.js +0 -401
- package/dist/client/rag/graphRag/index.js +0 -4
- package/dist/client/rag/index.js +0 -141
- package/dist/client/rag/metadata/MetadataExtractorFactory.js +0 -418
- package/dist/client/rag/metadata/MetadataExtractorRegistry.js +0 -362
- package/dist/client/rag/metadata/index.js +0 -9
- package/dist/client/rag/metadata/metadataExtractor.js +0 -280
- package/dist/client/rag/pipeline/RAGPipeline.js +0 -436
- package/dist/client/rag/pipeline/contextAssembly.js +0 -341
- package/dist/client/rag/pipeline/index.js +0 -5
- package/dist/client/rag/ragIntegration.js +0 -321
- package/dist/client/rag/reranker/RerankerFactory.js +0 -430
- package/dist/client/rag/reranker/RerankerRegistry.js +0 -402
- package/dist/client/rag/reranker/index.js +0 -9
- package/dist/client/rag/reranker/reranker.js +0 -277
- package/dist/client/rag/resilience/CircuitBreaker.js +0 -431
- package/dist/client/rag/resilience/RetryHandler.js +0 -304
- package/dist/client/rag/resilience/index.js +0 -7
- package/dist/client/rag/retrieval/hybridSearch.js +0 -335
- package/dist/client/rag/retrieval/index.js +0 -5
- package/dist/client/rag/retrieval/vectorQueryTool.js +0 -307
- package/dist/client/rag/types.js +0 -8
- package/dist/client/sdk/toolRegistration.js +0 -377
- package/dist/client/server/abstract/baseServerAdapter.js +0 -575
- package/dist/client/server/adapters/expressAdapter.js +0 -486
- package/dist/client/server/adapters/fastifyAdapter.js +0 -472
- package/dist/client/server/adapters/honoAdapter.js +0 -632
- package/dist/client/server/adapters/koaAdapter.js +0 -510
- package/dist/client/server/errors.js +0 -486
- package/dist/client/server/factory/serverAdapterFactory.js +0 -160
- package/dist/client/server/index.js +0 -108
- package/dist/client/server/middleware/abortSignal.js +0 -111
- package/dist/client/server/middleware/auth.js +0 -388
- package/dist/client/server/middleware/cache.js +0 -359
- package/dist/client/server/middleware/common.js +0 -281
- package/dist/client/server/middleware/deprecation.js +0 -190
- package/dist/client/server/middleware/mcpBodyAttachment.js +0 -63
- package/dist/client/server/middleware/rateLimit.js +0 -227
- package/dist/client/server/middleware/validation.js +0 -388
- package/dist/client/server/openapi/generator.js +0 -398
- package/dist/client/server/openapi/index.js +0 -36
- package/dist/client/server/openapi/schemas.js +0 -695
- package/dist/client/server/openapi/templates.js +0 -374
- package/dist/client/server/routes/agentRoutes.js +0 -189
- package/dist/client/server/routes/claudeProxyRoutes.js +0 -1600
- package/dist/client/server/routes/healthRoutes.js +0 -187
- package/dist/client/server/routes/index.js +0 -57
- package/dist/client/server/routes/mcpRoutes.js +0 -342
- package/dist/client/server/routes/memoryRoutes.js +0 -350
- package/dist/client/server/routes/openApiRoutes.js +0 -126
- package/dist/client/server/routes/toolRoutes.js +0 -199
- package/dist/client/server/streaming/dataStream.js +0 -486
- package/dist/client/server/streaming/index.js +0 -11
- package/dist/client/server/types.js +0 -67
- package/dist/client/server/utils/redaction.js +0 -334
- package/dist/client/server/utils/validation.js +0 -243
- package/dist/client/server/websocket/WebSocketHandler.js +0 -383
- package/dist/client/server/websocket/index.js +0 -4
- package/dist/client/services/server/ai/observability/instrumentation.js +0 -808
- package/dist/client/telemetry/attributes.js +0 -100
- package/dist/client/telemetry/index.js +0 -26
- package/dist/client/telemetry/telemetryService.js +0 -308
- package/dist/client/telemetry/tracers.js +0 -17
- package/dist/client/telemetry/withSpan.js +0 -34
- package/dist/client/types/actionTypes.js +0 -6
- package/dist/client/types/analytics.js +0 -5
- package/dist/client/types/authTypes.js +0 -9
- package/dist/client/types/circuitBreakerErrors.js +0 -34
- package/dist/client/types/cli.js +0 -21
- package/dist/client/types/clientTypes.js +0 -10
- package/dist/client/types/common.js +0 -51
- package/dist/client/types/configTypes.js +0 -49
- package/dist/client/types/content.js +0 -19
- package/dist/client/types/contextTypes.js +0 -400
- package/dist/client/types/conversation.js +0 -47
- package/dist/client/types/conversationMemoryInterface.js +0 -6
- package/dist/client/types/domainTypes.js +0 -5
- package/dist/client/types/errors.js +0 -167
- package/dist/client/types/evaluation.js +0 -5
- package/dist/client/types/evaluationProviders.js +0 -5
- package/dist/client/types/evaluationTypes.js +0 -1
- package/dist/client/types/externalMcp.js +0 -6
- package/dist/client/types/fileReferenceTypes.js +0 -8
- package/dist/client/types/fileTypes.js +0 -4
- package/dist/client/types/generateTypes.js +0 -1
- package/dist/client/types/guardrails.js +0 -1
- package/dist/client/types/hitlTypes.js +0 -8
- package/dist/client/types/index.js +0 -57
- package/dist/client/types/mcpTypes.js +0 -5
- package/dist/client/types/middlewareTypes.js +0 -1
- package/dist/client/types/modelTypes.js +0 -30
- package/dist/client/types/multimodal.js +0 -135
- package/dist/client/types/observability.js +0 -6
- package/dist/client/types/pptTypes.js +0 -82
- package/dist/client/types/providers.js +0 -111
- package/dist/client/types/proxyTypes.js +0 -16
- package/dist/client/types/ragTypes.js +0 -7
- package/dist/client/types/sdkTypes.js +0 -8
- package/dist/client/types/serviceTypes.js +0 -5
- package/dist/client/types/streamTypes.js +0 -1
- package/dist/client/types/subscriptionTypes.js +0 -9
- package/dist/client/types/taskClassificationTypes.js +0 -5
- package/dist/client/types/tools.js +0 -24
- package/dist/client/types/ttsTypes.js +0 -57
- package/dist/client/types/typeAliases.js +0 -48
- package/dist/client/types/utilities.js +0 -4
- package/dist/client/types/workflowTypes.js +0 -30
- package/dist/client/utils/async/withTimeout.js +0 -98
- package/dist/client/utils/asyncMutex.js +0 -60
- package/dist/client/utils/conversationMemory.js +0 -431
- package/dist/client/utils/csvProcessor.js +0 -846
- package/dist/client/utils/errorHandling.js +0 -936
- package/dist/client/utils/evaluationUtils.js +0 -131
- package/dist/client/utils/factoryProcessing.js +0 -589
- package/dist/client/utils/fileDetector.js +0 -2161
- package/dist/client/utils/imageCache.js +0 -376
- package/dist/client/utils/imageProcessor.js +0 -704
- package/dist/client/utils/logger.js +0 -491
- package/dist/client/utils/mcpDefaults.js +0 -134
- package/dist/client/utils/messageBuilder.js +0 -1653
- package/dist/client/utils/modelAliasResolver.js +0 -54
- package/dist/client/utils/modelDetection.js +0 -80
- package/dist/client/utils/modelRouter.js +0 -292
- package/dist/client/utils/multimodalOptionsBuilder.js +0 -65
- package/dist/client/utils/observabilityHelpers.js +0 -47
- package/dist/client/utils/parameterValidation.js +0 -966
- package/dist/client/utils/pdfProcessor.js +0 -410
- package/dist/client/utils/performance.js +0 -222
- package/dist/client/utils/pricing.js +0 -340
- package/dist/client/utils/promptRedaction.js +0 -62
- package/dist/client/utils/providerConfig.js +0 -1009
- package/dist/client/utils/providerHealth.js +0 -1237
- package/dist/client/utils/providerRetry.js +0 -112
- package/dist/client/utils/providerUtils.js +0 -434
- package/dist/client/utils/rateLimiter.js +0 -200
- package/dist/client/utils/redis.js +0 -368
- package/dist/client/utils/retryHandler.js +0 -269
- package/dist/client/utils/retryability.js +0 -22
- package/dist/client/utils/sanitizers/svg.js +0 -481
- package/dist/client/utils/schemaConversion.js +0 -255
- package/dist/client/utils/taskClassificationUtils.js +0 -149
- package/dist/client/utils/taskClassifier.js +0 -94
- package/dist/client/utils/thinkingConfig.js +0 -104
- package/dist/client/utils/timeout.js +0 -359
- package/dist/client/utils/tokenEstimation.js +0 -142
- package/dist/client/utils/tokenLimits.js +0 -125
- package/dist/client/utils/tokenUtils.js +0 -239
- package/dist/client/utils/toolUtils.js +0 -75
- package/dist/client/utils/transformationUtils.js +0 -554
- package/dist/client/utils/ttsProcessor.js +0 -286
- package/dist/client/utils/typeUtils.js +0 -97
- package/dist/client/utils/videoAnalysisProcessor.js +0 -67
- package/dist/client/workflow/config.js +0 -398
- package/dist/client/workflow/core/ensembleExecutor.js +0 -407
- package/dist/client/workflow/core/judgeScorer.js +0 -544
- package/dist/client/workflow/core/responseConditioner.js +0 -225
- package/dist/client/workflow/core/types/conditionerTypes.js +0 -7
- package/dist/client/workflow/core/types/ensembleTypes.js +0 -7
- package/dist/client/workflow/core/types/index.js +0 -7
- package/dist/client/workflow/core/types/judgeTypes.js +0 -7
- package/dist/client/workflow/core/types/layerTypes.js +0 -7
- package/dist/client/workflow/core/types/registryTypes.js +0 -7
- package/dist/client/workflow/core/workflowRegistry.js +0 -304
- package/dist/client/workflow/core/workflowRunner.js +0 -586
- package/dist/client/workflow/index.js +0 -50
- package/dist/client/workflow/types.js +0 -9
- package/dist/client/workflow/utils/types/index.js +0 -7
- package/dist/client/workflow/utils/workflowMetrics.js +0 -311
- package/dist/client/workflow/utils/workflowValidation.js +0 -420
- package/dist/client/workflow/workflows/adaptiveWorkflow.js +0 -366
- package/dist/client/workflow/workflows/consensusWorkflow.js +0 -192
- package/dist/client/workflow/workflows/fallbackWorkflow.js +0 -225
- package/dist/client/workflow/workflows/multiJudgeWorkflow.js +0 -351
- /package/dist/client/{client/reactHooks.js → reactHooks.js} +0 -0
|
@@ -1,1512 +0,0 @@
|
|
|
1
|
-
import { BedrockClient, ListFoundationModelsCommand, } from "@aws-sdk/client-bedrock";
|
|
2
|
-
import { BedrockRuntimeClient, ConverseCommand, ConverseStreamCommand, ImageFormat, } from "@aws-sdk/client-bedrock-runtime";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { createAnalytics } from "../core/analytics.js";
|
|
5
|
-
import { BaseProvider } from "../core/baseProvider.js";
|
|
6
|
-
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
7
|
-
import { AuthenticationError, ProviderError } from "../types/errors.js";
|
|
8
|
-
import { isAbortError, withTimeout } from "../utils/errorHandling.js";
|
|
9
|
-
import { logger } from "../utils/logger.js";
|
|
10
|
-
import { calculateCost } from "../utils/pricing.js";
|
|
11
|
-
import { buildMultimodalMessagesArray } from "../utils/messageBuilder.js";
|
|
12
|
-
import { buildMultimodalOptions } from "../utils/multimodalOptionsBuilder.js";
|
|
13
|
-
import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
|
|
14
|
-
import { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
|
|
15
|
-
const bedrockTracer = trace.getTracer("neurolink.bedrock");
|
|
16
|
-
// Bedrock-specific types now imported from ../types/providerSpecific.js
|
|
17
|
-
export class AmazonBedrockProvider extends BaseProvider {
|
|
18
|
-
bedrockClient;
|
|
19
|
-
conversationHistory = [];
|
|
20
|
-
region;
|
|
21
|
-
constructor(modelName, neurolink, region) {
|
|
22
|
-
super(modelName, "bedrock", neurolink);
|
|
23
|
-
this.region = region || process.env.AWS_REGION || "us-east-1";
|
|
24
|
-
logger.debug("[AmazonBedrockProvider] Starting constructor with extensive logging for debugging");
|
|
25
|
-
// Log environment variables for debugging
|
|
26
|
-
logger.debug(`[AmazonBedrockProvider] Environment check: AWS_REGION=${process.env.AWS_REGION || "undefined"}, AWS_ACCESS_KEY_ID=${process.env.AWS_ACCESS_KEY_ID ? "SET" : "undefined"}, AWS_SECRET_ACCESS_KEY=${process.env.AWS_SECRET_ACCESS_KEY ? "SET" : "undefined"}`);
|
|
27
|
-
try {
|
|
28
|
-
// Create BedrockRuntimeClient with clean configuration like working Bedrock-MCP-Connector
|
|
29
|
-
// Absolutely no proxy interference - let AWS SDK handle everything natively
|
|
30
|
-
logger.debug("[AmazonBedrockProvider] Creating BedrockRuntimeClient with clean configuration");
|
|
31
|
-
this.bedrockClient = new BedrockRuntimeClient({
|
|
32
|
-
region: this.region,
|
|
33
|
-
// Clean configuration - AWS SDK will handle credentials via:
|
|
34
|
-
// 1. IAM roles (preferred in production)
|
|
35
|
-
// 2. Environment variables
|
|
36
|
-
// 3. AWS config files
|
|
37
|
-
// 4. Instance metadata
|
|
38
|
-
});
|
|
39
|
-
logger.debug(`[AmazonBedrockProvider] Successfully created BedrockRuntimeClient with model: ${this.modelName}, region: ${this.region}`);
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
logger.error(`[AmazonBedrockProvider] CRITICAL: Failed to initialize BedrockRuntimeClient:`, error);
|
|
43
|
-
throw error;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Perform initial health check to catch credential/connectivity issues early
|
|
48
|
-
* This prevents the health check failure we saw in production logs
|
|
49
|
-
*/
|
|
50
|
-
async performInitialHealthCheck() {
|
|
51
|
-
const bedrockClient = new BedrockClient({
|
|
52
|
-
region: this.region,
|
|
53
|
-
});
|
|
54
|
-
try {
|
|
55
|
-
logger.debug("[AmazonBedrockProvider] Starting initial health check to validate credentials and connectivity");
|
|
56
|
-
// Try to list foundation models as a lightweight health check
|
|
57
|
-
const command = new ListFoundationModelsCommand({});
|
|
58
|
-
const startTime = Date.now();
|
|
59
|
-
await bedrockClient.send(command);
|
|
60
|
-
const responseTime = Date.now() - startTime;
|
|
61
|
-
logger.debug(`[AmazonBedrockProvider] Health check PASSED - credentials valid, connectivity good, responseTime: ${responseTime}ms`);
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
65
|
-
logger.error(`[AmazonBedrockProvider] Health check FAILED - this will cause production failures:`, {
|
|
66
|
-
error: errorMessage,
|
|
67
|
-
errorType: error instanceof Error ? error.constructor.name : "Unknown",
|
|
68
|
-
region: process.env.AWS_REGION || "us-east-1",
|
|
69
|
-
hasAccessKey: !!process.env.AWS_ACCESS_KEY_ID,
|
|
70
|
-
hasSecretKey: !!process.env.AWS_SECRET_ACCESS_KEY,
|
|
71
|
-
});
|
|
72
|
-
// Don't throw here - let the actual usage fail with better context
|
|
73
|
-
}
|
|
74
|
-
finally {
|
|
75
|
-
try {
|
|
76
|
-
bedrockClient.destroy();
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
// Ignore destroy errors during cleanup
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
// Not using AI SDK approach in conversation management
|
|
84
|
-
getAISDKModel() {
|
|
85
|
-
throw new Error("AmazonBedrockProvider does not use AI SDK models");
|
|
86
|
-
}
|
|
87
|
-
getProviderName() {
|
|
88
|
-
return "bedrock";
|
|
89
|
-
}
|
|
90
|
-
getDefaultModel() {
|
|
91
|
-
return process.env.BEDROCK_MODEL || "anthropic.claude-sonnet-4-6";
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Get the default embedding model for Amazon Bedrock
|
|
95
|
-
* @returns The default Bedrock embedding model name
|
|
96
|
-
*/
|
|
97
|
-
getDefaultEmbeddingModel() {
|
|
98
|
-
return (process.env.BEDROCK_EMBEDDING_MODEL ||
|
|
99
|
-
process.env.AWS_EMBEDDING_MODEL ||
|
|
100
|
-
"amazon.titan-embed-text-v2:0");
|
|
101
|
-
}
|
|
102
|
-
// Override the main generate method to implement conversation management
|
|
103
|
-
async generate(optionsOrPrompt) {
|
|
104
|
-
logger.debug("[AmazonBedrockProvider] generate() called with conversation management");
|
|
105
|
-
const options = typeof optionsOrPrompt === "string"
|
|
106
|
-
? { prompt: optionsOrPrompt }
|
|
107
|
-
: optionsOrPrompt;
|
|
108
|
-
// Clear conversation history for new generation
|
|
109
|
-
this.conversationHistory = [];
|
|
110
|
-
// Check for multimodal input (images, PDFs, CSVs, files)
|
|
111
|
-
// Cast to any to access multimodal properties (runtime check is safe)
|
|
112
|
-
const input = options.input;
|
|
113
|
-
const hasMultimodalInput = !!(input?.images?.length ||
|
|
114
|
-
input?.content?.length ||
|
|
115
|
-
input?.files?.length ||
|
|
116
|
-
input?.csvFiles?.length ||
|
|
117
|
-
input?.pdfFiles?.length);
|
|
118
|
-
if (hasMultimodalInput) {
|
|
119
|
-
logger.debug(`[AmazonBedrockProvider] Detected multimodal input in generate(), using multimodal message builder`, {
|
|
120
|
-
hasImages: !!input?.images?.length,
|
|
121
|
-
imageCount: input?.images?.length || 0,
|
|
122
|
-
hasContent: !!input?.content?.length,
|
|
123
|
-
contentCount: input?.content?.length || 0,
|
|
124
|
-
hasFiles: !!input?.files?.length,
|
|
125
|
-
fileCount: input?.files?.length || 0,
|
|
126
|
-
hasCSVFiles: !!input?.csvFiles?.length,
|
|
127
|
-
csvFileCount: input?.csvFiles?.length || 0,
|
|
128
|
-
hasPDFFiles: !!input?.pdfFiles?.length,
|
|
129
|
-
pdfFileCount: input?.pdfFiles?.length || 0,
|
|
130
|
-
});
|
|
131
|
-
// Cast options to StreamOptions for multimodal processing
|
|
132
|
-
const streamOptions = options;
|
|
133
|
-
const multimodalOptions = buildMultimodalOptions(streamOptions, this.providerName, this.modelName);
|
|
134
|
-
const multimodalMessages = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
|
135
|
-
// Convert to Bedrock format
|
|
136
|
-
this.conversationHistory =
|
|
137
|
-
this.convertToBedrockMessages(multimodalMessages);
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
logger.debug(`[AmazonBedrockProvider] Text-only input in generate(), using simple message builder`);
|
|
141
|
-
// Add user message to conversation - simple text-only case
|
|
142
|
-
const userMessage = {
|
|
143
|
-
role: "user",
|
|
144
|
-
content: [{ text: options.prompt }],
|
|
145
|
-
};
|
|
146
|
-
this.conversationHistory.push(userMessage);
|
|
147
|
-
}
|
|
148
|
-
logger.debug(`[AmazonBedrockProvider] Starting conversation with ${this.conversationHistory.length} message(s)`);
|
|
149
|
-
// Start conversation loop and return enhanced result
|
|
150
|
-
const text = await this.conversationLoop(options);
|
|
151
|
-
return {
|
|
152
|
-
content: text, // CLI expects 'content' not 'text'
|
|
153
|
-
usage: { total: 0, input: 0, output: 0 },
|
|
154
|
-
model: this.modelName || this.getDefaultModel(),
|
|
155
|
-
provider: this.getProviderName(),
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
async conversationLoop(options) {
|
|
159
|
-
const maxIterations = 10; // Prevent infinite loops
|
|
160
|
-
let iteration = 0;
|
|
161
|
-
while (iteration < maxIterations) {
|
|
162
|
-
iteration++;
|
|
163
|
-
logger.debug(`[AmazonBedrockProvider] Conversation iteration ${iteration}`);
|
|
164
|
-
try {
|
|
165
|
-
logger.debug(`[AmazonBedrockProvider] About to call Bedrock API`);
|
|
166
|
-
const response = await this.callBedrock(options);
|
|
167
|
-
logger.debug(`[AmazonBedrockProvider] Received Bedrock response`, JSON.stringify(response, null, 2));
|
|
168
|
-
const result = await this.handleBedrockResponse(response);
|
|
169
|
-
logger.debug(`[AmazonBedrockProvider] Handle response result:`, result);
|
|
170
|
-
if (result.shouldContinue) {
|
|
171
|
-
logger.debug(`[AmazonBedrockProvider] Continuing conversation loop...`);
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
logger.debug(`[AmazonBedrockProvider] Conversation completed with final text`);
|
|
175
|
-
logger.debug(`[AmazonBedrockProvider] Returning final text: "${result.text}"`);
|
|
176
|
-
return result.text || "";
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
catch (error) {
|
|
180
|
-
logger.error(`[AmazonBedrockProvider] Error in conversation loop:`, error);
|
|
181
|
-
throw this.handleProviderError(error);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
throw new Error("Conversation loop exceeded maximum iterations");
|
|
185
|
-
}
|
|
186
|
-
async callBedrock(options) {
|
|
187
|
-
const startTime = Date.now();
|
|
188
|
-
return bedrockTracer.startActiveSpan("bedrock.generate", {
|
|
189
|
-
kind: SpanKind.CLIENT,
|
|
190
|
-
attributes: {
|
|
191
|
-
"gen_ai.system": "aws.bedrock",
|
|
192
|
-
"gen_ai.request.model": this.modelName || this.getDefaultModel(),
|
|
193
|
-
"gen_ai.operation.name": "chat",
|
|
194
|
-
},
|
|
195
|
-
}, async (generateSpan) => {
|
|
196
|
-
logger.info(`🚀 [AmazonBedrockProvider] Starting Bedrock API call at ${new Date().toISOString()}`);
|
|
197
|
-
try {
|
|
198
|
-
// Pre-call validation and logging
|
|
199
|
-
let region = "unknown";
|
|
200
|
-
try {
|
|
201
|
-
region =
|
|
202
|
-
typeof this.bedrockClient.config.region === "function"
|
|
203
|
-
? await this.bedrockClient.config.region()
|
|
204
|
-
: (this.bedrockClient.config.region ?? "unknown");
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
// Region lookup failed — not critical, only used for logging
|
|
208
|
-
}
|
|
209
|
-
logger.info(`🔧 [AmazonBedrockProvider] Client region: ${region}`);
|
|
210
|
-
logger.info(`🔧 [AmazonBedrockProvider] Model: ${this.modelName || this.getDefaultModel()}`);
|
|
211
|
-
logger.info(`🔧 [AmazonBedrockProvider] Conversation history length: ${this.conversationHistory.length}`);
|
|
212
|
-
// Get all available tools
|
|
213
|
-
const aiTools = await this.getAllTools();
|
|
214
|
-
const allTools = this.convertAISDKToolsToToolDefinitions(aiTools);
|
|
215
|
-
const toolConfig = this.formatToolsForBedrock(allTools);
|
|
216
|
-
const commandInput = {
|
|
217
|
-
modelId: this.modelName || this.getDefaultModel(),
|
|
218
|
-
messages: this.convertToAWSMessages(this.conversationHistory),
|
|
219
|
-
system: [
|
|
220
|
-
{
|
|
221
|
-
text: options.systemPrompt ||
|
|
222
|
-
"You are a helpful assistant with access to external tools. Use tools when necessary to provide accurate information.",
|
|
223
|
-
},
|
|
224
|
-
],
|
|
225
|
-
inferenceConfig: {
|
|
226
|
-
maxTokens: options.maxTokens, // No default limit - unlimited unless specified
|
|
227
|
-
temperature: options.temperature || 0.7,
|
|
228
|
-
},
|
|
229
|
-
};
|
|
230
|
-
if (toolConfig) {
|
|
231
|
-
commandInput.toolConfig = toolConfig;
|
|
232
|
-
logger.info(`🛠️ [AmazonBedrockProvider] Tools configured: ${toolConfig.tools?.length || 0}`);
|
|
233
|
-
}
|
|
234
|
-
// Log command details for debugging
|
|
235
|
-
logger.info(`📋 [AmazonBedrockProvider] Command input summary:`);
|
|
236
|
-
logger.info(` - Model ID: ${commandInput.modelId}`);
|
|
237
|
-
logger.info(` - Messages count: ${commandInput.messages?.length || 0}`);
|
|
238
|
-
logger.info(` - System prompts: ${commandInput.system?.length || 0}`);
|
|
239
|
-
logger.info(` - Max tokens: ${commandInput.inferenceConfig?.maxTokens}`);
|
|
240
|
-
logger.info(` - Temperature: ${commandInput.inferenceConfig?.temperature}`);
|
|
241
|
-
logger.debug(`[AmazonBedrockProvider] Calling Bedrock with ${this.conversationHistory.length} messages and ${toolConfig?.tools?.length || 0} tools`);
|
|
242
|
-
// Create command and attempt API call
|
|
243
|
-
const command = new ConverseCommand(commandInput);
|
|
244
|
-
logger.debug("[Observability] Bedrock API request", {
|
|
245
|
-
model: commandInput.modelId,
|
|
246
|
-
region: region,
|
|
247
|
-
messageCount: commandInput.messages?.length || 0,
|
|
248
|
-
toolCount: commandInput.toolConfig?.tools?.length || 0,
|
|
249
|
-
maxTokens: commandInput.inferenceConfig?.maxTokens,
|
|
250
|
-
});
|
|
251
|
-
const apiCallStartTime = Date.now();
|
|
252
|
-
const response = await withTimeout(this.bedrockClient.send(command), 120_000, new Error("Bedrock API call timed out"));
|
|
253
|
-
const apiCallDuration = Date.now() - apiCallStartTime;
|
|
254
|
-
logger.debug("[Observability] Bedrock API response", {
|
|
255
|
-
model: commandInput.modelId,
|
|
256
|
-
durationMs: apiCallDuration,
|
|
257
|
-
hasContent: !!response.output?.message?.content?.length,
|
|
258
|
-
stopReason: response.stopReason,
|
|
259
|
-
usage: response.usage
|
|
260
|
-
? {
|
|
261
|
-
inputTokens: response.usage.inputTokens,
|
|
262
|
-
outputTokens: response.usage.outputTokens,
|
|
263
|
-
totalTokens: (response.usage.inputTokens || 0) +
|
|
264
|
-
(response.usage.outputTokens || 0),
|
|
265
|
-
}
|
|
266
|
-
: undefined,
|
|
267
|
-
});
|
|
268
|
-
logger.info(`[AmazonBedrockProvider] Bedrock API call successful`);
|
|
269
|
-
logger.info(`[AmazonBedrockProvider] API call duration: ${apiCallDuration}ms`);
|
|
270
|
-
const totalDuration = Date.now() - startTime;
|
|
271
|
-
logger.info(`[AmazonBedrockProvider] Total callBedrock duration: ${totalDuration}ms`);
|
|
272
|
-
generateSpan.setAttribute("gen_ai.response.stop_reason", response.stopReason ?? "");
|
|
273
|
-
generateSpan.setAttribute("gen_ai.usage.input_tokens", response.usage?.inputTokens ?? 0);
|
|
274
|
-
generateSpan.setAttribute("gen_ai.usage.output_tokens", response.usage?.outputTokens ?? 0);
|
|
275
|
-
const cost = calculateCost(this.providerName, this.modelName, {
|
|
276
|
-
input: response.usage?.inputTokens ?? 0,
|
|
277
|
-
output: response.usage?.outputTokens ?? 0,
|
|
278
|
-
total: (response.usage?.inputTokens ?? 0) +
|
|
279
|
-
(response.usage?.outputTokens ?? 0),
|
|
280
|
-
});
|
|
281
|
-
if (cost && cost > 0) {
|
|
282
|
-
generateSpan.setAttribute("neurolink.cost", cost);
|
|
283
|
-
}
|
|
284
|
-
generateSpan.setStatus({ code: SpanStatusCode.OK });
|
|
285
|
-
generateSpan.end();
|
|
286
|
-
return response;
|
|
287
|
-
}
|
|
288
|
-
catch (error) {
|
|
289
|
-
const errorDuration = Date.now() - startTime;
|
|
290
|
-
// Extract AWS metadata for structured logging
|
|
291
|
-
const awsError = error && typeof error === "object"
|
|
292
|
-
? error
|
|
293
|
-
: null;
|
|
294
|
-
const metadata = awsError?.$metadata && typeof awsError.$metadata === "object"
|
|
295
|
-
? awsError.$metadata
|
|
296
|
-
: null;
|
|
297
|
-
logger.debug("[Observability] Bedrock API request failed", {
|
|
298
|
-
model: this.modelName || this.getDefaultModel(),
|
|
299
|
-
durationMs: errorDuration,
|
|
300
|
-
error: error instanceof Error ? error.message : String(error),
|
|
301
|
-
errorName: error instanceof Error ? error.name : undefined,
|
|
302
|
-
httpStatus: metadata?.httpStatusCode,
|
|
303
|
-
awsRequestId: metadata?.requestId,
|
|
304
|
-
awsErrorCode: awsError?.Code,
|
|
305
|
-
});
|
|
306
|
-
logger.error(`[AmazonBedrockProvider] Bedrock API call failed after ${errorDuration}ms`);
|
|
307
|
-
if (error instanceof Error) {
|
|
308
|
-
logger.error(`[AmazonBedrockProvider] Error: ${error.name} - ${error.message}`);
|
|
309
|
-
}
|
|
310
|
-
if (metadata) {
|
|
311
|
-
logger.error(`[AmazonBedrockProvider] AWS SDK metadata`, {
|
|
312
|
-
httpStatus: metadata.httpStatusCode,
|
|
313
|
-
requestId: metadata.requestId,
|
|
314
|
-
attempts: metadata.attempts,
|
|
315
|
-
totalRetryDelay: metadata.totalRetryDelay,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
generateSpan.setStatus({
|
|
319
|
-
code: SpanStatusCode.ERROR,
|
|
320
|
-
message: error instanceof Error ? error.message : String(error),
|
|
321
|
-
});
|
|
322
|
-
generateSpan.recordException(error instanceof Error ? error : new Error(String(error)));
|
|
323
|
-
generateSpan.end();
|
|
324
|
-
throw error;
|
|
325
|
-
}
|
|
326
|
-
}); // end bedrockTracer.startActiveSpan('bedrock.generate')
|
|
327
|
-
}
|
|
328
|
-
async handleBedrockResponse(response) {
|
|
329
|
-
logger.debug(`[AmazonBedrockProvider] Received response with stopReason: ${response.stopReason}`);
|
|
330
|
-
if (!response.output || !response.output.message) {
|
|
331
|
-
throw new Error("Invalid response structure from Bedrock API");
|
|
332
|
-
}
|
|
333
|
-
const assistantMessage = response.output.message;
|
|
334
|
-
const stopReason = response.stopReason;
|
|
335
|
-
// Add assistant message to conversation history
|
|
336
|
-
const bedrockAssistantMessage = {
|
|
337
|
-
role: "assistant",
|
|
338
|
-
content: (assistantMessage.content || []).map((item) => {
|
|
339
|
-
const bedrockItem = {};
|
|
340
|
-
if ("text" in item && item.text) {
|
|
341
|
-
bedrockItem.text = item.text;
|
|
342
|
-
}
|
|
343
|
-
if ("toolUse" in item && item.toolUse) {
|
|
344
|
-
bedrockItem.toolUse = {
|
|
345
|
-
toolUseId: item.toolUse.toolUseId || "",
|
|
346
|
-
name: item.toolUse.name || "",
|
|
347
|
-
input: item.toolUse.input || {},
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
if ("toolResult" in item && item.toolResult) {
|
|
351
|
-
bedrockItem.toolResult = {
|
|
352
|
-
toolUseId: item.toolResult.toolUseId || "",
|
|
353
|
-
content: (item.toolResult.content || []).map((c) => ({
|
|
354
|
-
text: typeof c === "object" && "text" in c
|
|
355
|
-
? c.text || ""
|
|
356
|
-
: "",
|
|
357
|
-
})),
|
|
358
|
-
status: item.toolResult.status || "unknown",
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
return bedrockItem;
|
|
362
|
-
}),
|
|
363
|
-
};
|
|
364
|
-
this.conversationHistory.push(bedrockAssistantMessage);
|
|
365
|
-
if (stopReason === "end_turn" || stopReason === "stop_sequence") {
|
|
366
|
-
// Extract text from assistant message
|
|
367
|
-
const textContent = bedrockAssistantMessage.content
|
|
368
|
-
.filter((item) => item.text)
|
|
369
|
-
.map((item) => item.text)
|
|
370
|
-
.join(" ");
|
|
371
|
-
return { shouldContinue: false, text: textContent };
|
|
372
|
-
}
|
|
373
|
-
else if (stopReason === "tool_use") {
|
|
374
|
-
logger.debug(`[AmazonBedrockProvider] Tool use detected - executing tools immediately`);
|
|
375
|
-
// Execute all tool uses in the message
|
|
376
|
-
const toolResults = [];
|
|
377
|
-
for (const contentItem of bedrockAssistantMessage.content) {
|
|
378
|
-
if (contentItem.toolUse) {
|
|
379
|
-
logger.debug(`[AmazonBedrockProvider] Executing tool: ${contentItem.toolUse.name}`);
|
|
380
|
-
try {
|
|
381
|
-
// Execute tool using BaseProvider's tool execution
|
|
382
|
-
logger.debug(`[AmazonBedrockProvider] Debug toolUse.input:`, JSON.stringify(contentItem.toolUse.input, null, 2));
|
|
383
|
-
const toolResult = await this.executeSingleTool(contentItem.toolUse.name, contentItem.toolUse.input || {}, contentItem.toolUse.toolUseId);
|
|
384
|
-
logger.debug(`[AmazonBedrockProvider] Tool execution successful: ${contentItem.toolUse.name}`);
|
|
385
|
-
toolResults.push({
|
|
386
|
-
toolResult: {
|
|
387
|
-
toolUseId: contentItem.toolUse.toolUseId,
|
|
388
|
-
content: [{ text: String(toolResult) }],
|
|
389
|
-
status: "success",
|
|
390
|
-
},
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
catch (error) {
|
|
394
|
-
logger.error(`[AmazonBedrockProvider] Tool execution failed: ${contentItem.toolUse.name}`, error);
|
|
395
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
396
|
-
// Still create toolResult for failed tools to maintain 1:1 mapping with toolUse blocks
|
|
397
|
-
toolResults.push({
|
|
398
|
-
toolResult: {
|
|
399
|
-
toolUseId: contentItem.toolUse.toolUseId,
|
|
400
|
-
content: [
|
|
401
|
-
{
|
|
402
|
-
text: `Error executing tool ${contentItem.toolUse.name}: ${errorMessage}`,
|
|
403
|
-
},
|
|
404
|
-
],
|
|
405
|
-
status: "error",
|
|
406
|
-
},
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
// Add tool results as user message
|
|
412
|
-
if (toolResults.length > 0) {
|
|
413
|
-
const userMessageWithToolResults = {
|
|
414
|
-
role: "user",
|
|
415
|
-
content: toolResults,
|
|
416
|
-
};
|
|
417
|
-
this.conversationHistory.push(userMessageWithToolResults);
|
|
418
|
-
logger.debug(`[AmazonBedrockProvider] Added ${toolResults.length} tool results to conversation`);
|
|
419
|
-
}
|
|
420
|
-
return { shouldContinue: true };
|
|
421
|
-
}
|
|
422
|
-
else if (stopReason === "max_tokens") {
|
|
423
|
-
// Max tokens reached — return what we have rather than continuing,
|
|
424
|
-
// since the model hit the configured limit.
|
|
425
|
-
const textContent = bedrockAssistantMessage.content
|
|
426
|
-
.filter((item) => item.text)
|
|
427
|
-
.map((item) => item.text)
|
|
428
|
-
.join(" ");
|
|
429
|
-
return { shouldContinue: false, text: textContent };
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
logger.warn(`[AmazonBedrockProvider] Unrecognized stop reason "${stopReason}", ending conversation.`);
|
|
433
|
-
return { shouldContinue: false, text: "" };
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
convertToAWSMessages(bedrockMessages) {
|
|
437
|
-
return bedrockMessages.map((msg) => ({
|
|
438
|
-
role: msg.role,
|
|
439
|
-
content: msg.content.map((item) => {
|
|
440
|
-
if (item.text) {
|
|
441
|
-
return {
|
|
442
|
-
text: item.text,
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
if (item.image) {
|
|
446
|
-
return {
|
|
447
|
-
image: item.image,
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
if (item.document) {
|
|
451
|
-
return {
|
|
452
|
-
document: item.document,
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
if (item.toolUse) {
|
|
456
|
-
return {
|
|
457
|
-
toolUse: {
|
|
458
|
-
toolUseId: item.toolUse.toolUseId,
|
|
459
|
-
name: item.toolUse.name,
|
|
460
|
-
input: item.toolUse.input,
|
|
461
|
-
},
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
if (item.toolResult) {
|
|
465
|
-
return {
|
|
466
|
-
toolResult: {
|
|
467
|
-
toolUseId: item.toolResult.toolUseId,
|
|
468
|
-
content: item.toolResult.content,
|
|
469
|
-
status: item.toolResult.status,
|
|
470
|
-
},
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
return { text: "" };
|
|
474
|
-
}),
|
|
475
|
-
}));
|
|
476
|
-
}
|
|
477
|
-
async executeSingleTool(toolName, args, _toolUseId) {
|
|
478
|
-
return bedrockTracer.startActiveSpan("bedrock.tool.execute", {
|
|
479
|
-
kind: SpanKind.CLIENT,
|
|
480
|
-
attributes: {
|
|
481
|
-
"gen_ai.tool.name": toolName,
|
|
482
|
-
"gen_ai.system": "aws.bedrock",
|
|
483
|
-
},
|
|
484
|
-
}, async (span) => {
|
|
485
|
-
try {
|
|
486
|
-
logger.debug(`[AmazonBedrockProvider] Executing single tool: ${toolName}`, {
|
|
487
|
-
args,
|
|
488
|
-
});
|
|
489
|
-
// Use BaseProvider's tool execution mechanism
|
|
490
|
-
const aiTools = await this.getAllTools();
|
|
491
|
-
const tools = this.convertAISDKToolsToToolDefinitions(aiTools);
|
|
492
|
-
if (!tools[toolName]) {
|
|
493
|
-
throw new Error(`Tool not found: ${toolName}`);
|
|
494
|
-
}
|
|
495
|
-
const tool = tools[toolName];
|
|
496
|
-
if (!tool || !tool.execute) {
|
|
497
|
-
throw new Error(`Tool ${toolName} does not have execute method`);
|
|
498
|
-
}
|
|
499
|
-
// Apply robust parameter handling like Bedrock-MCP-Connector
|
|
500
|
-
// Bedrock toolUse.input already contains the correct parameter structure
|
|
501
|
-
const toolInput = args || {};
|
|
502
|
-
// Add default parameters for common tools that Claude might call without required params
|
|
503
|
-
if (toolName === "list_directory" && !toolInput.path) {
|
|
504
|
-
toolInput.path = ".";
|
|
505
|
-
logger.debug(`[AmazonBedrockProvider] Added default path '.' for list_directory tool`);
|
|
506
|
-
}
|
|
507
|
-
logger.debug(`[AmazonBedrockProvider] Tool input parameters:`, toolInput);
|
|
508
|
-
// Convert Record<string, unknown> to ToolArgs by filtering out non-JsonValue types
|
|
509
|
-
const toolArgs = {};
|
|
510
|
-
for (const [key, value] of Object.entries(toolInput)) {
|
|
511
|
-
// Only include values that are JsonValue compatible
|
|
512
|
-
if (value === null ||
|
|
513
|
-
typeof value === "string" ||
|
|
514
|
-
typeof value === "number" ||
|
|
515
|
-
typeof value === "boolean" ||
|
|
516
|
-
(typeof value === "object" && value !== null)) {
|
|
517
|
-
toolArgs[key] = value;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
const result = await tool.execute(toolArgs);
|
|
521
|
-
logger.debug(`[AmazonBedrockProvider] Tool execution result:`, {
|
|
522
|
-
toolName,
|
|
523
|
-
result,
|
|
524
|
-
});
|
|
525
|
-
// Handle ToolResult type
|
|
526
|
-
let finalResult;
|
|
527
|
-
if (result && typeof result === "object" && "success" in result) {
|
|
528
|
-
if (result.success && result.data !== undefined) {
|
|
529
|
-
if (typeof result.data === "string") {
|
|
530
|
-
finalResult = result.data;
|
|
531
|
-
}
|
|
532
|
-
else if (typeof result.data === "object") {
|
|
533
|
-
finalResult = JSON.stringify(result.data, null, 2);
|
|
534
|
-
}
|
|
535
|
-
else {
|
|
536
|
-
finalResult = String(result.data);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
else if (result.error) {
|
|
540
|
-
const errorMessage = typeof result.error === "string"
|
|
541
|
-
? result.error
|
|
542
|
-
: result.error.message || "Tool execution failed";
|
|
543
|
-
throw new Error(errorMessage);
|
|
544
|
-
}
|
|
545
|
-
else {
|
|
546
|
-
finalResult = "";
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
else if (typeof result === "string") {
|
|
550
|
-
// Fallback for non-ToolResult return types
|
|
551
|
-
finalResult = result;
|
|
552
|
-
}
|
|
553
|
-
else if (typeof result === "object") {
|
|
554
|
-
finalResult = JSON.stringify(result, null, 2);
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
finalResult = String(result);
|
|
558
|
-
}
|
|
559
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
560
|
-
return finalResult;
|
|
561
|
-
}
|
|
562
|
-
catch (error) {
|
|
563
|
-
logger.error(`[AmazonBedrockProvider] Tool execution error:`, {
|
|
564
|
-
toolName,
|
|
565
|
-
error,
|
|
566
|
-
});
|
|
567
|
-
span.setStatus({
|
|
568
|
-
code: SpanStatusCode.ERROR,
|
|
569
|
-
message: error.message,
|
|
570
|
-
});
|
|
571
|
-
span.recordException(error);
|
|
572
|
-
throw error;
|
|
573
|
-
}
|
|
574
|
-
finally {
|
|
575
|
-
span.end();
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
convertAISDKToolsToToolDefinitions(aiTools) {
|
|
580
|
-
const result = {};
|
|
581
|
-
for (const [name, tool] of Object.entries(aiTools)) {
|
|
582
|
-
if ("description" in tool && tool.description) {
|
|
583
|
-
// Extract schema from legacy `parameters` (AI SDK v3/v4) or current `inputSchema` (v6)
|
|
584
|
-
const legacyTool = tool;
|
|
585
|
-
const extractedParams = legacyTool.parameters ??
|
|
586
|
-
tool.inputSchema;
|
|
587
|
-
result[name] = {
|
|
588
|
-
description: tool.description,
|
|
589
|
-
parameters: extractedParams,
|
|
590
|
-
execute: async (params) => {
|
|
591
|
-
if ("execute" in tool && tool.execute) {
|
|
592
|
-
const result = await tool.execute(params, {
|
|
593
|
-
toolCallId: `tool_${Date.now()}`,
|
|
594
|
-
messages: [],
|
|
595
|
-
});
|
|
596
|
-
return {
|
|
597
|
-
success: true,
|
|
598
|
-
data: result,
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
throw new Error(`Tool ${name} has no execute method`);
|
|
602
|
-
},
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
return result;
|
|
607
|
-
}
|
|
608
|
-
formatToolsForBedrock(tools) {
|
|
609
|
-
if (!tools || Object.keys(tools).length === 0) {
|
|
610
|
-
return null;
|
|
611
|
-
}
|
|
612
|
-
const bedrockTools = Object.entries(tools).map(([name, tool]) => {
|
|
613
|
-
// Handle Zod schema or plain object schema
|
|
614
|
-
let schema;
|
|
615
|
-
if (tool.parameters && typeof tool.parameters === "object") {
|
|
616
|
-
// Check if it's a Zod schema
|
|
617
|
-
if ("_def" in tool.parameters) {
|
|
618
|
-
// It's a Zod schema, convert to JSON schema
|
|
619
|
-
schema = convertZodToJsonSchema(tool.parameters);
|
|
620
|
-
}
|
|
621
|
-
else {
|
|
622
|
-
// It's already a plain object schema
|
|
623
|
-
schema = tool.parameters;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
else {
|
|
627
|
-
schema = {
|
|
628
|
-
type: "object",
|
|
629
|
-
properties: {},
|
|
630
|
-
required: [],
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
// Ensure the schema always has type: "object" at the root level
|
|
634
|
-
if (!schema.type || schema.type !== "object") {
|
|
635
|
-
schema = {
|
|
636
|
-
type: "object",
|
|
637
|
-
properties: schema.properties || {},
|
|
638
|
-
required: schema.required || [],
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
const toolSpec = {
|
|
642
|
-
name,
|
|
643
|
-
description: tool.description,
|
|
644
|
-
inputSchema: {
|
|
645
|
-
json: schema,
|
|
646
|
-
},
|
|
647
|
-
};
|
|
648
|
-
return {
|
|
649
|
-
toolSpec,
|
|
650
|
-
};
|
|
651
|
-
});
|
|
652
|
-
logger.debug(`[AmazonBedrockProvider] Formatted ${bedrockTools.length} tools for Bedrock`);
|
|
653
|
-
return { tools: bedrockTools };
|
|
654
|
-
}
|
|
655
|
-
// Convert multimodal messages to Bedrock format
|
|
656
|
-
convertToBedrockMessages(messages) {
|
|
657
|
-
return messages.map((msg) => {
|
|
658
|
-
const bedrockMessage = {
|
|
659
|
-
role: msg.role === "system" ? "user" : msg.role,
|
|
660
|
-
content: [],
|
|
661
|
-
};
|
|
662
|
-
if (typeof msg.content === "string") {
|
|
663
|
-
bedrockMessage.content.push({ text: msg.content });
|
|
664
|
-
}
|
|
665
|
-
else {
|
|
666
|
-
msg.content.forEach((contentItem) => {
|
|
667
|
-
if (contentItem.type === "text" && contentItem.text) {
|
|
668
|
-
bedrockMessage.content.push({ text: contentItem.text });
|
|
669
|
-
}
|
|
670
|
-
else if (contentItem.type === "image" && contentItem.image) {
|
|
671
|
-
const imageData = typeof contentItem.image === "string"
|
|
672
|
-
? Buffer.from(contentItem.image.replace(/^data:image\/\w+;base64,/, ""), "base64")
|
|
673
|
-
: contentItem.image;
|
|
674
|
-
let format = contentItem.mimeType?.split("/")[1] || "png";
|
|
675
|
-
if (format === "jpg") {
|
|
676
|
-
format = "jpeg";
|
|
677
|
-
}
|
|
678
|
-
bedrockMessage.content.push({
|
|
679
|
-
image: {
|
|
680
|
-
format: format === "jpeg"
|
|
681
|
-
? ImageFormat.JPEG
|
|
682
|
-
: format === "png"
|
|
683
|
-
? ImageFormat.PNG
|
|
684
|
-
: format === "gif"
|
|
685
|
-
? ImageFormat.GIF
|
|
686
|
-
: ImageFormat.WEBP,
|
|
687
|
-
source: {
|
|
688
|
-
bytes: imageData,
|
|
689
|
-
},
|
|
690
|
-
},
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
else if (contentItem.type === "document" ||
|
|
694
|
-
contentItem.type === "pdf" ||
|
|
695
|
-
(contentItem.type === "file" &&
|
|
696
|
-
contentItem.mimeType?.toLowerCase().startsWith("application/pdf"))) {
|
|
697
|
-
let docData;
|
|
698
|
-
if (typeof contentItem.data === "string") {
|
|
699
|
-
const pdfString = contentItem.data.replace(/^data:application\/pdf;base64,/i, "");
|
|
700
|
-
docData = Buffer.from(pdfString, "base64");
|
|
701
|
-
}
|
|
702
|
-
else {
|
|
703
|
-
docData = contentItem.data;
|
|
704
|
-
}
|
|
705
|
-
// Extract basename and sanitize for Bedrock's filename requirements
|
|
706
|
-
// Bedrock only allows: alphanumeric, whitespace, hyphens, parentheses, brackets
|
|
707
|
-
// NOTE: Periods (.) are NOT allowed, so we remove the extension
|
|
708
|
-
let filename = typeof contentItem.name === "string" && contentItem.name
|
|
709
|
-
? path.basename(contentItem.name)
|
|
710
|
-
: "document-pdf";
|
|
711
|
-
// Remove file extension
|
|
712
|
-
filename = filename.replace(/\.[^.]+$/, "");
|
|
713
|
-
// Replace all disallowed characters with hyphens
|
|
714
|
-
// Bedrock constraint: only alphanumeric, whitespace, hyphens, parentheses, brackets allowed
|
|
715
|
-
filename = filename.replace(/[^a-zA-Z0-9\s\-()[\]]/g, "-");
|
|
716
|
-
// Clean up: remove multiple consecutive hyphens and trim
|
|
717
|
-
filename = filename
|
|
718
|
-
.replace(/-+/g, "-")
|
|
719
|
-
.trim()
|
|
720
|
-
.replace(/^-+|-+$/g, "");
|
|
721
|
-
// Fallback if filename becomes empty after sanitization
|
|
722
|
-
filename = filename || "document";
|
|
723
|
-
bedrockMessage.content.push({
|
|
724
|
-
document: {
|
|
725
|
-
format: "pdf",
|
|
726
|
-
name: filename,
|
|
727
|
-
source: {
|
|
728
|
-
bytes: docData,
|
|
729
|
-
},
|
|
730
|
-
},
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
return bedrockMessage;
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
// Bedrock-MCP-Connector compatibility
|
|
739
|
-
getBedrockClient() {
|
|
740
|
-
return this.bedrockClient;
|
|
741
|
-
}
|
|
742
|
-
async executeStream(options) {
|
|
743
|
-
logger.debug("🟢 [TRACE] executeStream ENTRY - starting streaming attempt");
|
|
744
|
-
logger.info("🚀 [AmazonBedrockProvider] Attempting real streaming with ConverseStreamCommand");
|
|
745
|
-
return bedrockTracer.startActiveSpan("bedrock.stream", {
|
|
746
|
-
kind: SpanKind.CLIENT,
|
|
747
|
-
attributes: {
|
|
748
|
-
"gen_ai.system": "aws.bedrock",
|
|
749
|
-
"gen_ai.request.model": this.modelName || this.getDefaultModel(),
|
|
750
|
-
"gen_ai.operation.name": "stream",
|
|
751
|
-
},
|
|
752
|
-
}, async (streamSpan) => {
|
|
753
|
-
try {
|
|
754
|
-
logger.debug("🟢 [TRACE] executeStream TRY block - about to call streamingConversationLoop");
|
|
755
|
-
// Clear conversation history for new streaming session
|
|
756
|
-
this.conversationHistory = [];
|
|
757
|
-
// Check for multimodal input (images, PDFs, CSVs, files)
|
|
758
|
-
const hasMultimodalInput = !!(options.input?.images?.length ||
|
|
759
|
-
options.input?.content?.length ||
|
|
760
|
-
options.input?.files?.length ||
|
|
761
|
-
options.input?.csvFiles?.length ||
|
|
762
|
-
options.input?.pdfFiles?.length);
|
|
763
|
-
if (hasMultimodalInput) {
|
|
764
|
-
logger.debug(`[AmazonBedrockProvider] Detected multimodal input, using multimodal message builder`, {
|
|
765
|
-
hasImages: !!options.input?.images?.length,
|
|
766
|
-
imageCount: options.input?.images?.length || 0,
|
|
767
|
-
hasContent: !!options.input?.content?.length,
|
|
768
|
-
contentCount: options.input?.content?.length || 0,
|
|
769
|
-
hasFiles: !!options.input?.files?.length,
|
|
770
|
-
fileCount: options.input?.files?.length || 0,
|
|
771
|
-
hasCSVFiles: !!options.input?.csvFiles?.length,
|
|
772
|
-
csvFileCount: options.input?.csvFiles?.length || 0,
|
|
773
|
-
hasPDFFiles: !!options.input?.pdfFiles?.length,
|
|
774
|
-
pdfFileCount: options.input?.pdfFiles?.length || 0,
|
|
775
|
-
});
|
|
776
|
-
const multimodalOptions = buildMultimodalOptions(options, this.providerName, this.modelName);
|
|
777
|
-
const multimodalMessages = await buildMultimodalMessagesArray(multimodalOptions, this.providerName, this.modelName);
|
|
778
|
-
// Convert to Bedrock format
|
|
779
|
-
this.conversationHistory =
|
|
780
|
-
this.convertToBedrockMessages(multimodalMessages);
|
|
781
|
-
}
|
|
782
|
-
else {
|
|
783
|
-
logger.debug(`[AmazonBedrockProvider] Text-only input, using simple message builder`);
|
|
784
|
-
// Add user message to conversation - simple text-only case
|
|
785
|
-
const userMessage = {
|
|
786
|
-
role: "user",
|
|
787
|
-
content: [{ text: options.input.text }],
|
|
788
|
-
};
|
|
789
|
-
this.conversationHistory.push(userMessage);
|
|
790
|
-
}
|
|
791
|
-
logger.debug(`[AmazonBedrockProvider] Starting streaming conversation with ${this.conversationHistory.length} message(s)`);
|
|
792
|
-
// Call the actual streaming implementation that already exists
|
|
793
|
-
logger.debug("🟢 [TRACE] executeStream - calling streamingConversationLoop NOW");
|
|
794
|
-
const result = await this.streamingConversationLoop(options, streamSpan);
|
|
795
|
-
logger.debug("🟢 [TRACE] executeStream - streamingConversationLoop SUCCESS, returning result");
|
|
796
|
-
streamSpan.setStatus({ code: SpanStatusCode.OK });
|
|
797
|
-
streamSpan.end();
|
|
798
|
-
return result;
|
|
799
|
-
}
|
|
800
|
-
catch (error) {
|
|
801
|
-
logger.debug("🔴 [TRACE] executeStream CATCH - error caught from streamingConversationLoop");
|
|
802
|
-
const errorObj = error;
|
|
803
|
-
// Check if error is related to streaming permissions
|
|
804
|
-
const isPermissionError = errorObj?.name ===
|
|
805
|
-
"AccessDeniedException" ||
|
|
806
|
-
errorObj?.name ===
|
|
807
|
-
"UnauthorizedOperation" ||
|
|
808
|
-
errorObj?.message?.includes("bedrock:InvokeModelWithResponseStream") ||
|
|
809
|
-
errorObj?.message?.includes("streaming") ||
|
|
810
|
-
errorObj?.message?.includes("ConverseStream");
|
|
811
|
-
logger.debug("🔴 [TRACE] executeStream CATCH - checking if permission error");
|
|
812
|
-
logger.debug(`🔴 [TRACE] executeStream CATCH - isPermissionError=${isPermissionError}`);
|
|
813
|
-
if (isPermissionError) {
|
|
814
|
-
logger.debug("🟡 [TRACE] executeStream CATCH - PERMISSION ERROR DETECTED, starting fallback");
|
|
815
|
-
logger.warn(`[AmazonBedrockProvider] Streaming permissions not available, falling back to generate method: ${errorObj.message}`);
|
|
816
|
-
streamSpan.addEvent("stream.fallback_to_generate", {
|
|
817
|
-
reason: errorObj.message,
|
|
818
|
-
});
|
|
819
|
-
// Fallback to generate method and convert to streaming format
|
|
820
|
-
const generateResult = await this.generate({
|
|
821
|
-
prompt: options.input.text,
|
|
822
|
-
input: options.input,
|
|
823
|
-
maxTokens: options.maxTokens,
|
|
824
|
-
temperature: options.temperature,
|
|
825
|
-
systemPrompt: options.systemPrompt,
|
|
826
|
-
});
|
|
827
|
-
if (!generateResult) {
|
|
828
|
-
streamSpan.setStatus({
|
|
829
|
-
code: SpanStatusCode.ERROR,
|
|
830
|
-
message: "Generate method returned null result",
|
|
831
|
-
});
|
|
832
|
-
streamSpan.end();
|
|
833
|
-
// eslint-disable-next-line preserve-caught-error
|
|
834
|
-
throw new Error("Generate method returned null result");
|
|
835
|
-
}
|
|
836
|
-
streamSpan.setAttribute("gen_ai.response.stop_reason", "fallback_end_turn");
|
|
837
|
-
streamSpan.setStatus({ code: SpanStatusCode.OK });
|
|
838
|
-
streamSpan.end();
|
|
839
|
-
// Convert generate result to streaming format
|
|
840
|
-
const stream = new ReadableStream({
|
|
841
|
-
start(controller) {
|
|
842
|
-
// Split the response into chunks for pseudo-streaming
|
|
843
|
-
const responseText = generateResult.content || "";
|
|
844
|
-
const chunks = responseText.split(" ");
|
|
845
|
-
chunks.forEach((word, _index) => {
|
|
846
|
-
controller.enqueue({ content: word + " " });
|
|
847
|
-
});
|
|
848
|
-
controller.enqueue({ content: "" });
|
|
849
|
-
controller.close();
|
|
850
|
-
},
|
|
851
|
-
});
|
|
852
|
-
// Convert ReadableStream to AsyncIterable like streamingConversationLoop does
|
|
853
|
-
const asyncIterable = {
|
|
854
|
-
async *[Symbol.asyncIterator]() {
|
|
855
|
-
const reader = stream.getReader();
|
|
856
|
-
try {
|
|
857
|
-
while (true) {
|
|
858
|
-
const { done, value } = await reader.read();
|
|
859
|
-
if (done) {
|
|
860
|
-
break;
|
|
861
|
-
}
|
|
862
|
-
yield value;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
finally {
|
|
866
|
-
reader.releaseLock();
|
|
867
|
-
}
|
|
868
|
-
},
|
|
869
|
-
};
|
|
870
|
-
return {
|
|
871
|
-
stream: asyncIterable,
|
|
872
|
-
usage: { total: 0, input: 0, output: 0 },
|
|
873
|
-
model: this.modelName || this.getDefaultModel(),
|
|
874
|
-
provider: this.getProviderName(),
|
|
875
|
-
metadata: {
|
|
876
|
-
fallback: true,
|
|
877
|
-
},
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
// Re-throw non-permission errors
|
|
881
|
-
streamSpan.setStatus({
|
|
882
|
-
code: SpanStatusCode.ERROR,
|
|
883
|
-
message: errorObj instanceof Error ? errorObj.message : String(errorObj),
|
|
884
|
-
});
|
|
885
|
-
streamSpan.recordException(errorObj instanceof Error ? errorObj : new Error(String(errorObj)));
|
|
886
|
-
streamSpan.end();
|
|
887
|
-
throw error;
|
|
888
|
-
}
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
async streamingConversationLoop(options, streamSpan) {
|
|
892
|
-
logger.debug("🟦 [TRACE] streamingConversationLoop ENTRY");
|
|
893
|
-
const startTime = Date.now();
|
|
894
|
-
const maxIterations = options.maxSteps || DEFAULT_MAX_STEPS;
|
|
895
|
-
let iteration = 0;
|
|
896
|
-
// The REAL issue: ReadableStream errors don't bubble up to the caller
|
|
897
|
-
// So we need to make the first streaming call synchronously to test permissions
|
|
898
|
-
try {
|
|
899
|
-
logger.debug("🟦 [TRACE] streamingConversationLoop - testing first streaming call");
|
|
900
|
-
const commandInput = await this.prepareStreamCommand(options);
|
|
901
|
-
const command = new ConverseStreamCommand(commandInput);
|
|
902
|
-
logger.debug("[Observability] Bedrock streaming API request", {
|
|
903
|
-
model: commandInput.modelId,
|
|
904
|
-
messageCount: commandInput.messages?.length || 0,
|
|
905
|
-
toolCount: commandInput.toolConfig?.tools?.length || 0,
|
|
906
|
-
});
|
|
907
|
-
streamSpan.addEvent("stream.api_call", {
|
|
908
|
-
"bedrock.message_count": commandInput.messages?.length || 0,
|
|
909
|
-
"bedrock.tool_count": commandInput.toolConfig?.tools?.length || 0,
|
|
910
|
-
});
|
|
911
|
-
const streamStartTime = Date.now();
|
|
912
|
-
const response = await withTimeout(this.bedrockClient.send(command), 120_000, new Error("Bedrock streaming API call timed out"));
|
|
913
|
-
logger.debug("[Observability] Bedrock streaming API connection established", {
|
|
914
|
-
model: commandInput.modelId,
|
|
915
|
-
durationMs: Date.now() - streamStartTime,
|
|
916
|
-
hasStream: !!response.stream,
|
|
917
|
-
});
|
|
918
|
-
// Process the first response immediately to avoid waste
|
|
919
|
-
const stream = new ReadableStream({
|
|
920
|
-
start: async (controller) => {
|
|
921
|
-
logger.debug("🟦 [TRACE] streamingConversationLoop - ReadableStream start() called");
|
|
922
|
-
try {
|
|
923
|
-
// Process the first response we already have, tracking all event types
|
|
924
|
-
let firstStopReason = "";
|
|
925
|
-
if (response.stream) {
|
|
926
|
-
const firstMessageContent = [];
|
|
927
|
-
let firstText = "";
|
|
928
|
-
for await (const chunk of response.stream) {
|
|
929
|
-
if (chunk.contentBlockStart) {
|
|
930
|
-
firstMessageContent.push({});
|
|
931
|
-
}
|
|
932
|
-
if (chunk.contentBlockDelta?.delta?.text) {
|
|
933
|
-
const textDelta = chunk.contentBlockDelta.delta.text;
|
|
934
|
-
firstText += textDelta;
|
|
935
|
-
controller.enqueue({ content: textDelta });
|
|
936
|
-
}
|
|
937
|
-
if (chunk.contentBlockStart?.start?.toolUse) {
|
|
938
|
-
const currentBlock = firstMessageContent[firstMessageContent.length - 1];
|
|
939
|
-
currentBlock.toolUse = {
|
|
940
|
-
name: chunk.contentBlockStart.start.toolUse.name || "",
|
|
941
|
-
input: {},
|
|
942
|
-
toolUseId: chunk.contentBlockStart.start.toolUse.toolUseId ||
|
|
943
|
-
`tool_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
if (chunk.contentBlockDelta?.delta?.toolUse) {
|
|
947
|
-
const currentBlock = firstMessageContent[firstMessageContent.length - 1];
|
|
948
|
-
if (!currentBlock.toolUse) {
|
|
949
|
-
currentBlock.toolUse = {
|
|
950
|
-
name: "",
|
|
951
|
-
input: {},
|
|
952
|
-
toolUseId: `tool_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
const deltaInput = chunk.contentBlockDelta.delta.toolUse.input;
|
|
956
|
-
if (!deltaInput) {
|
|
957
|
-
// no input delta
|
|
958
|
-
}
|
|
959
|
-
else if (typeof deltaInput === "string") {
|
|
960
|
-
currentBlock._inputBuffer =
|
|
961
|
-
(currentBlock._inputBuffer || "") + deltaInput;
|
|
962
|
-
}
|
|
963
|
-
else if (typeof deltaInput === "object" &&
|
|
964
|
-
!Array.isArray(deltaInput)) {
|
|
965
|
-
const currentInput = currentBlock.toolUse.input || {};
|
|
966
|
-
currentBlock.toolUse.input = {
|
|
967
|
-
...currentInput,
|
|
968
|
-
...deltaInput,
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
if (chunk.contentBlockStop) {
|
|
973
|
-
const currentBlock = firstMessageContent[firstMessageContent.length - 1];
|
|
974
|
-
if (currentBlock?.toolUse && currentBlock._inputBuffer) {
|
|
975
|
-
try {
|
|
976
|
-
currentBlock.toolUse.input = JSON.parse(currentBlock._inputBuffer);
|
|
977
|
-
}
|
|
978
|
-
catch {
|
|
979
|
-
currentBlock.toolUse.input = {};
|
|
980
|
-
}
|
|
981
|
-
delete currentBlock._inputBuffer;
|
|
982
|
-
}
|
|
983
|
-
if (firstText && currentBlock && !currentBlock.toolUse) {
|
|
984
|
-
currentBlock.text = firstText;
|
|
985
|
-
}
|
|
986
|
-
firstText = "";
|
|
987
|
-
}
|
|
988
|
-
if (chunk.messageStop) {
|
|
989
|
-
firstStopReason = chunk.messageStop.stopReason || "end_turn";
|
|
990
|
-
break;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
// Add first assistant message to conversation history
|
|
994
|
-
const firstAssistantMessage = {
|
|
995
|
-
role: "assistant",
|
|
996
|
-
content: firstMessageContent,
|
|
997
|
-
};
|
|
998
|
-
this.conversationHistory.push(firstAssistantMessage);
|
|
999
|
-
streamSpan.addEvent("stream.turn_complete", {
|
|
1000
|
-
iteration: 0,
|
|
1001
|
-
stop_reason: firstStopReason,
|
|
1002
|
-
});
|
|
1003
|
-
if (firstStopReason === "tool_use") {
|
|
1004
|
-
const toolNames = firstMessageContent
|
|
1005
|
-
.filter((b) => b.toolUse?.name)
|
|
1006
|
-
.map((b) => b.toolUse.name)
|
|
1007
|
-
.join(", ");
|
|
1008
|
-
streamSpan.addEvent("stream.tool_use", {
|
|
1009
|
-
iteration: 0,
|
|
1010
|
-
tool_names: toolNames,
|
|
1011
|
-
});
|
|
1012
|
-
}
|
|
1013
|
-
// Handle the stop reason from the first response
|
|
1014
|
-
const shouldContinue = await this.handleStreamStopReason(firstStopReason, firstAssistantMessage, controller, options);
|
|
1015
|
-
if (!shouldContinue) {
|
|
1016
|
-
streamSpan.setAttribute("gen_ai.response.stop_reason", firstStopReason);
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
// Continue with normal iterations if needed
|
|
1021
|
-
while (iteration < maxIterations) {
|
|
1022
|
-
iteration++;
|
|
1023
|
-
logger.debug(`[AmazonBedrockProvider] Streaming iteration ${iteration}`);
|
|
1024
|
-
const commandInput = await this.prepareStreamCommand(options);
|
|
1025
|
-
const { stopReason, assistantMessage } = await this.processStreamResponse(commandInput, controller);
|
|
1026
|
-
streamSpan.addEvent("stream.turn_complete", {
|
|
1027
|
-
iteration,
|
|
1028
|
-
stop_reason: stopReason,
|
|
1029
|
-
});
|
|
1030
|
-
if (stopReason === "tool_use") {
|
|
1031
|
-
const toolNames = assistantMessage.content
|
|
1032
|
-
.filter((b) => b.toolUse?.name)
|
|
1033
|
-
.map((b) => b.toolUse.name)
|
|
1034
|
-
.join(", ");
|
|
1035
|
-
streamSpan.addEvent("stream.tool_use", {
|
|
1036
|
-
iteration,
|
|
1037
|
-
tool_names: toolNames,
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
const shouldContinue = await this.handleStreamStopReason(stopReason, assistantMessage, controller, options);
|
|
1041
|
-
if (!shouldContinue) {
|
|
1042
|
-
streamSpan.setAttribute("gen_ai.response.stop_reason", stopReason);
|
|
1043
|
-
break;
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
if (iteration >= maxIterations) {
|
|
1047
|
-
streamSpan.setAttribute("gen_ai.response.stop_reason", "max_iterations");
|
|
1048
|
-
controller.error(new Error("Streaming conversation exceeded maximum iterations"));
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
catch (error) {
|
|
1052
|
-
logger.debug("🔴 [TRACE] streamingConversationLoop - CATCH block hit in ReadableStream");
|
|
1053
|
-
controller.error(error);
|
|
1054
|
-
}
|
|
1055
|
-
},
|
|
1056
|
-
});
|
|
1057
|
-
// Create analytics promise (without token tracking for now due to AWS SDK limitations)
|
|
1058
|
-
const analyticsPromise = Promise.resolve(createAnalytics(this.providerName, this.modelName || this.getDefaultModel(), { usage: { input: 0, output: 0, total: 0 } }, Date.now() - startTime, {
|
|
1059
|
-
requestId: `bedrock-stream-${Date.now()}`,
|
|
1060
|
-
streamingMode: true,
|
|
1061
|
-
note: "Token usage not available from AWS SDK streaming responses",
|
|
1062
|
-
}));
|
|
1063
|
-
return {
|
|
1064
|
-
stream: this.convertToAsyncIterable(stream),
|
|
1065
|
-
usage: { total: 0, input: 0, output: 0 },
|
|
1066
|
-
model: this.modelName || this.getDefaultModel(),
|
|
1067
|
-
provider: this.getProviderName(),
|
|
1068
|
-
analytics: analyticsPromise,
|
|
1069
|
-
metadata: {
|
|
1070
|
-
startTime,
|
|
1071
|
-
streamId: `bedrock-${Date.now()}`,
|
|
1072
|
-
},
|
|
1073
|
-
};
|
|
1074
|
-
}
|
|
1075
|
-
catch (error) {
|
|
1076
|
-
logger.debug("🔴 [TRACE] streamingConversationLoop - first streaming call FAILED, throwing");
|
|
1077
|
-
throw error; // This will be caught by executeStream
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
convertToAsyncIterable(stream) {
|
|
1081
|
-
return {
|
|
1082
|
-
async *[Symbol.asyncIterator]() {
|
|
1083
|
-
const reader = stream.getReader();
|
|
1084
|
-
try {
|
|
1085
|
-
while (true) {
|
|
1086
|
-
const { done, value } = await reader.read();
|
|
1087
|
-
if (done) {
|
|
1088
|
-
break;
|
|
1089
|
-
}
|
|
1090
|
-
yield value;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
finally {
|
|
1094
|
-
reader.releaseLock();
|
|
1095
|
-
}
|
|
1096
|
-
},
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
async prepareStreamCommand(options) {
|
|
1100
|
-
// CRITICAL DEBUG: Log conversation history before conversion
|
|
1101
|
-
if (logger.shouldLog("debug")) {
|
|
1102
|
-
logger.debug(`[AmazonBedrockProvider] BEFORE conversion - conversationHistory length: ${this.conversationHistory.length}`);
|
|
1103
|
-
this.conversationHistory.forEach((msg, index) => {
|
|
1104
|
-
logger.debug(`[AmazonBedrockProvider] Message ${index}: role=${msg.role}, content=${JSON.stringify(msg.content)}`);
|
|
1105
|
-
});
|
|
1106
|
-
}
|
|
1107
|
-
// Get all available tools
|
|
1108
|
-
// BaseProvider.stream() pre-merges base tools + external tools into options.tools
|
|
1109
|
-
const aiTools = options.tools ||
|
|
1110
|
-
(await this.getAllTools());
|
|
1111
|
-
const allTools = this.convertAISDKToolsToToolDefinitions(aiTools);
|
|
1112
|
-
const toolConfig = this.formatToolsForBedrock(allTools);
|
|
1113
|
-
const convertedMessages = this.convertToAWSMessages(this.conversationHistory);
|
|
1114
|
-
if (logger.shouldLog("debug")) {
|
|
1115
|
-
logger.debug(`[AmazonBedrockProvider] AFTER conversion - messages length: ${convertedMessages.length}`);
|
|
1116
|
-
convertedMessages.forEach((msg, index) => {
|
|
1117
|
-
logger.debug(`[AmazonBedrockProvider] Converted Message ${index}: role=${msg.role}, content=${JSON.stringify(msg.content)}`);
|
|
1118
|
-
});
|
|
1119
|
-
}
|
|
1120
|
-
const commandInput = {
|
|
1121
|
-
modelId: this.modelName || this.getDefaultModel(),
|
|
1122
|
-
messages: convertedMessages,
|
|
1123
|
-
system: [
|
|
1124
|
-
{
|
|
1125
|
-
text: options.systemPrompt ||
|
|
1126
|
-
"You are a helpful assistant with access to external tools. Use tools when necessary to provide accurate information.",
|
|
1127
|
-
},
|
|
1128
|
-
],
|
|
1129
|
-
inferenceConfig: {
|
|
1130
|
-
maxTokens: options.maxTokens, // No default limit - unlimited unless specified
|
|
1131
|
-
temperature: options.temperature || 0.7,
|
|
1132
|
-
},
|
|
1133
|
-
};
|
|
1134
|
-
if (toolConfig) {
|
|
1135
|
-
commandInput.toolConfig = toolConfig;
|
|
1136
|
-
}
|
|
1137
|
-
logger.debug(`[AmazonBedrockProvider] Calling Bedrock streaming with ${this.conversationHistory.length} messages`);
|
|
1138
|
-
// DEBUG: Log exact conversation structure being sent to Bedrock
|
|
1139
|
-
logger.debug(`[AmazonBedrockProvider] DEBUG - Conversation structure:`);
|
|
1140
|
-
this.conversationHistory.forEach((msg, index) => {
|
|
1141
|
-
logger.debug(` Message ${index} (${msg.role}): ${msg.content.length} content items`);
|
|
1142
|
-
msg.content.forEach((item, itemIndex) => {
|
|
1143
|
-
const keys = Object.keys(item);
|
|
1144
|
-
logger.debug(` Content ${itemIndex}: ${keys.join(", ")}`);
|
|
1145
|
-
});
|
|
1146
|
-
});
|
|
1147
|
-
return commandInput;
|
|
1148
|
-
}
|
|
1149
|
-
async processStreamResponse(commandInput, controller) {
|
|
1150
|
-
const command = new ConverseStreamCommand(commandInput);
|
|
1151
|
-
logger.debug("[Observability] Bedrock streaming API request (continuation)", {
|
|
1152
|
-
model: commandInput.modelId,
|
|
1153
|
-
messageCount: commandInput.messages?.length || 0,
|
|
1154
|
-
});
|
|
1155
|
-
const iterationStartTime = Date.now();
|
|
1156
|
-
const response = await withTimeout(this.bedrockClient.send(command), 120_000, new Error("Bedrock streaming API call timed out"));
|
|
1157
|
-
logger.debug("[Observability] Bedrock streaming API connection established (continuation)", {
|
|
1158
|
-
model: commandInput.modelId,
|
|
1159
|
-
durationMs: Date.now() - iterationStartTime,
|
|
1160
|
-
});
|
|
1161
|
-
if (!response.stream) {
|
|
1162
|
-
throw new Error("No stream returned from Bedrock");
|
|
1163
|
-
}
|
|
1164
|
-
const currentMessageContent = [];
|
|
1165
|
-
let stopReason = "";
|
|
1166
|
-
let currentText = "";
|
|
1167
|
-
// Process streaming chunks
|
|
1168
|
-
for await (const chunk of response.stream) {
|
|
1169
|
-
if (chunk.contentBlockStart) {
|
|
1170
|
-
// Starting a new content block
|
|
1171
|
-
currentMessageContent.push({});
|
|
1172
|
-
}
|
|
1173
|
-
if (chunk.contentBlockDelta?.delta?.text) {
|
|
1174
|
-
// Text delta - stream it to user
|
|
1175
|
-
const textDelta = chunk.contentBlockDelta.delta.text;
|
|
1176
|
-
currentText += textDelta;
|
|
1177
|
-
controller.enqueue({
|
|
1178
|
-
content: textDelta,
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
if (chunk.contentBlockStart?.start?.toolUse) {
|
|
1182
|
-
// Tool use block starting - initialize tool information
|
|
1183
|
-
const currentBlock = currentMessageContent[currentMessageContent.length - 1];
|
|
1184
|
-
currentBlock.toolUse = {
|
|
1185
|
-
name: chunk.contentBlockStart.start.toolUse.name || "",
|
|
1186
|
-
input: {}, // Initialize empty - will be populated by delta chunks
|
|
1187
|
-
toolUseId: chunk.contentBlockStart.start.toolUse.toolUseId ||
|
|
1188
|
-
`tool_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
if (chunk.contentBlockDelta?.delta?.toolUse) {
|
|
1192
|
-
// Tool use delta - accumulate tool information
|
|
1193
|
-
const currentBlock = currentMessageContent[currentMessageContent.length - 1];
|
|
1194
|
-
if (!currentBlock.toolUse) {
|
|
1195
|
-
currentBlock.toolUse = {
|
|
1196
|
-
name: "",
|
|
1197
|
-
input: {},
|
|
1198
|
-
toolUseId: `tool_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
|
|
1199
|
-
};
|
|
1200
|
-
}
|
|
1201
|
-
// Accumulate JSON string fragments into _inputBuffer.
|
|
1202
|
-
// Bedrock sends toolUse.input as incremental JSON string fragments,
|
|
1203
|
-
// not pre-parsed objects. We buffer them and parse at contentBlockStop.
|
|
1204
|
-
if (chunk.contentBlockDelta.delta.toolUse.input) {
|
|
1205
|
-
const deltaInput = chunk.contentBlockDelta.delta.toolUse.input;
|
|
1206
|
-
if (typeof deltaInput === "string") {
|
|
1207
|
-
currentBlock._inputBuffer =
|
|
1208
|
-
(currentBlock._inputBuffer || "") + deltaInput;
|
|
1209
|
-
}
|
|
1210
|
-
else if (deltaInput &&
|
|
1211
|
-
typeof deltaInput === "object" &&
|
|
1212
|
-
!Array.isArray(deltaInput)) {
|
|
1213
|
-
// Some SDK versions may deliver pre-parsed objects; merge directly
|
|
1214
|
-
const currentInput = currentBlock.toolUse.input || {};
|
|
1215
|
-
currentBlock.toolUse.input = {
|
|
1216
|
-
...currentInput,
|
|
1217
|
-
...deltaInput,
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
if (chunk.contentBlockStop) {
|
|
1223
|
-
// Content block completed
|
|
1224
|
-
const currentBlock = currentMessageContent[currentMessageContent.length - 1];
|
|
1225
|
-
// Parse accumulated JSON input buffer for tool-use blocks
|
|
1226
|
-
if (currentBlock?.toolUse && currentBlock._inputBuffer) {
|
|
1227
|
-
try {
|
|
1228
|
-
currentBlock.toolUse.input = JSON.parse(currentBlock._inputBuffer);
|
|
1229
|
-
}
|
|
1230
|
-
catch {
|
|
1231
|
-
currentBlock.toolUse.input = {};
|
|
1232
|
-
}
|
|
1233
|
-
delete currentBlock._inputBuffer;
|
|
1234
|
-
}
|
|
1235
|
-
if (currentText && currentBlock && !currentBlock.toolUse) {
|
|
1236
|
-
// Only add text to blocks that don't have toolUse
|
|
1237
|
-
currentBlock.text = currentText;
|
|
1238
|
-
}
|
|
1239
|
-
currentText = "";
|
|
1240
|
-
}
|
|
1241
|
-
if (chunk.messageStop) {
|
|
1242
|
-
stopReason = chunk.messageStop.stopReason || "end_turn";
|
|
1243
|
-
break;
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
// Add assistant message to conversation history
|
|
1247
|
-
const assistantMessage = {
|
|
1248
|
-
role: "assistant",
|
|
1249
|
-
content: currentMessageContent,
|
|
1250
|
-
};
|
|
1251
|
-
this.conversationHistory.push(assistantMessage);
|
|
1252
|
-
return { stopReason, assistantMessage };
|
|
1253
|
-
}
|
|
1254
|
-
async handleStreamStopReason(stopReason, assistantMessage, controller, options) {
|
|
1255
|
-
if (stopReason === "end_turn" || stopReason === "stop_sequence") {
|
|
1256
|
-
// Conversation completed
|
|
1257
|
-
controller.close();
|
|
1258
|
-
return false;
|
|
1259
|
-
}
|
|
1260
|
-
else if (stopReason === "tool_use") {
|
|
1261
|
-
logger.debug(`🛠️ [AmazonBedrockProvider] Tool use detected in streaming - executing tools`);
|
|
1262
|
-
await this.executeStreamTools(assistantMessage.content, options);
|
|
1263
|
-
return true; // Continue conversation loop
|
|
1264
|
-
}
|
|
1265
|
-
else if (stopReason === "max_tokens") {
|
|
1266
|
-
// Max tokens reached — close the stream rather than continuing,
|
|
1267
|
-
// since the model hit the configured limit.
|
|
1268
|
-
controller.close();
|
|
1269
|
-
return false;
|
|
1270
|
-
}
|
|
1271
|
-
else {
|
|
1272
|
-
// Unknown stop reason - end conversation
|
|
1273
|
-
controller.close();
|
|
1274
|
-
return false;
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
async executeStreamTools(messageContent, options) {
|
|
1278
|
-
// Execute all tool uses in the message - ensure 1:1 mapping like Bedrock-MCP-Connector
|
|
1279
|
-
const toolResults = [];
|
|
1280
|
-
let toolUseCount = 0;
|
|
1281
|
-
// Track tool calls and results for storage (similar to Vertex onStepFinish)
|
|
1282
|
-
const toolCalls = [];
|
|
1283
|
-
const toolResultsForStorage = [];
|
|
1284
|
-
// Count toolUse blocks first to ensure 1:1 mapping
|
|
1285
|
-
for (const contentItem of messageContent) {
|
|
1286
|
-
if (contentItem.toolUse) {
|
|
1287
|
-
toolUseCount++;
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
logger.debug(`🔍 [AmazonBedrockProvider] Found ${toolUseCount} toolUse blocks in assistant message`);
|
|
1291
|
-
for (const contentItem of messageContent) {
|
|
1292
|
-
if (contentItem.toolUse) {
|
|
1293
|
-
logger.debug(`🔧 [AmazonBedrockProvider] Executing tool: ${contentItem.toolUse.name}`);
|
|
1294
|
-
// Track tool call
|
|
1295
|
-
toolCalls.push({
|
|
1296
|
-
type: "tool-call",
|
|
1297
|
-
toolCallId: contentItem.toolUse.toolUseId,
|
|
1298
|
-
toolName: contentItem.toolUse.name,
|
|
1299
|
-
args: contentItem.toolUse.input || {},
|
|
1300
|
-
});
|
|
1301
|
-
try {
|
|
1302
|
-
const toolResult = await this.executeSingleTool(contentItem.toolUse.name, contentItem.toolUse.input || {}, contentItem.toolUse.toolUseId);
|
|
1303
|
-
logger.debug(`✅ [AmazonBedrockProvider] Tool execution successful: ${contentItem.toolUse.name}`);
|
|
1304
|
-
// Track tool result for storage
|
|
1305
|
-
toolResultsForStorage.push({
|
|
1306
|
-
type: "tool-result",
|
|
1307
|
-
toolCallId: contentItem.toolUse.toolUseId,
|
|
1308
|
-
toolName: contentItem.toolUse.name,
|
|
1309
|
-
result: toolResult,
|
|
1310
|
-
});
|
|
1311
|
-
// Ensure exact structure matching Bedrock-MCP-Connector
|
|
1312
|
-
toolResults.push({
|
|
1313
|
-
toolResult: {
|
|
1314
|
-
toolUseId: contentItem.toolUse.toolUseId,
|
|
1315
|
-
content: [{ text: String(toolResult) }],
|
|
1316
|
-
status: "success",
|
|
1317
|
-
},
|
|
1318
|
-
});
|
|
1319
|
-
}
|
|
1320
|
-
catch (error) {
|
|
1321
|
-
logger.error(`❌ [AmazonBedrockProvider] Tool execution failed: ${contentItem.toolUse.name}`, error);
|
|
1322
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1323
|
-
// Track failed tool result
|
|
1324
|
-
toolResultsForStorage.push({
|
|
1325
|
-
type: "tool-result",
|
|
1326
|
-
toolCallId: contentItem.toolUse.toolUseId,
|
|
1327
|
-
toolName: contentItem.toolUse.name,
|
|
1328
|
-
result: { error: errorMessage },
|
|
1329
|
-
});
|
|
1330
|
-
toolResults.push({
|
|
1331
|
-
toolResult: {
|
|
1332
|
-
toolUseId: contentItem.toolUse.toolUseId,
|
|
1333
|
-
content: [
|
|
1334
|
-
{
|
|
1335
|
-
text: `Error executing tool ${contentItem.toolUse.name}: ${errorMessage}`,
|
|
1336
|
-
},
|
|
1337
|
-
],
|
|
1338
|
-
status: "error",
|
|
1339
|
-
},
|
|
1340
|
-
});
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
logger.debug(`📊 [AmazonBedrockProvider] Created ${toolResults.length} toolResult blocks for ${toolUseCount} toolUse blocks`);
|
|
1345
|
-
// Validate 1:1 mapping before adding to conversation
|
|
1346
|
-
if (toolResults.length !== toolUseCount) {
|
|
1347
|
-
logger.error(`❌ [AmazonBedrockProvider] Mismatch: ${toolResults.length} toolResults vs ${toolUseCount} toolUse blocks`);
|
|
1348
|
-
throw new Error(`Tool mapping mismatch: ${toolResults.length} toolResults for ${toolUseCount} toolUse blocks`);
|
|
1349
|
-
}
|
|
1350
|
-
// Add tool results as user message - exact structure like Bedrock-MCP-Connector
|
|
1351
|
-
if (toolResults.length > 0) {
|
|
1352
|
-
const userMessageWithToolResults = {
|
|
1353
|
-
role: "user",
|
|
1354
|
-
content: toolResults,
|
|
1355
|
-
};
|
|
1356
|
-
this.conversationHistory.push(userMessageWithToolResults);
|
|
1357
|
-
logger.debug(`📤 [AmazonBedrockProvider] Added ${toolResults.length} tool results to conversation (1:1 mapping validated)`);
|
|
1358
|
-
// Store tool execution for analytics and debugging (similar to Vertex onStepFinish)
|
|
1359
|
-
this.handleToolExecutionStorage(toolCalls, toolResultsForStorage, options, new Date()).catch((error) => {
|
|
1360
|
-
logger.warn("[AmazonBedrockProvider] Failed to store tool executions", {
|
|
1361
|
-
provider: this.providerName,
|
|
1362
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1363
|
-
});
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Health check for Amazon Bedrock service
|
|
1369
|
-
* Uses ListFoundationModels API to validate connectivity and permissions
|
|
1370
|
-
*/
|
|
1371
|
-
async checkBedrockHealth() {
|
|
1372
|
-
const controller = new AbortController();
|
|
1373
|
-
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
|
|
1374
|
-
// Create a separate BedrockClient for health checks (not BedrockRuntimeClient)
|
|
1375
|
-
// Use simple configuration like working example - no custom proxy handler
|
|
1376
|
-
const healthCheckClient = new BedrockClient({
|
|
1377
|
-
region: process.env.AWS_REGION || "us-east-1",
|
|
1378
|
-
});
|
|
1379
|
-
try {
|
|
1380
|
-
logger.debug("🔍 [AmazonBedrockProvider] Starting health check...");
|
|
1381
|
-
const command = new ListFoundationModelsCommand({});
|
|
1382
|
-
const response = await healthCheckClient.send(command, {
|
|
1383
|
-
abortSignal: controller.signal,
|
|
1384
|
-
});
|
|
1385
|
-
const models = response.modelSummaries || [];
|
|
1386
|
-
const activeModels = models.filter((model) => model.modelLifecycle?.status === "ACTIVE");
|
|
1387
|
-
logger.debug(`✅ [AmazonBedrockProvider] Health check passed - Found ${activeModels.length} active models out of ${models.length} total models`);
|
|
1388
|
-
if (activeModels.length === 0) {
|
|
1389
|
-
throw new Error("No active foundation models available in the region");
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
catch (error) {
|
|
1393
|
-
clearTimeout(timeoutId);
|
|
1394
|
-
const errorObj = error;
|
|
1395
|
-
if (isAbortError(error)) {
|
|
1396
|
-
throw new Error("Bedrock health check timed out after 10 seconds", {
|
|
1397
|
-
cause: error,
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
const errorMessage = typeof errorObj.message === "string" ? errorObj.message : "";
|
|
1401
|
-
if (errorMessage.includes("UnauthorizedOperation") ||
|
|
1402
|
-
errorMessage.includes("AccessDenied")) {
|
|
1403
|
-
throw new Error("Bedrock access denied. Check your AWS credentials and IAM permissions for bedrock:ListFoundationModels", { cause: error });
|
|
1404
|
-
}
|
|
1405
|
-
if (errorObj.code === "ECONNREFUSED" || errorObj.code === "ENOTFOUND") {
|
|
1406
|
-
throw new Error("Unable to connect to Bedrock service. Check your network connectivity and AWS region configuration", { cause: error });
|
|
1407
|
-
}
|
|
1408
|
-
logger.error("❌ [AmazonBedrockProvider] Health check failed:", error);
|
|
1409
|
-
throw new Error(`Bedrock health check failed: ${errorMessage || "Unknown error"}`, { cause: error });
|
|
1410
|
-
}
|
|
1411
|
-
finally {
|
|
1412
|
-
clearTimeout(timeoutId);
|
|
1413
|
-
try {
|
|
1414
|
-
healthCheckClient.destroy();
|
|
1415
|
-
}
|
|
1416
|
-
catch {
|
|
1417
|
-
// Ignore destroy errors during cleanup
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
formatProviderError(error) {
|
|
1422
|
-
// Handle AWS SDK specific errors
|
|
1423
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1424
|
-
if (message.includes("AccessDeniedException")) {
|
|
1425
|
-
return new AuthenticationError("AWS Bedrock access denied. Check your credentials and permissions.", this.providerName);
|
|
1426
|
-
}
|
|
1427
|
-
if (message.includes("ValidationException")) {
|
|
1428
|
-
return new ProviderError(`Validation error: ${message}`, this.providerName);
|
|
1429
|
-
}
|
|
1430
|
-
return new ProviderError(`AWS Bedrock error: ${message}`, this.providerName);
|
|
1431
|
-
}
|
|
1432
|
-
/**
|
|
1433
|
-
* Generate embeddings for text using Amazon Bedrock embedding models
|
|
1434
|
-
* Uses the native AWS SDK InvokeModel command for Titan embeddings
|
|
1435
|
-
* @param text - The text to embed
|
|
1436
|
-
* @param modelName - The embedding model to use (default: amazon.titan-embed-text-v2:0)
|
|
1437
|
-
* @returns Promise resolving to the embedding vector
|
|
1438
|
-
*/
|
|
1439
|
-
async embed(text, modelName) {
|
|
1440
|
-
const embeddingModelName = modelName || "amazon.titan-embed-text-v2:0";
|
|
1441
|
-
logger.debug("Generating embedding", {
|
|
1442
|
-
provider: this.providerName,
|
|
1443
|
-
model: embeddingModelName,
|
|
1444
|
-
textLength: text.length,
|
|
1445
|
-
});
|
|
1446
|
-
try {
|
|
1447
|
-
const { InvokeModelCommand } = await import("@aws-sdk/client-bedrock-runtime");
|
|
1448
|
-
// Titan Embed models expect a specific input format
|
|
1449
|
-
const requestBody = JSON.stringify({
|
|
1450
|
-
inputText: text,
|
|
1451
|
-
});
|
|
1452
|
-
const command = new InvokeModelCommand({
|
|
1453
|
-
modelId: embeddingModelName,
|
|
1454
|
-
contentType: "application/json",
|
|
1455
|
-
accept: "application/json",
|
|
1456
|
-
body: requestBody,
|
|
1457
|
-
});
|
|
1458
|
-
const response = await withTimeout(this.bedrockClient.send(command), 60_000, new Error("Bedrock embedding API call timed out"));
|
|
1459
|
-
// Parse the response
|
|
1460
|
-
const responseBody = JSON.parse(new TextDecoder().decode(response.body));
|
|
1461
|
-
if (!responseBody.embedding || !Array.isArray(responseBody.embedding)) {
|
|
1462
|
-
throw new Error("Invalid embedding response from Bedrock");
|
|
1463
|
-
}
|
|
1464
|
-
logger.debug("Embedding generated successfully", {
|
|
1465
|
-
provider: this.providerName,
|
|
1466
|
-
model: embeddingModelName,
|
|
1467
|
-
embeddingDimension: responseBody.embedding.length,
|
|
1468
|
-
});
|
|
1469
|
-
return responseBody.embedding;
|
|
1470
|
-
}
|
|
1471
|
-
catch (error) {
|
|
1472
|
-
logger.error("Embedding generation failed", {
|
|
1473
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1474
|
-
model: embeddingModelName,
|
|
1475
|
-
textLength: text.length,
|
|
1476
|
-
});
|
|
1477
|
-
throw this.handleProviderError(error);
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
/**
|
|
1481
|
-
* Generate embeddings for multiple texts in a single batch
|
|
1482
|
-
* @param texts - The texts to embed
|
|
1483
|
-
* @param modelName - The embedding model to use (default: amazon.titan-embed-text-v2:0)
|
|
1484
|
-
* @returns Promise resolving to an array of embedding vectors
|
|
1485
|
-
*/
|
|
1486
|
-
async embedMany(texts, modelName) {
|
|
1487
|
-
const embeddingModelName = modelName || "amazon.titan-embed-text-v2:0";
|
|
1488
|
-
logger.debug("Generating batch embeddings", {
|
|
1489
|
-
provider: this.providerName,
|
|
1490
|
-
model: embeddingModelName,
|
|
1491
|
-
count: texts.length,
|
|
1492
|
-
});
|
|
1493
|
-
try {
|
|
1494
|
-
const embeddings = await Promise.all(texts.map((text) => this.embed(text, embeddingModelName)));
|
|
1495
|
-
logger.debug("Batch embeddings generated successfully", {
|
|
1496
|
-
provider: this.providerName,
|
|
1497
|
-
model: embeddingModelName,
|
|
1498
|
-
count: embeddings.length,
|
|
1499
|
-
embeddingDimension: embeddings[0]?.length,
|
|
1500
|
-
});
|
|
1501
|
-
return embeddings;
|
|
1502
|
-
}
|
|
1503
|
-
catch (error) {
|
|
1504
|
-
logger.error("Batch embedding generation failed", {
|
|
1505
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1506
|
-
model: embeddingModelName,
|
|
1507
|
-
count: texts.length,
|
|
1508
|
-
});
|
|
1509
|
-
throw this.handleProviderError(error);
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
}
|