@nexus-cortex/core 4.26.0
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/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +13 -0
- package/dist/adapters/AdapterRegistry.d.ts +118 -0
- package/dist/adapters/AdapterRegistry.d.ts.map +1 -0
- package/dist/adapters/AdapterRegistry.js +195 -0
- package/dist/adapters/AdapterRegistry.js.map +1 -0
- package/dist/adapters/ChatCompletionsAPIAdapter.d.ts +254 -0
- package/dist/adapters/ChatCompletionsAPIAdapter.d.ts.map +1 -0
- package/dist/adapters/ChatCompletionsAPIAdapter.js +531 -0
- package/dist/adapters/ChatCompletionsAPIAdapter.js.map +1 -0
- package/dist/adapters/FormatAdapter.interface.d.ts +66 -0
- package/dist/adapters/FormatAdapter.interface.d.ts.map +1 -0
- package/dist/adapters/FormatAdapter.interface.js +89 -0
- package/dist/adapters/FormatAdapter.interface.js.map +1 -0
- package/dist/adapters/GatewayTranslationLayer.d.ts +222 -0
- package/dist/adapters/GatewayTranslationLayer.d.ts.map +1 -0
- package/dist/adapters/GatewayTranslationLayer.js +679 -0
- package/dist/adapters/GatewayTranslationLayer.js.map +1 -0
- package/dist/adapters/GenerateContentAPIAdapter.d.ts +240 -0
- package/dist/adapters/GenerateContentAPIAdapter.d.ts.map +1 -0
- package/dist/adapters/GenerateContentAPIAdapter.js +489 -0
- package/dist/adapters/GenerateContentAPIAdapter.js.map +1 -0
- package/dist/adapters/GoogleGenAPIAdapter.d.ts +217 -0
- package/dist/adapters/GoogleGenAPIAdapter.d.ts.map +1 -0
- package/dist/adapters/GoogleGenAPIAdapter.js +310 -0
- package/dist/adapters/GoogleGenAPIAdapter.js.map +1 -0
- package/dist/adapters/MessagesAPIAdapter.d.ts +280 -0
- package/dist/adapters/MessagesAPIAdapter.d.ts.map +1 -0
- package/dist/adapters/MessagesAPIAdapter.js +586 -0
- package/dist/adapters/MessagesAPIAdapter.js.map +1 -0
- package/dist/adapters/ResponsesAPIAdapter.d.ts +323 -0
- package/dist/adapters/ResponsesAPIAdapter.d.ts.map +1 -0
- package/dist/adapters/ResponsesAPIAdapter.js +584 -0
- package/dist/adapters/ResponsesAPIAdapter.js.map +1 -0
- package/dist/adapters/ServerSideToolDetection.d.ts +105 -0
- package/dist/adapters/ServerSideToolDetection.d.ts.map +1 -0
- package/dist/adapters/ServerSideToolDetection.js +249 -0
- package/dist/adapters/ServerSideToolDetection.js.map +1 -0
- package/dist/adapters/ToolNamingHandler.d.ts +129 -0
- package/dist/adapters/ToolNamingHandler.d.ts.map +1 -0
- package/dist/adapters/ToolNamingHandler.js +227 -0
- package/dist/adapters/ToolNamingHandler.js.map +1 -0
- package/dist/adapters/index.d.ts +19 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +23 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/node/NodeConfigProvider.d.ts +19 -0
- package/dist/adapters/node/NodeConfigProvider.d.ts.map +1 -0
- package/dist/adapters/node/NodeConfigProvider.js +37 -0
- package/dist/adapters/node/NodeConfigProvider.js.map +1 -0
- package/dist/adapters/node/NodeHistoryStoreAdapter.d.ts +26 -0
- package/dist/adapters/node/NodeHistoryStoreAdapter.d.ts.map +1 -0
- package/dist/adapters/node/NodeHistoryStoreAdapter.js +61 -0
- package/dist/adapters/node/NodeHistoryStoreAdapter.js.map +1 -0
- package/dist/adapters/node/NodePermissionAdapter.d.ts +21 -0
- package/dist/adapters/node/NodePermissionAdapter.d.ts.map +1 -0
- package/dist/adapters/node/NodePermissionAdapter.js +39 -0
- package/dist/adapters/node/NodePermissionAdapter.js.map +1 -0
- package/dist/adapters/node/NodeToolExecutorAdapter.d.ts +22 -0
- package/dist/adapters/node/NodeToolExecutorAdapter.d.ts.map +1 -0
- package/dist/adapters/node/NodeToolExecutorAdapter.js +33 -0
- package/dist/adapters/node/NodeToolExecutorAdapter.js.map +1 -0
- package/dist/adapters/node/index.d.ts +18 -0
- package/dist/adapters/node/index.d.ts.map +1 -0
- package/dist/adapters/node/index.js +18 -0
- package/dist/adapters/node/index.js.map +1 -0
- package/dist/agents/AgentStore.d.ts +172 -0
- package/dist/agents/AgentStore.d.ts.map +1 -0
- package/dist/agents/AgentStore.js +649 -0
- package/dist/agents/AgentStore.js.map +1 -0
- package/dist/agents/index.d.ts +9 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +8 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/projectRoot.d.ts +2 -0
- package/dist/agents/projectRoot.d.ts.map +1 -0
- package/dist/agents/projectRoot.js +37 -0
- package/dist/agents/projectRoot.js.map +1 -0
- package/dist/commands/SlashCommandCompleter.d.ts +116 -0
- package/dist/commands/SlashCommandCompleter.d.ts.map +1 -0
- package/dist/commands/SlashCommandCompleter.js +321 -0
- package/dist/commands/SlashCommandCompleter.js.map +1 -0
- package/dist/commands/SlashCommandParser.d.ts +139 -0
- package/dist/commands/SlashCommandParser.d.ts.map +1 -0
- package/dist/commands/SlashCommandParser.js +338 -0
- package/dist/commands/SlashCommandParser.js.map +1 -0
- package/dist/commands/SlashCommandRegistry.d.ts +92 -0
- package/dist/commands/SlashCommandRegistry.d.ts.map +1 -0
- package/dist/commands/SlashCommandRegistry.js +983 -0
- package/dist/commands/SlashCommandRegistry.js.map +1 -0
- package/dist/commands/index.d.ts +13 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +15 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/types.d.ts +154 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +10 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/config/AnthropicCredentialService.d.ts +107 -0
- package/dist/config/AnthropicCredentialService.d.ts.map +1 -0
- package/dist/config/AnthropicCredentialService.js +209 -0
- package/dist/config/AnthropicCredentialService.js.map +1 -0
- package/dist/config/InteractiveConfigurator.d.ts +26 -0
- package/dist/config/InteractiveConfigurator.d.ts.map +1 -0
- package/dist/config/InteractiveConfigurator.js +330 -0
- package/dist/config/InteractiveConfigurator.js.map +1 -0
- package/dist/config/MentorshipConfigService.d.ts +90 -0
- package/dist/config/MentorshipConfigService.d.ts.map +1 -0
- package/dist/config/MentorshipConfigService.js +318 -0
- package/dist/config/MentorshipConfigService.js.map +1 -0
- package/dist/config/RuntimeConfigRegistry.d.ts +20 -0
- package/dist/config/RuntimeConfigRegistry.d.ts.map +1 -0
- package/dist/config/RuntimeConfigRegistry.js +57 -0
- package/dist/config/RuntimeConfigRegistry.js.map +1 -0
- package/dist/config/SettingsLoader.d.ts +128 -0
- package/dist/config/SettingsLoader.d.ts.map +1 -0
- package/dist/config/SettingsLoader.js +487 -0
- package/dist/config/SettingsLoader.js.map +1 -0
- package/dist/config/SettingsSchema.d.ts +219 -0
- package/dist/config/SettingsSchema.d.ts.map +1 -0
- package/dist/config/SettingsSchema.js +855 -0
- package/dist/config/SettingsSchema.js.map +1 -0
- package/dist/config/SettingsWriter.d.ts +83 -0
- package/dist/config/SettingsWriter.d.ts.map +1 -0
- package/dist/config/SettingsWriter.js +256 -0
- package/dist/config/SettingsWriter.js.map +1 -0
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +13 -0
- package/dist/config/index.js.map +1 -0
- package/dist/conversation/ContextBudgetManager.d.ts +172 -0
- package/dist/conversation/ContextBudgetManager.d.ts.map +1 -0
- package/dist/conversation/ContextBudgetManager.js +445 -0
- package/dist/conversation/ContextBudgetManager.js.map +1 -0
- package/dist/conversation/StoredCompactionManager.d.ts +208 -0
- package/dist/conversation/StoredCompactionManager.d.ts.map +1 -0
- package/dist/conversation/StoredCompactionManager.js +314 -0
- package/dist/conversation/StoredCompactionManager.js.map +1 -0
- package/dist/conversation/SummaryTemplates.d.ts +35 -0
- package/dist/conversation/SummaryTemplates.d.ts.map +1 -0
- package/dist/conversation/SummaryTemplates.js +174 -0
- package/dist/conversation/SummaryTemplates.js.map +1 -0
- package/dist/conversation/index.d.ts +8 -0
- package/dist/conversation/index.d.ts.map +1 -0
- package/dist/conversation/index.js +8 -0
- package/dist/conversation/index.js.map +1 -0
- package/dist/file-tracking/ContentAddressableStore.d.ts +86 -0
- package/dist/file-tracking/ContentAddressableStore.d.ts.map +1 -0
- package/dist/file-tracking/ContentAddressableStore.js +187 -0
- package/dist/file-tracking/ContentAddressableStore.js.map +1 -0
- package/dist/file-tracking/FileCheckpointManager.d.ts +103 -0
- package/dist/file-tracking/FileCheckpointManager.d.ts.map +1 -0
- package/dist/file-tracking/FileCheckpointManager.js +269 -0
- package/dist/file-tracking/FileCheckpointManager.js.map +1 -0
- package/dist/file-tracking/index.d.ts +7 -0
- package/dist/file-tracking/index.d.ts.map +1 -0
- package/dist/file-tracking/index.js +7 -0
- package/dist/file-tracking/index.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/APITransport.d.ts +120 -0
- package/dist/interfaces/APITransport.d.ts.map +1 -0
- package/dist/interfaces/APITransport.js +14 -0
- package/dist/interfaces/APITransport.js.map +1 -0
- package/dist/interfaces/ConfigProvider.d.ts +52 -0
- package/dist/interfaces/ConfigProvider.d.ts.map +1 -0
- package/dist/interfaces/ConfigProvider.js +14 -0
- package/dist/interfaces/ConfigProvider.js.map +1 -0
- package/dist/interfaces/CredentialResolver.d.ts +57 -0
- package/dist/interfaces/CredentialResolver.d.ts.map +1 -0
- package/dist/interfaces/CredentialResolver.js +13 -0
- package/dist/interfaces/CredentialResolver.js.map +1 -0
- package/dist/interfaces/HistoryStore.d.ts +108 -0
- package/dist/interfaces/HistoryStore.d.ts.map +1 -0
- package/dist/interfaces/HistoryStore.js +14 -0
- package/dist/interfaces/HistoryStore.js.map +1 -0
- package/dist/interfaces/PermissionHandler.d.ts +74 -0
- package/dist/interfaces/PermissionHandler.d.ts.map +1 -0
- package/dist/interfaces/PermissionHandler.js +13 -0
- package/dist/interfaces/PermissionHandler.js.map +1 -0
- package/dist/interfaces/ToolExecutorRegistry.d.ts +126 -0
- package/dist/interfaces/ToolExecutorRegistry.d.ts.map +1 -0
- package/dist/interfaces/ToolExecutorRegistry.js +14 -0
- package/dist/interfaces/ToolExecutorRegistry.js.map +1 -0
- package/dist/interfaces/index.d.ts +19 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +14 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/mcp/McpClient.d.ts +142 -0
- package/dist/mcp/McpClient.d.ts.map +1 -0
- package/dist/mcp/McpClient.js +349 -0
- package/dist/mcp/McpClient.js.map +1 -0
- package/dist/mcp/McpClientManager.d.ts +148 -0
- package/dist/mcp/McpClientManager.d.ts.map +1 -0
- package/dist/mcp/McpClientManager.js +366 -0
- package/dist/mcp/McpClientManager.js.map +1 -0
- package/dist/mcp/McpConfigManager.d.ts +125 -0
- package/dist/mcp/McpConfigManager.d.ts.map +1 -0
- package/dist/mcp/McpConfigManager.js +448 -0
- package/dist/mcp/McpConfigManager.js.map +1 -0
- package/dist/mcp/McpServerRegistry.d.ts +102 -0
- package/dist/mcp/McpServerRegistry.d.ts.map +1 -0
- package/dist/mcp/McpServerRegistry.js +281 -0
- package/dist/mcp/McpServerRegistry.js.map +1 -0
- package/dist/mcp/index.d.ts +11 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcpToolNamespacing.d.ts +26 -0
- package/dist/mcp/mcpToolNamespacing.d.ts.map +1 -0
- package/dist/mcp/mcpToolNamespacing.js +36 -0
- package/dist/mcp/mcpToolNamespacing.js.map +1 -0
- package/dist/mcp/test-mcp-integration.d.ts +8 -0
- package/dist/mcp/test-mcp-integration.d.ts.map +1 -0
- package/dist/mcp/test-mcp-integration.js +115 -0
- package/dist/mcp/test-mcp-integration.js.map +1 -0
- package/dist/middleware/ErrorClassificationMiddleware.d.ts +116 -0
- package/dist/middleware/ErrorClassificationMiddleware.d.ts.map +1 -0
- package/dist/middleware/ErrorClassificationMiddleware.js +225 -0
- package/dist/middleware/ErrorClassificationMiddleware.js.map +1 -0
- package/dist/middleware/HelperModelMiddleware.d.ts +337 -0
- package/dist/middleware/HelperModelMiddleware.d.ts.map +1 -0
- package/dist/middleware/HelperModelMiddleware.js +1376 -0
- package/dist/middleware/HelperModelMiddleware.js.map +1 -0
- package/dist/middleware/MentorshipMiddleware.d.ts +210 -0
- package/dist/middleware/MentorshipMiddleware.d.ts.map +1 -0
- package/dist/middleware/MentorshipMiddleware.js +427 -0
- package/dist/middleware/MentorshipMiddleware.js.map +1 -0
- package/dist/middleware/PermissionsMiddleware.d.ts +193 -0
- package/dist/middleware/PermissionsMiddleware.d.ts.map +1 -0
- package/dist/middleware/PermissionsMiddleware.js +344 -0
- package/dist/middleware/PermissionsMiddleware.js.map +1 -0
- package/dist/middleware/RetryMiddleware.d.ts +159 -0
- package/dist/middleware/RetryMiddleware.d.ts.map +1 -0
- package/dist/middleware/RetryMiddleware.js +268 -0
- package/dist/middleware/RetryMiddleware.js.map +1 -0
- package/dist/middleware/SystemMessageMiddleware.d.ts +165 -0
- package/dist/middleware/SystemMessageMiddleware.d.ts.map +1 -0
- package/dist/middleware/SystemMessageMiddleware.js +354 -0
- package/dist/middleware/SystemMessageMiddleware.js.map +1 -0
- package/dist/middleware/contracts/MiddlewareContracts.d.ts +294 -0
- package/dist/middleware/contracts/MiddlewareContracts.d.ts.map +1 -0
- package/dist/middleware/contracts/MiddlewareContracts.js +12 -0
- package/dist/middleware/contracts/MiddlewareContracts.js.map +1 -0
- package/dist/middleware/helpers/HelperMiddlewareAdapter.interface.d.ts +211 -0
- package/dist/middleware/helpers/HelperMiddlewareAdapter.interface.d.ts.map +1 -0
- package/dist/middleware/helpers/HelperMiddlewareAdapter.interface.js +211 -0
- package/dist/middleware/helpers/HelperMiddlewareAdapter.interface.js.map +1 -0
- package/dist/middleware/helpers/HelperModelMiddlewareRegistry.d.ts +106 -0
- package/dist/middleware/helpers/HelperModelMiddlewareRegistry.d.ts.map +1 -0
- package/dist/middleware/helpers/HelperModelMiddlewareRegistry.js +148 -0
- package/dist/middleware/helpers/HelperModelMiddlewareRegistry.js.map +1 -0
- package/dist/middleware/helpers/adapters/ChatCompletionsAPIHelperAdapter.d.ts +76 -0
- package/dist/middleware/helpers/adapters/ChatCompletionsAPIHelperAdapter.d.ts.map +1 -0
- package/dist/middleware/helpers/adapters/ChatCompletionsAPIHelperAdapter.js +277 -0
- package/dist/middleware/helpers/adapters/ChatCompletionsAPIHelperAdapter.js.map +1 -0
- package/dist/middleware/helpers/adapters/GenerateContentAPIHelperAdapter.d.ts +57 -0
- package/dist/middleware/helpers/adapters/GenerateContentAPIHelperAdapter.d.ts.map +1 -0
- package/dist/middleware/helpers/adapters/GenerateContentAPIHelperAdapter.js +155 -0
- package/dist/middleware/helpers/adapters/GenerateContentAPIHelperAdapter.js.map +1 -0
- package/dist/middleware/helpers/adapters/GoogleGenAPIHelperAdapter.d.ts +124 -0
- package/dist/middleware/helpers/adapters/GoogleGenAPIHelperAdapter.d.ts.map +1 -0
- package/dist/middleware/helpers/adapters/GoogleGenAPIHelperAdapter.js +276 -0
- package/dist/middleware/helpers/adapters/GoogleGenAPIHelperAdapter.js.map +1 -0
- package/dist/middleware/helpers/adapters/MessagesAPIHelperAdapter.d.ts +71 -0
- package/dist/middleware/helpers/adapters/MessagesAPIHelperAdapter.d.ts.map +1 -0
- package/dist/middleware/helpers/adapters/MessagesAPIHelperAdapter.js +264 -0
- package/dist/middleware/helpers/adapters/MessagesAPIHelperAdapter.js.map +1 -0
- package/dist/middleware/helpers/adapters/ResponsesAPIHelperAdapter.d.ts +64 -0
- package/dist/middleware/helpers/adapters/ResponsesAPIHelperAdapter.d.ts.map +1 -0
- package/dist/middleware/helpers/adapters/ResponsesAPIHelperAdapter.js +189 -0
- package/dist/middleware/helpers/adapters/ResponsesAPIHelperAdapter.js.map +1 -0
- package/dist/middleware/helpers/adapters/index.d.ts +31 -0
- package/dist/middleware/helpers/adapters/index.d.ts.map +1 -0
- package/dist/middleware/helpers/adapters/index.js +40 -0
- package/dist/middleware/helpers/adapters/index.js.map +1 -0
- package/dist/middleware/helpers/index.d.ts +10 -0
- package/dist/middleware/helpers/index.d.ts.map +1 -0
- package/dist/middleware/helpers/index.js +13 -0
- package/dist/middleware/helpers/index.js.map +1 -0
- package/dist/middleware/index.d.ts +12 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +13 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/permissions/ApprovalHandler.d.ts +22 -0
- package/dist/middleware/permissions/ApprovalHandler.d.ts.map +1 -0
- package/dist/middleware/permissions/ApprovalHandler.js +27 -0
- package/dist/middleware/permissions/ApprovalHandler.js.map +1 -0
- package/dist/middleware/permissions/AutoApproveHandler.d.ts +62 -0
- package/dist/middleware/permissions/AutoApproveHandler.d.ts.map +1 -0
- package/dist/middleware/permissions/AutoApproveHandler.js +68 -0
- package/dist/middleware/permissions/AutoApproveHandler.js.map +1 -0
- package/dist/middleware/permissions/BashCommandPolicy.d.ts +134 -0
- package/dist/middleware/permissions/BashCommandPolicy.d.ts.map +1 -0
- package/dist/middleware/permissions/BashCommandPolicy.js +239 -0
- package/dist/middleware/permissions/BashCommandPolicy.js.map +1 -0
- package/dist/middleware/permissions/BlacklistPolicy.d.ts +66 -0
- package/dist/middleware/permissions/BlacklistPolicy.d.ts.map +1 -0
- package/dist/middleware/permissions/BlacklistPolicy.js +82 -0
- package/dist/middleware/permissions/BlacklistPolicy.js.map +1 -0
- package/dist/middleware/permissions/CLIApprovalHandler.d.ts +124 -0
- package/dist/middleware/permissions/CLIApprovalHandler.d.ts.map +1 -0
- package/dist/middleware/permissions/CLIApprovalHandler.js +366 -0
- package/dist/middleware/permissions/CLIApprovalHandler.js.map +1 -0
- package/dist/middleware/permissions/DefaultPolicies.d.ts +53 -0
- package/dist/middleware/permissions/DefaultPolicies.d.ts.map +1 -0
- package/dist/middleware/permissions/DefaultPolicies.js +307 -0
- package/dist/middleware/permissions/DefaultPolicies.js.map +1 -0
- package/dist/middleware/permissions/DenyAllHandler.d.ts +58 -0
- package/dist/middleware/permissions/DenyAllHandler.d.ts.map +1 -0
- package/dist/middleware/permissions/DenyAllHandler.js +59 -0
- package/dist/middleware/permissions/DenyAllHandler.js.map +1 -0
- package/dist/middleware/permissions/FileOperationPolicy.d.ts +117 -0
- package/dist/middleware/permissions/FileOperationPolicy.d.ts.map +1 -0
- package/dist/middleware/permissions/FileOperationPolicy.js +222 -0
- package/dist/middleware/permissions/FileOperationPolicy.js.map +1 -0
- package/dist/middleware/permissions/IPCApprovalHandler.d.ts +94 -0
- package/dist/middleware/permissions/IPCApprovalHandler.d.ts.map +1 -0
- package/dist/middleware/permissions/IPCApprovalHandler.js +178 -0
- package/dist/middleware/permissions/IPCApprovalHandler.js.map +1 -0
- package/dist/middleware/permissions/PermissionAuditLogger.d.ts +169 -0
- package/dist/middleware/permissions/PermissionAuditLogger.d.ts.map +1 -0
- package/dist/middleware/permissions/PermissionAuditLogger.js +267 -0
- package/dist/middleware/permissions/PermissionAuditLogger.js.map +1 -0
- package/dist/middleware/permissions/PermissionConfig.d.ts +181 -0
- package/dist/middleware/permissions/PermissionConfig.d.ts.map +1 -0
- package/dist/middleware/permissions/PermissionConfig.js +110 -0
- package/dist/middleware/permissions/PermissionConfig.js.map +1 -0
- package/dist/middleware/permissions/PermissionConfigLoader.d.ts +89 -0
- package/dist/middleware/permissions/PermissionConfigLoader.d.ts.map +1 -0
- package/dist/middleware/permissions/PermissionConfigLoader.js +296 -0
- package/dist/middleware/permissions/PermissionConfigLoader.js.map +1 -0
- package/dist/middleware/permissions/PermissionEvaluator.d.ts +111 -0
- package/dist/middleware/permissions/PermissionEvaluator.d.ts.map +1 -0
- package/dist/middleware/permissions/PermissionEvaluator.js +196 -0
- package/dist/middleware/permissions/PermissionEvaluator.js.map +1 -0
- package/dist/middleware/permissions/PermissionPolicy.d.ts +59 -0
- package/dist/middleware/permissions/PermissionPolicy.d.ts.map +1 -0
- package/dist/middleware/permissions/PermissionPolicy.js +67 -0
- package/dist/middleware/permissions/PermissionPolicy.js.map +1 -0
- package/dist/middleware/permissions/PermissionProfileStore.d.ts +77 -0
- package/dist/middleware/permissions/PermissionProfileStore.d.ts.map +1 -0
- package/dist/middleware/permissions/PermissionProfileStore.js +167 -0
- package/dist/middleware/permissions/PermissionProfileStore.js.map +1 -0
- package/dist/middleware/permissions/WhitelistPolicy.d.ts +66 -0
- package/dist/middleware/permissions/WhitelistPolicy.d.ts.map +1 -0
- package/dist/middleware/permissions/WhitelistPolicy.js +82 -0
- package/dist/middleware/permissions/WhitelistPolicy.js.map +1 -0
- package/dist/middleware/permissions/index.d.ts +40 -0
- package/dist/middleware/permissions/index.d.ts.map +1 -0
- package/dist/middleware/permissions/index.js +30 -0
- package/dist/middleware/permissions/index.js.map +1 -0
- package/dist/middleware/permissions/profilePath.d.ts +15 -0
- package/dist/middleware/permissions/profilePath.d.ts.map +1 -0
- package/dist/middleware/permissions/profilePath.js +35 -0
- package/dist/middleware/permissions/profilePath.js.map +1 -0
- package/dist/models/ModelConfig.interface.d.ts +18 -0
- package/dist/models/ModelConfig.interface.d.ts.map +1 -0
- package/dist/models/ModelConfig.interface.js +157 -0
- package/dist/models/ModelConfig.interface.js.map +1 -0
- package/dist/models/cards/anthropic/claude-3-5-haiku.d.ts +11 -0
- package/dist/models/cards/anthropic/claude-3-5-haiku.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-3-5-haiku.js +19 -0
- package/dist/models/cards/anthropic/claude-3-5-haiku.js.map +1 -0
- package/dist/models/cards/anthropic/claude-3-5-sonnet.d.ts +11 -0
- package/dist/models/cards/anthropic/claude-3-5-sonnet.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-3-5-sonnet.js +19 -0
- package/dist/models/cards/anthropic/claude-3-5-sonnet.js.map +1 -0
- package/dist/models/cards/anthropic/claude-3-haiku.d.ts +11 -0
- package/dist/models/cards/anthropic/claude-3-haiku.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-3-haiku.js +19 -0
- package/dist/models/cards/anthropic/claude-3-haiku.js.map +1 -0
- package/dist/models/cards/anthropic/claude-4-5-haiku.d.ts +11 -0
- package/dist/models/cards/anthropic/claude-4-5-haiku.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-4-5-haiku.js +26 -0
- package/dist/models/cards/anthropic/claude-4-5-haiku.js.map +1 -0
- package/dist/models/cards/anthropic/claude-fable-5.d.ts +14 -0
- package/dist/models/cards/anthropic/claude-fable-5.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-fable-5.js +30 -0
- package/dist/models/cards/anthropic/claude-fable-5.js.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-1.d.ts +11 -0
- package/dist/models/cards/anthropic/claude-opus-4-1.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-1.js +19 -0
- package/dist/models/cards/anthropic/claude-opus-4-1.js.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-5.d.ts +11 -0
- package/dist/models/cards/anthropic/claude-opus-4-5.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-5.js +27 -0
- package/dist/models/cards/anthropic/claude-opus-4-5.js.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-6.d.ts +10 -0
- package/dist/models/cards/anthropic/claude-opus-4-6.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-6.js +26 -0
- package/dist/models/cards/anthropic/claude-opus-4-6.js.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-7.d.ts +10 -0
- package/dist/models/cards/anthropic/claude-opus-4-7.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-7.js +26 -0
- package/dist/models/cards/anthropic/claude-opus-4-7.js.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-8.d.ts +12 -0
- package/dist/models/cards/anthropic/claude-opus-4-8.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-opus-4-8.js +28 -0
- package/dist/models/cards/anthropic/claude-opus-4-8.js.map +1 -0
- package/dist/models/cards/anthropic/claude-sonnet-4-5.d.ts +11 -0
- package/dist/models/cards/anthropic/claude-sonnet-4-5.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-sonnet-4-5.js +27 -0
- package/dist/models/cards/anthropic/claude-sonnet-4-5.js.map +1 -0
- package/dist/models/cards/anthropic/claude-sonnet-4-6.d.ts +10 -0
- package/dist/models/cards/anthropic/claude-sonnet-4-6.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-sonnet-4-6.js +26 -0
- package/dist/models/cards/anthropic/claude-sonnet-4-6.js.map +1 -0
- package/dist/models/cards/anthropic/claude-sonnet-4.d.ts +11 -0
- package/dist/models/cards/anthropic/claude-sonnet-4.d.ts.map +1 -0
- package/dist/models/cards/anthropic/claude-sonnet-4.js +19 -0
- package/dist/models/cards/anthropic/claude-sonnet-4.js.map +1 -0
- package/dist/models/cards/anthropic/index.d.ts +15 -0
- package/dist/models/cards/anthropic/index.d.ts.map +1 -0
- package/dist/models/cards/anthropic/index.js +15 -0
- package/dist/models/cards/anthropic/index.js.map +1 -0
- package/dist/models/cards/cloudflare/gemma-4-26b.d.ts +3 -0
- package/dist/models/cards/cloudflare/gemma-4-26b.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/gemma-4-26b.js +17 -0
- package/dist/models/cards/cloudflare/gemma-4-26b.js.map +1 -0
- package/dist/models/cards/cloudflare/glm-4-7-flash.d.ts +3 -0
- package/dist/models/cards/cloudflare/glm-4-7-flash.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/glm-4-7-flash.js +18 -0
- package/dist/models/cards/cloudflare/glm-4-7-flash.js.map +1 -0
- package/dist/models/cards/cloudflare/gpt-oss-120b.d.ts +3 -0
- package/dist/models/cards/cloudflare/gpt-oss-120b.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/gpt-oss-120b.js +18 -0
- package/dist/models/cards/cloudflare/gpt-oss-120b.js.map +1 -0
- package/dist/models/cards/cloudflare/gpt-oss-20b.d.ts +3 -0
- package/dist/models/cards/cloudflare/gpt-oss-20b.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/gpt-oss-20b.js +18 -0
- package/dist/models/cards/cloudflare/gpt-oss-20b.js.map +1 -0
- package/dist/models/cards/cloudflare/granite-4.d.ts +3 -0
- package/dist/models/cards/cloudflare/granite-4.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/granite-4.js +17 -0
- package/dist/models/cards/cloudflare/granite-4.js.map +1 -0
- package/dist/models/cards/cloudflare/index.d.ts +24 -0
- package/dist/models/cards/cloudflare/index.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/index.js +24 -0
- package/dist/models/cards/cloudflare/index.js.map +1 -0
- package/dist/models/cards/cloudflare/kimi-k2-5.d.ts +3 -0
- package/dist/models/cards/cloudflare/kimi-k2-5.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/kimi-k2-5.js +17 -0
- package/dist/models/cards/cloudflare/kimi-k2-5.js.map +1 -0
- package/dist/models/cards/cloudflare/kimi-k2-6.d.ts +3 -0
- package/dist/models/cards/cloudflare/kimi-k2-6.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/kimi-k2-6.js +20 -0
- package/dist/models/cards/cloudflare/kimi-k2-6.js.map +1 -0
- package/dist/models/cards/cloudflare/llama-3-3-70b.d.ts +3 -0
- package/dist/models/cards/cloudflare/llama-3-3-70b.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/llama-3-3-70b.js +17 -0
- package/dist/models/cards/cloudflare/llama-3-3-70b.js.map +1 -0
- package/dist/models/cards/cloudflare/llama-4-scout.d.ts +3 -0
- package/dist/models/cards/cloudflare/llama-4-scout.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/llama-4-scout.js +18 -0
- package/dist/models/cards/cloudflare/llama-4-scout.js.map +1 -0
- package/dist/models/cards/cloudflare/mistral-small-3-1.d.ts +3 -0
- package/dist/models/cards/cloudflare/mistral-small-3-1.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/mistral-small-3-1.js +18 -0
- package/dist/models/cards/cloudflare/mistral-small-3-1.js.map +1 -0
- package/dist/models/cards/cloudflare/nemotron-3-120b.d.ts +3 -0
- package/dist/models/cards/cloudflare/nemotron-3-120b.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/nemotron-3-120b.js +16 -0
- package/dist/models/cards/cloudflare/nemotron-3-120b.js.map +1 -0
- package/dist/models/cards/cloudflare/qwen3-30b.d.ts +3 -0
- package/dist/models/cards/cloudflare/qwen3-30b.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/qwen3-30b.js +17 -0
- package/dist/models/cards/cloudflare/qwen3-30b.js.map +1 -0
- package/dist/models/cards/cloudflare/qwq-32b.d.ts +3 -0
- package/dist/models/cards/cloudflare/qwq-32b.d.ts.map +1 -0
- package/dist/models/cards/cloudflare/qwq-32b.js +18 -0
- package/dist/models/cards/cloudflare/qwq-32b.js.map +1 -0
- package/dist/models/cards/deepseek/deepseek-v4-flash.d.ts +11 -0
- package/dist/models/cards/deepseek/deepseek-v4-flash.d.ts.map +1 -0
- package/dist/models/cards/deepseek/deepseek-v4-flash.js +25 -0
- package/dist/models/cards/deepseek/deepseek-v4-flash.js.map +1 -0
- package/dist/models/cards/deepseek/deepseek-v4-pro.d.ts +11 -0
- package/dist/models/cards/deepseek/deepseek-v4-pro.d.ts.map +1 -0
- package/dist/models/cards/deepseek/deepseek-v4-pro.js +25 -0
- package/dist/models/cards/deepseek/deepseek-v4-pro.js.map +1 -0
- package/dist/models/cards/deepseek/index.d.ts +22 -0
- package/dist/models/cards/deepseek/index.d.ts.map +1 -0
- package/dist/models/cards/deepseek/index.js +22 -0
- package/dist/models/cards/deepseek/index.js.map +1 -0
- package/dist/models/cards/gemma/gemma-3-12b-it.d.ts +10 -0
- package/dist/models/cards/gemma/gemma-3-12b-it.d.ts.map +1 -0
- package/dist/models/cards/gemma/gemma-3-12b-it.js +16 -0
- package/dist/models/cards/gemma/gemma-3-12b-it.js.map +1 -0
- package/dist/models/cards/gemma/gemma-3-1b-it.d.ts +10 -0
- package/dist/models/cards/gemma/gemma-3-1b-it.d.ts.map +1 -0
- package/dist/models/cards/gemma/gemma-3-1b-it.js +16 -0
- package/dist/models/cards/gemma/gemma-3-1b-it.js.map +1 -0
- package/dist/models/cards/gemma/gemma-3-27b-it.d.ts +10 -0
- package/dist/models/cards/gemma/gemma-3-27b-it.d.ts.map +1 -0
- package/dist/models/cards/gemma/gemma-3-27b-it.js +16 -0
- package/dist/models/cards/gemma/gemma-3-27b-it.js.map +1 -0
- package/dist/models/cards/gemma/gemma-3-4b-it.d.ts +10 -0
- package/dist/models/cards/gemma/gemma-3-4b-it.d.ts.map +1 -0
- package/dist/models/cards/gemma/gemma-3-4b-it.js +16 -0
- package/dist/models/cards/gemma/gemma-3-4b-it.js.map +1 -0
- package/dist/models/cards/gemma/index.d.ts +9 -0
- package/dist/models/cards/gemma/index.d.ts.map +1 -0
- package/dist/models/cards/gemma/index.js +9 -0
- package/dist/models/cards/gemma/index.js.map +1 -0
- package/dist/models/cards/glm/glm-4-5-air.d.ts +17 -0
- package/dist/models/cards/glm/glm-4-5-air.d.ts.map +1 -0
- package/dist/models/cards/glm/glm-4-5-air.js +27 -0
- package/dist/models/cards/glm/glm-4-5-air.js.map +1 -0
- package/dist/models/cards/glm/glm-4-5.d.ts +16 -0
- package/dist/models/cards/glm/glm-4-5.d.ts.map +1 -0
- package/dist/models/cards/glm/glm-4-5.js +26 -0
- package/dist/models/cards/glm/glm-4-5.js.map +1 -0
- package/dist/models/cards/glm/glm-4-6.d.ts +17 -0
- package/dist/models/cards/glm/glm-4-6.d.ts.map +1 -0
- package/dist/models/cards/glm/glm-4-6.js +27 -0
- package/dist/models/cards/glm/glm-4-6.js.map +1 -0
- package/dist/models/cards/glm/glm-4-flash.d.ts +12 -0
- package/dist/models/cards/glm/glm-4-flash.d.ts.map +1 -0
- package/dist/models/cards/glm/glm-4-flash.js +20 -0
- package/dist/models/cards/glm/glm-4-flash.js.map +1 -0
- package/dist/models/cards/glm/glm-4.d.ts +12 -0
- package/dist/models/cards/glm/glm-4.d.ts.map +1 -0
- package/dist/models/cards/glm/glm-4.js +21 -0
- package/dist/models/cards/glm/glm-4.js.map +1 -0
- package/dist/models/cards/glm/index.d.ts +13 -0
- package/dist/models/cards/glm/index.d.ts.map +1 -0
- package/dist/models/cards/glm/index.js +13 -0
- package/dist/models/cards/glm/index.js.map +1 -0
- package/dist/models/cards/google/gemini-1-5-flash.d.ts +10 -0
- package/dist/models/cards/google/gemini-1-5-flash.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-1-5-flash.js +18 -0
- package/dist/models/cards/google/gemini-1-5-flash.js.map +1 -0
- package/dist/models/cards/google/gemini-1-5-pro.d.ts +10 -0
- package/dist/models/cards/google/gemini-1-5-pro.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-1-5-pro.js +18 -0
- package/dist/models/cards/google/gemini-1-5-pro.js.map +1 -0
- package/dist/models/cards/google/gemini-2-0-flash-lite.d.ts +10 -0
- package/dist/models/cards/google/gemini-2-0-flash-lite.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-2-0-flash-lite.js +18 -0
- package/dist/models/cards/google/gemini-2-0-flash-lite.js.map +1 -0
- package/dist/models/cards/google/gemini-2-0-flash.d.ts +10 -0
- package/dist/models/cards/google/gemini-2-0-flash.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-2-0-flash.js +24 -0
- package/dist/models/cards/google/gemini-2-0-flash.js.map +1 -0
- package/dist/models/cards/google/gemini-2-5-computer-use-preview.d.ts +18 -0
- package/dist/models/cards/google/gemini-2-5-computer-use-preview.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-2-5-computer-use-preview.js +26 -0
- package/dist/models/cards/google/gemini-2-5-computer-use-preview.js.map +1 -0
- package/dist/models/cards/google/gemini-2-5-flash-lite.d.ts +10 -0
- package/dist/models/cards/google/gemini-2-5-flash-lite.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-2-5-flash-lite.js +18 -0
- package/dist/models/cards/google/gemini-2-5-flash-lite.js.map +1 -0
- package/dist/models/cards/google/gemini-2-5-flash-sdk.d.ts +11 -0
- package/dist/models/cards/google/gemini-2-5-flash-sdk.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-2-5-flash-sdk.js +25 -0
- package/dist/models/cards/google/gemini-2-5-flash-sdk.js.map +1 -0
- package/dist/models/cards/google/gemini-2-5-flash.d.ts +10 -0
- package/dist/models/cards/google/gemini-2-5-flash.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-2-5-flash.js +24 -0
- package/dist/models/cards/google/gemini-2-5-flash.js.map +1 -0
- package/dist/models/cards/google/gemini-2-5-pro.d.ts +10 -0
- package/dist/models/cards/google/gemini-2-5-pro.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-2-5-pro.js +24 -0
- package/dist/models/cards/google/gemini-2-5-pro.js.map +1 -0
- package/dist/models/cards/google/gemini-3-1-flash-lite-preview.d.ts +11 -0
- package/dist/models/cards/google/gemini-3-1-flash-lite-preview.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-3-1-flash-lite-preview.js +20 -0
- package/dist/models/cards/google/gemini-3-1-flash-lite-preview.js.map +1 -0
- package/dist/models/cards/google/gemini-3-1-pro-preview.d.ts +10 -0
- package/dist/models/cards/google/gemini-3-1-pro-preview.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-3-1-pro-preview.js +19 -0
- package/dist/models/cards/google/gemini-3-1-pro-preview.js.map +1 -0
- package/dist/models/cards/google/gemini-3-5-flash.d.ts +10 -0
- package/dist/models/cards/google/gemini-3-5-flash.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-3-5-flash.js +19 -0
- package/dist/models/cards/google/gemini-3-5-flash.js.map +1 -0
- package/dist/models/cards/google/gemini-3-flash-preview.d.ts +11 -0
- package/dist/models/cards/google/gemini-3-flash-preview.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-3-flash-preview.js +20 -0
- package/dist/models/cards/google/gemini-3-flash-preview.js.map +1 -0
- package/dist/models/cards/google/gemini-3.d.ts +11 -0
- package/dist/models/cards/google/gemini-3.d.ts.map +1 -0
- package/dist/models/cards/google/gemini-3.js +20 -0
- package/dist/models/cards/google/gemini-3.js.map +1 -0
- package/dist/models/cards/google/index.d.ts +17 -0
- package/dist/models/cards/google/index.d.ts.map +1 -0
- package/dist/models/cards/google/index.js +17 -0
- package/dist/models/cards/google/index.js.map +1 -0
- package/dist/models/cards/huggingface/index.d.ts +10 -0
- package/dist/models/cards/huggingface/index.d.ts.map +1 -0
- package/dist/models/cards/huggingface/index.js +10 -0
- package/dist/models/cards/huggingface/index.js.map +1 -0
- package/dist/models/cards/huggingface/llama-3-8b-instruct.d.ts +17 -0
- package/dist/models/cards/huggingface/llama-3-8b-instruct.d.ts.map +1 -0
- package/dist/models/cards/huggingface/llama-3-8b-instruct.js +25 -0
- package/dist/models/cards/huggingface/llama-3-8b-instruct.js.map +1 -0
- package/dist/models/cards/huggingface/mistral-7b-instruct.d.ts +10 -0
- package/dist/models/cards/huggingface/mistral-7b-instruct.d.ts.map +1 -0
- package/dist/models/cards/huggingface/mistral-7b-instruct.js +18 -0
- package/dist/models/cards/huggingface/mistral-7b-instruct.js.map +1 -0
- package/dist/models/cards/local/codellama-13b-local.d.ts +19 -0
- package/dist/models/cards/local/codellama-13b-local.d.ts.map +1 -0
- package/dist/models/cards/local/codellama-13b-local.js +28 -0
- package/dist/models/cards/local/codellama-13b-local.js.map +1 -0
- package/dist/models/cards/local/index.d.ts +17 -0
- package/dist/models/cards/local/index.d.ts.map +1 -0
- package/dist/models/cards/local/index.js +17 -0
- package/dist/models/cards/local/index.js.map +1 -0
- package/dist/models/cards/local/llama-3-8b-local.d.ts +25 -0
- package/dist/models/cards/local/llama-3-8b-local.d.ts.map +1 -0
- package/dist/models/cards/local/llama-3-8b-local.js +34 -0
- package/dist/models/cards/local/llama-3-8b-local.js.map +1 -0
- package/dist/models/cards/local/mistral-7b-ollama.d.ts +18 -0
- package/dist/models/cards/local/mistral-7b-ollama.d.ts.map +1 -0
- package/dist/models/cards/local/mistral-7b-ollama.js +27 -0
- package/dist/models/cards/local/mistral-7b-ollama.js.map +1 -0
- package/dist/models/cards/mercury/index.d.ts +10 -0
- package/dist/models/cards/mercury/index.d.ts.map +1 -0
- package/dist/models/cards/mercury/index.js +10 -0
- package/dist/models/cards/mercury/index.js.map +1 -0
- package/dist/models/cards/mercury/mercury-2.d.ts +24 -0
- package/dist/models/cards/mercury/mercury-2.d.ts.map +1 -0
- package/dist/models/cards/mercury/mercury-2.js +33 -0
- package/dist/models/cards/mercury/mercury-2.js.map +1 -0
- package/dist/models/cards/minimax/index.d.ts +10 -0
- package/dist/models/cards/minimax/index.d.ts.map +1 -0
- package/dist/models/cards/minimax/index.js +10 -0
- package/dist/models/cards/minimax/index.js.map +1 -0
- package/dist/models/cards/minimax/minimax-m2-stable.d.ts +21 -0
- package/dist/models/cards/minimax/minimax-m2-stable.d.ts.map +1 -0
- package/dist/models/cards/minimax/minimax-m2-stable.js +31 -0
- package/dist/models/cards/minimax/minimax-m2-stable.js.map +1 -0
- package/dist/models/cards/minimax/minimax-m2.d.ts +20 -0
- package/dist/models/cards/minimax/minimax-m2.d.ts.map +1 -0
- package/dist/models/cards/minimax/minimax-m2.js +30 -0
- package/dist/models/cards/minimax/minimax-m2.js.map +1 -0
- package/dist/models/cards/moonshot/index.d.ts +9 -0
- package/dist/models/cards/moonshot/index.d.ts.map +1 -0
- package/dist/models/cards/moonshot/index.js +9 -0
- package/dist/models/cards/moonshot/index.js.map +1 -0
- package/dist/models/cards/moonshot/kimi-chat-128k.d.ts +10 -0
- package/dist/models/cards/moonshot/kimi-chat-128k.d.ts.map +1 -0
- package/dist/models/cards/moonshot/kimi-chat-128k.js +18 -0
- package/dist/models/cards/moonshot/kimi-chat-128k.js.map +1 -0
- package/dist/models/cards/moonshot/kimi-chat-32k.d.ts +10 -0
- package/dist/models/cards/moonshot/kimi-chat-32k.d.ts.map +1 -0
- package/dist/models/cards/moonshot/kimi-chat-32k.js +18 -0
- package/dist/models/cards/moonshot/kimi-chat-32k.js.map +1 -0
- package/dist/models/cards/moonshot/kimi-chat.d.ts +12 -0
- package/dist/models/cards/moonshot/kimi-chat.d.ts.map +1 -0
- package/dist/models/cards/moonshot/kimi-chat.js +20 -0
- package/dist/models/cards/moonshot/kimi-chat.js.map +1 -0
- package/dist/models/cards/moonshot/kimi-k2-instruct.d.ts +17 -0
- package/dist/models/cards/moonshot/kimi-k2-instruct.d.ts.map +1 -0
- package/dist/models/cards/moonshot/kimi-k2-instruct.js +26 -0
- package/dist/models/cards/moonshot/kimi-k2-instruct.js.map +1 -0
- package/dist/models/cards/moonshot/kimi-k2-thinking.d.ts +18 -0
- package/dist/models/cards/moonshot/kimi-k2-thinking.d.ts.map +1 -0
- package/dist/models/cards/moonshot/kimi-k2-thinking.js +27 -0
- package/dist/models/cards/moonshot/kimi-k2-thinking.js.map +1 -0
- package/dist/models/cards/openai/gpt-4-1-mini.d.ts +10 -0
- package/dist/models/cards/openai/gpt-4-1-mini.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-4-1-mini.js +18 -0
- package/dist/models/cards/openai/gpt-4-1-mini.js.map +1 -0
- package/dist/models/cards/openai/gpt-4-1-nano.d.ts +10 -0
- package/dist/models/cards/openai/gpt-4-1-nano.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-4-1-nano.js +18 -0
- package/dist/models/cards/openai/gpt-4-1-nano.js.map +1 -0
- package/dist/models/cards/openai/gpt-4-1.d.ts +10 -0
- package/dist/models/cards/openai/gpt-4-1.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-4-1.js +18 -0
- package/dist/models/cards/openai/gpt-4-1.js.map +1 -0
- package/dist/models/cards/openai/gpt-4o-mini.d.ts +10 -0
- package/dist/models/cards/openai/gpt-4o-mini.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-4o-mini.js +19 -0
- package/dist/models/cards/openai/gpt-4o-mini.js.map +1 -0
- package/dist/models/cards/openai/gpt-4o.d.ts +10 -0
- package/dist/models/cards/openai/gpt-4o.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-4o.js +19 -0
- package/dist/models/cards/openai/gpt-4o.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-1-reasoning.d.ts +14 -0
- package/dist/models/cards/openai/gpt-5-1-reasoning.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-1-reasoning.js +29 -0
- package/dist/models/cards/openai/gpt-5-1-reasoning.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-1.d.ts +11 -0
- package/dist/models/cards/openai/gpt-5-1.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-1.js +27 -0
- package/dist/models/cards/openai/gpt-5-1.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-2.d.ts +21 -0
- package/dist/models/cards/openai/gpt-5-2.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-2.js +37 -0
- package/dist/models/cards/openai/gpt-5-2.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-4-mini.d.ts +11 -0
- package/dist/models/cards/openai/gpt-5-4-mini.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-4-mini.js +27 -0
- package/dist/models/cards/openai/gpt-5-4-mini.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-4.d.ts +11 -0
- package/dist/models/cards/openai/gpt-5-4.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-4.js +27 -0
- package/dist/models/cards/openai/gpt-5-4.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-5.d.ts +11 -0
- package/dist/models/cards/openai/gpt-5-5.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-5.js +27 -0
- package/dist/models/cards/openai/gpt-5-5.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-chat-latest.d.ts +10 -0
- package/dist/models/cards/openai/gpt-5-chat-latest.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-chat-latest.js +26 -0
- package/dist/models/cards/openai/gpt-5-chat-latest.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-codex.d.ts +11 -0
- package/dist/models/cards/openai/gpt-5-codex.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-codex.js +19 -0
- package/dist/models/cards/openai/gpt-5-codex.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-mini.d.ts +10 -0
- package/dist/models/cards/openai/gpt-5-mini.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-mini.js +26 -0
- package/dist/models/cards/openai/gpt-5-mini.js.map +1 -0
- package/dist/models/cards/openai/gpt-5-nano.d.ts +10 -0
- package/dist/models/cards/openai/gpt-5-nano.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5-nano.js +26 -0
- package/dist/models/cards/openai/gpt-5-nano.js.map +1 -0
- package/dist/models/cards/openai/gpt-5.d.ts +10 -0
- package/dist/models/cards/openai/gpt-5.d.ts.map +1 -0
- package/dist/models/cards/openai/gpt-5.js +26 -0
- package/dist/models/cards/openai/gpt-5.js.map +1 -0
- package/dist/models/cards/openai/index.d.ts +32 -0
- package/dist/models/cards/openai/index.d.ts.map +1 -0
- package/dist/models/cards/openai/index.js +38 -0
- package/dist/models/cards/openai/index.js.map +1 -0
- package/dist/models/cards/openai/o1-mini.d.ts +11 -0
- package/dist/models/cards/openai/o1-mini.d.ts.map +1 -0
- package/dist/models/cards/openai/o1-mini.js +28 -0
- package/dist/models/cards/openai/o1-mini.js.map +1 -0
- package/dist/models/cards/openai/o1-pro.d.ts +11 -0
- package/dist/models/cards/openai/o1-pro.d.ts.map +1 -0
- package/dist/models/cards/openai/o1-pro.js +28 -0
- package/dist/models/cards/openai/o1-pro.js.map +1 -0
- package/dist/models/cards/openai/o1.d.ts +11 -0
- package/dist/models/cards/openai/o1.d.ts.map +1 -0
- package/dist/models/cards/openai/o1.js +28 -0
- package/dist/models/cards/openai/o1.js.map +1 -0
- package/dist/models/cards/openai/o3-mini.d.ts +11 -0
- package/dist/models/cards/openai/o3-mini.d.ts.map +1 -0
- package/dist/models/cards/openai/o3-mini.js +30 -0
- package/dist/models/cards/openai/o3-mini.js.map +1 -0
- package/dist/models/cards/openai/o3-pro.d.ts +11 -0
- package/dist/models/cards/openai/o3-pro.d.ts.map +1 -0
- package/dist/models/cards/openai/o3-pro.js +28 -0
- package/dist/models/cards/openai/o3-pro.js.map +1 -0
- package/dist/models/cards/openai/o3.d.ts +11 -0
- package/dist/models/cards/openai/o3.d.ts.map +1 -0
- package/dist/models/cards/openai/o3.js +28 -0
- package/dist/models/cards/openai/o3.js.map +1 -0
- package/dist/models/cards/openai/o4-mini.d.ts +11 -0
- package/dist/models/cards/openai/o4-mini.d.ts.map +1 -0
- package/dist/models/cards/openai/o4-mini.js +27 -0
- package/dist/models/cards/openai/o4-mini.js.map +1 -0
- package/dist/models/cards/qwen/index.d.ts +13 -0
- package/dist/models/cards/qwen/index.d.ts.map +1 -0
- package/dist/models/cards/qwen/index.js +13 -0
- package/dist/models/cards/qwen/index.js.map +1 -0
- package/dist/models/cards/qwen/qwen-3-coder.d.ts +17 -0
- package/dist/models/cards/qwen/qwen-3-coder.d.ts.map +1 -0
- package/dist/models/cards/qwen/qwen-3-coder.js +27 -0
- package/dist/models/cards/qwen/qwen-3-coder.js.map +1 -0
- package/dist/models/cards/qwen/qwen-3-max-preview.d.ts +17 -0
- package/dist/models/cards/qwen/qwen-3-max-preview.d.ts.map +1 -0
- package/dist/models/cards/qwen/qwen-3-max-preview.js +27 -0
- package/dist/models/cards/qwen/qwen-3-max-preview.js.map +1 -0
- package/dist/models/cards/qwen/qwen-max.d.ts +10 -0
- package/dist/models/cards/qwen/qwen-max.d.ts.map +1 -0
- package/dist/models/cards/qwen/qwen-max.js +18 -0
- package/dist/models/cards/qwen/qwen-max.js.map +1 -0
- package/dist/models/cards/qwen/qwen-plus.d.ts +10 -0
- package/dist/models/cards/qwen/qwen-plus.d.ts.map +1 -0
- package/dist/models/cards/qwen/qwen-plus.js +18 -0
- package/dist/models/cards/qwen/qwen-plus.js.map +1 -0
- package/dist/models/cards/qwen/qwen-turbo.d.ts +10 -0
- package/dist/models/cards/qwen/qwen-turbo.d.ts.map +1 -0
- package/dist/models/cards/qwen/qwen-turbo.js +18 -0
- package/dist/models/cards/qwen/qwen-turbo.js.map +1 -0
- package/dist/models/cards/xai/grok-4-1-fast-non-reasoning.d.ts +15 -0
- package/dist/models/cards/xai/grok-4-1-fast-non-reasoning.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-1-fast-non-reasoning.js +24 -0
- package/dist/models/cards/xai/grok-4-1-fast-non-reasoning.js.map +1 -0
- package/dist/models/cards/xai/grok-4-1.d.ts +14 -0
- package/dist/models/cards/xai/grok-4-1.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-1.js +24 -0
- package/dist/models/cards/xai/grok-4-1.js.map +1 -0
- package/dist/models/cards/xai/grok-4-20-multi-agent.d.ts +13 -0
- package/dist/models/cards/xai/grok-4-20-multi-agent.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-20-multi-agent.js +23 -0
- package/dist/models/cards/xai/grok-4-20-multi-agent.js.map +1 -0
- package/dist/models/cards/xai/grok-4-20-non-reasoning.d.ts +10 -0
- package/dist/models/cards/xai/grok-4-20-non-reasoning.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-20-non-reasoning.js +19 -0
- package/dist/models/cards/xai/grok-4-20-non-reasoning.js.map +1 -0
- package/dist/models/cards/xai/grok-4-20-reasoning.d.ts +10 -0
- package/dist/models/cards/xai/grok-4-20-reasoning.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-20-reasoning.js +20 -0
- package/dist/models/cards/xai/grok-4-20-reasoning.js.map +1 -0
- package/dist/models/cards/xai/grok-4-3.d.ts +11 -0
- package/dist/models/cards/xai/grok-4-3.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-3.js +22 -0
- package/dist/models/cards/xai/grok-4-3.js.map +1 -0
- package/dist/models/cards/xai/grok-4-fast-non-reasoning.d.ts +13 -0
- package/dist/models/cards/xai/grok-4-fast-non-reasoning.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-fast-non-reasoning.js +22 -0
- package/dist/models/cards/xai/grok-4-fast-non-reasoning.js.map +1 -0
- package/dist/models/cards/xai/grok-4-fast.d.ts +12 -0
- package/dist/models/cards/xai/grok-4-fast.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-4-fast.js +21 -0
- package/dist/models/cards/xai/grok-4-fast.js.map +1 -0
- package/dist/models/cards/xai/grok-build-0-1-responses.d.ts +22 -0
- package/dist/models/cards/xai/grok-build-0-1-responses.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-build-0-1-responses.js +39 -0
- package/dist/models/cards/xai/grok-build-0-1-responses.js.map +1 -0
- package/dist/models/cards/xai/grok-build-0-1.d.ts +17 -0
- package/dist/models/cards/xai/grok-build-0-1.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-build-0-1.js +31 -0
- package/dist/models/cards/xai/grok-build-0-1.js.map +1 -0
- package/dist/models/cards/xai/grok-code-fast-1.d.ts +10 -0
- package/dist/models/cards/xai/grok-code-fast-1.d.ts.map +1 -0
- package/dist/models/cards/xai/grok-code-fast-1.js +22 -0
- package/dist/models/cards/xai/grok-code-fast-1.js.map +1 -0
- package/dist/models/cards/xai/index.d.ts +17 -0
- package/dist/models/cards/xai/index.d.ts.map +1 -0
- package/dist/models/cards/xai/index.js +18 -0
- package/dist/models/cards/xai/index.js.map +1 -0
- package/dist/models/configurators/AnthropicConfigurator.d.ts +57 -0
- package/dist/models/configurators/AnthropicConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/AnthropicConfigurator.js +108 -0
- package/dist/models/configurators/AnthropicConfigurator.js.map +1 -0
- package/dist/models/configurators/CloudflareConfigurator.d.ts +46 -0
- package/dist/models/configurators/CloudflareConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/CloudflareConfigurator.js +112 -0
- package/dist/models/configurators/CloudflareConfigurator.js.map +1 -0
- package/dist/models/configurators/DeepSeekConfigurator.d.ts +26 -0
- package/dist/models/configurators/DeepSeekConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/DeepSeekConfigurator.js +81 -0
- package/dist/models/configurators/DeepSeekConfigurator.js.map +1 -0
- package/dist/models/configurators/GLMConfigurator.d.ts +22 -0
- package/dist/models/configurators/GLMConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/GLMConfigurator.js +82 -0
- package/dist/models/configurators/GLMConfigurator.js.map +1 -0
- package/dist/models/configurators/GemmaConfigurator.d.ts +18 -0
- package/dist/models/configurators/GemmaConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/GemmaConfigurator.js +89 -0
- package/dist/models/configurators/GemmaConfigurator.js.map +1 -0
- package/dist/models/configurators/GoogleConfigurator.d.ts +26 -0
- package/dist/models/configurators/GoogleConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/GoogleConfigurator.js +91 -0
- package/dist/models/configurators/GoogleConfigurator.js.map +1 -0
- package/dist/models/configurators/GoogleSDKConfigurator.d.ts +29 -0
- package/dist/models/configurators/GoogleSDKConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/GoogleSDKConfigurator.js +95 -0
- package/dist/models/configurators/GoogleSDKConfigurator.js.map +1 -0
- package/dist/models/configurators/HuggingFaceConfigurator.d.ts +31 -0
- package/dist/models/configurators/HuggingFaceConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/HuggingFaceConfigurator.js +85 -0
- package/dist/models/configurators/HuggingFaceConfigurator.js.map +1 -0
- package/dist/models/configurators/LocalModelConfigurator.d.ts +30 -0
- package/dist/models/configurators/LocalModelConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/LocalModelConfigurator.js +88 -0
- package/dist/models/configurators/LocalModelConfigurator.js.map +1 -0
- package/dist/models/configurators/MercuryConfigurator.d.ts +27 -0
- package/dist/models/configurators/MercuryConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/MercuryConfigurator.js +91 -0
- package/dist/models/configurators/MercuryConfigurator.js.map +1 -0
- package/dist/models/configurators/MiniMaxConfigurator.d.ts +47 -0
- package/dist/models/configurators/MiniMaxConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/MiniMaxConfigurator.js +103 -0
- package/dist/models/configurators/MiniMaxConfigurator.js.map +1 -0
- package/dist/models/configurators/MoonshotConfigurator.d.ts +21 -0
- package/dist/models/configurators/MoonshotConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/MoonshotConfigurator.js +81 -0
- package/dist/models/configurators/MoonshotConfigurator.js.map +1 -0
- package/dist/models/configurators/OpenAIConfigurator.d.ts +52 -0
- package/dist/models/configurators/OpenAIConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/OpenAIConfigurator.js +128 -0
- package/dist/models/configurators/OpenAIConfigurator.js.map +1 -0
- package/dist/models/configurators/OpenAIResponsesConfigurator.d.ts +23 -0
- package/dist/models/configurators/OpenAIResponsesConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/OpenAIResponsesConfigurator.js +113 -0
- package/dist/models/configurators/OpenAIResponsesConfigurator.js.map +1 -0
- package/dist/models/configurators/QwenConfigurator.d.ts +26 -0
- package/dist/models/configurators/QwenConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/QwenConfigurator.js +87 -0
- package/dist/models/configurators/QwenConfigurator.js.map +1 -0
- package/dist/models/configurators/XAIConfigurator.d.ts +52 -0
- package/dist/models/configurators/XAIConfigurator.d.ts.map +1 -0
- package/dist/models/configurators/XAIConfigurator.js +130 -0
- package/dist/models/configurators/XAIConfigurator.js.map +1 -0
- package/dist/models/configurators/index.d.ts +19 -0
- package/dist/models/configurators/index.d.ts.map +1 -0
- package/dist/models/configurators/index.js +19 -0
- package/dist/models/configurators/index.js.map +1 -0
- package/dist/models/index.d.ts +7 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +7 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/registry/ModelAliasResolver.d.ts +109 -0
- package/dist/models/registry/ModelAliasResolver.d.ts.map +1 -0
- package/dist/models/registry/ModelAliasResolver.js +255 -0
- package/dist/models/registry/ModelAliasResolver.js.map +1 -0
- package/dist/models/registry/ModelCardLoader.d.ts +44 -0
- package/dist/models/registry/ModelCardLoader.d.ts.map +1 -0
- package/dist/models/registry/ModelCardLoader.js +124 -0
- package/dist/models/registry/ModelCardLoader.js.map +1 -0
- package/dist/models/registry/ModularModelRegistry.d.ts +86 -0
- package/dist/models/registry/ModularModelRegistry.d.ts.map +1 -0
- package/dist/models/registry/ModularModelRegistry.js +311 -0
- package/dist/models/registry/ModularModelRegistry.js.map +1 -0
- package/dist/models/registry/index.d.ts +8 -0
- package/dist/models/registry/index.d.ts.map +1 -0
- package/dist/models/registry/index.js +9 -0
- package/dist/models/registry/index.js.map +1 -0
- package/dist/orchestrator/APIClient.d.ts +274 -0
- package/dist/orchestrator/APIClient.d.ts.map +1 -0
- package/dist/orchestrator/APIClient.js +2770 -0
- package/dist/orchestrator/APIClient.js.map +1 -0
- package/dist/orchestrator/CortexOrchestrator.d.ts +986 -0
- package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -0
- package/dist/orchestrator/CortexOrchestrator.js +6374 -0
- package/dist/orchestrator/CortexOrchestrator.js.map +1 -0
- package/dist/orchestrator/OrchestratorFactory.d.ts +81 -0
- package/dist/orchestrator/OrchestratorFactory.d.ts.map +1 -0
- package/dist/orchestrator/OrchestratorFactory.js +433 -0
- package/dist/orchestrator/OrchestratorFactory.js.map +1 -0
- package/dist/orchestrator/PauseController.d.ts +126 -0
- package/dist/orchestrator/PauseController.d.ts.map +1 -0
- package/dist/orchestrator/PauseController.js +248 -0
- package/dist/orchestrator/PauseController.js.map +1 -0
- package/dist/orchestrator/SubAgentEventEmitter.d.ts +152 -0
- package/dist/orchestrator/SubAgentEventEmitter.d.ts.map +1 -0
- package/dist/orchestrator/SubAgentEventEmitter.js +372 -0
- package/dist/orchestrator/SubAgentEventEmitter.js.map +1 -0
- package/dist/orchestrator/SubAgentIPC.d.ts +242 -0
- package/dist/orchestrator/SubAgentIPC.d.ts.map +1 -0
- package/dist/orchestrator/SubAgentIPC.js +85 -0
- package/dist/orchestrator/SubAgentIPC.js.map +1 -0
- package/dist/orchestrator/SubAgentManager.d.ts +215 -0
- package/dist/orchestrator/SubAgentManager.d.ts.map +1 -0
- package/dist/orchestrator/SubAgentManager.js +477 -0
- package/dist/orchestrator/SubAgentManager.js.map +1 -0
- package/dist/orchestrator/SubAgentOrchestrator.d.ts +125 -0
- package/dist/orchestrator/SubAgentOrchestrator.d.ts.map +1 -0
- package/dist/orchestrator/SubAgentOrchestrator.js +395 -0
- package/dist/orchestrator/SubAgentOrchestrator.js.map +1 -0
- package/dist/orchestrator/SubAgentPermissionChecker.d.ts +132 -0
- package/dist/orchestrator/SubAgentPermissionChecker.d.ts.map +1 -0
- package/dist/orchestrator/SubAgentPermissionChecker.js +239 -0
- package/dist/orchestrator/SubAgentPermissionChecker.js.map +1 -0
- package/dist/orchestrator/SubAgentProcessManager.d.ts +228 -0
- package/dist/orchestrator/SubAgentProcessManager.d.ts.map +1 -0
- package/dist/orchestrator/SubAgentProcessManager.js +679 -0
- package/dist/orchestrator/SubAgentProcessManager.js.map +1 -0
- package/dist/orchestrator/SubAgentTypes.d.ts +428 -0
- package/dist/orchestrator/SubAgentTypes.d.ts.map +1 -0
- package/dist/orchestrator/SubAgentTypes.js +12 -0
- package/dist/orchestrator/SubAgentTypes.js.map +1 -0
- package/dist/orchestrator/apiErrorClassifier.d.ts +17 -0
- package/dist/orchestrator/apiErrorClassifier.d.ts.map +1 -0
- package/dist/orchestrator/apiErrorClassifier.js +38 -0
- package/dist/orchestrator/apiErrorClassifier.js.map +1 -0
- package/dist/orchestrator/assistantTextPresence.d.ts +32 -0
- package/dist/orchestrator/assistantTextPresence.d.ts.map +1 -0
- package/dist/orchestrator/assistantTextPresence.js +47 -0
- package/dist/orchestrator/assistantTextPresence.js.map +1 -0
- package/dist/orchestrator/citationVerification.d.ts +32 -0
- package/dist/orchestrator/citationVerification.d.ts.map +1 -0
- package/dist/orchestrator/citationVerification.js +47 -0
- package/dist/orchestrator/citationVerification.js.map +1 -0
- package/dist/orchestrator/coordinateVerification.d.ts +50 -0
- package/dist/orchestrator/coordinateVerification.d.ts.map +1 -0
- package/dist/orchestrator/coordinateVerification.js +114 -0
- package/dist/orchestrator/coordinateVerification.js.map +1 -0
- package/dist/orchestrator/cortexTrainingRecord.d.ts +88 -0
- package/dist/orchestrator/cortexTrainingRecord.d.ts.map +1 -0
- package/dist/orchestrator/cortexTrainingRecord.js +102 -0
- package/dist/orchestrator/cortexTrainingRecord.js.map +1 -0
- package/dist/orchestrator/index.d.ts +20 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +18 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/mcpAutoInjectPolicy.d.ts +18 -0
- package/dist/orchestrator/mcpAutoInjectPolicy.d.ts.map +1 -0
- package/dist/orchestrator/mcpAutoInjectPolicy.js +24 -0
- package/dist/orchestrator/mcpAutoInjectPolicy.js.map +1 -0
- package/dist/orchestrator/staticSystemPromptPin.d.ts +20 -0
- package/dist/orchestrator/staticSystemPromptPin.d.ts.map +1 -0
- package/dist/orchestrator/staticSystemPromptPin.js +33 -0
- package/dist/orchestrator/staticSystemPromptPin.js.map +1 -0
- package/dist/orchestrator/toolBudgetSignal.d.ts +41 -0
- package/dist/orchestrator/toolBudgetSignal.d.ts.map +1 -0
- package/dist/orchestrator/toolBudgetSignal.js +77 -0
- package/dist/orchestrator/toolBudgetSignal.js.map +1 -0
- package/dist/orchestrator/toolNameMatcher.d.ts +21 -0
- package/dist/orchestrator/toolNameMatcher.d.ts.map +1 -0
- package/dist/orchestrator/toolNameMatcher.js +69 -0
- package/dist/orchestrator/toolNameMatcher.js.map +1 -0
- package/dist/session/CacheMetricsAccumulator.d.ts +72 -0
- package/dist/session/CacheMetricsAccumulator.d.ts.map +1 -0
- package/dist/session/CacheMetricsAccumulator.js +144 -0
- package/dist/session/CacheMetricsAccumulator.js.map +1 -0
- package/dist/session/CheckpointManager.d.ts +106 -0
- package/dist/session/CheckpointManager.d.ts.map +1 -0
- package/dist/session/CheckpointManager.js +223 -0
- package/dist/session/CheckpointManager.js.map +1 -0
- package/dist/session/JSONLHistoryStore.d.ts +154 -0
- package/dist/session/JSONLHistoryStore.d.ts.map +1 -0
- package/dist/session/JSONLHistoryStore.js +393 -0
- package/dist/session/JSONLHistoryStore.js.map +1 -0
- package/dist/session/MessageTypes.d.ts +269 -0
- package/dist/session/MessageTypes.d.ts.map +1 -0
- package/dist/session/MessageTypes.js +38 -0
- package/dist/session/MessageTypes.js.map +1 -0
- package/dist/session/SessionTimeline.d.ts +304 -0
- package/dist/session/SessionTimeline.d.ts.map +1 -0
- package/dist/session/SessionTimeline.js +396 -0
- package/dist/session/SessionTimeline.js.map +1 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +10 -0
- package/dist/session/index.js.map +1 -0
- package/dist/system-messages/MessageValidator.d.ts +71 -0
- package/dist/system-messages/MessageValidator.d.ts.map +1 -0
- package/dist/system-messages/MessageValidator.js +305 -0
- package/dist/system-messages/MessageValidator.js.map +1 -0
- package/dist/system-messages/SystemMessageLoader.d.ts +154 -0
- package/dist/system-messages/SystemMessageLoader.d.ts.map +1 -0
- package/dist/system-messages/SystemMessageLoader.js +695 -0
- package/dist/system-messages/SystemMessageLoader.js.map +1 -0
- package/dist/system-messages/SystemMessageRegistry.d.ts +67 -0
- package/dist/system-messages/SystemMessageRegistry.d.ts.map +1 -0
- package/dist/system-messages/SystemMessageRegistry.interface.d.ts +166 -0
- package/dist/system-messages/SystemMessageRegistry.interface.d.ts.map +1 -0
- package/dist/system-messages/SystemMessageRegistry.interface.js +6 -0
- package/dist/system-messages/SystemMessageRegistry.interface.js.map +1 -0
- package/dist/system-messages/SystemMessageRegistry.js +164 -0
- package/dist/system-messages/SystemMessageRegistry.js.map +1 -0
- package/dist/system-messages/SystemMessageStore.d.ts +207 -0
- package/dist/system-messages/SystemMessageStore.d.ts.map +1 -0
- package/dist/system-messages/SystemMessageStore.js +748 -0
- package/dist/system-messages/SystemMessageStore.js.map +1 -0
- package/dist/system-messages/SystemReminderInjector.d.ts +148 -0
- package/dist/system-messages/SystemReminderInjector.d.ts.map +1 -0
- package/dist/system-messages/SystemReminderInjector.js +331 -0
- package/dist/system-messages/SystemReminderInjector.js.map +1 -0
- package/dist/system-messages/docTruncation.d.ts +31 -0
- package/dist/system-messages/docTruncation.d.ts.map +1 -0
- package/dist/system-messages/docTruncation.js +26 -0
- package/dist/system-messages/docTruncation.js.map +1 -0
- package/dist/system-messages/index.d.ts +12 -0
- package/dist/system-messages/index.d.ts.map +1 -0
- package/dist/system-messages/index.js +15 -0
- package/dist/system-messages/index.js.map +1 -0
- package/dist/system-messages/messages/ACTIVE_DISCOVERY.md +26 -0
- package/dist/system-messages/messages/EXAMPLES.md +73 -0
- package/dist/system-messages/messages/PERIODIC_REMINDER.md +6 -0
- package/dist/system-messages/messages/REASONING_GUIDE.md +12 -0
- package/dist/system-messages/messages/SYSTEM_PROMPT.md +47 -0
- package/dist/system-messages/messages/TASK_AGENT_GUIDE.md +5 -0
- package/dist/system-messages/messages/TOOL_USAGE_GUIDE.md +131 -0
- package/dist/system-messages/system-message-registry.json +233 -0
- package/dist/system-messages/turnVaryingClassifier.d.ts +20 -0
- package/dist/system-messages/turnVaryingClassifier.d.ts.map +1 -0
- package/dist/system-messages/turnVaryingClassifier.js +27 -0
- package/dist/system-messages/turnVaryingClassifier.js.map +1 -0
- package/dist/system-messages/types.d.ts +184 -0
- package/dist/system-messages/types.d.ts.map +1 -0
- package/dist/system-messages/types.js +7 -0
- package/dist/system-messages/types.js.map +1 -0
- package/dist/tools/ClientSideToolFilter.d.ts +35 -0
- package/dist/tools/ClientSideToolFilter.d.ts.map +1 -0
- package/dist/tools/ClientSideToolFilter.js +74 -0
- package/dist/tools/ClientSideToolFilter.js.map +1 -0
- package/dist/tools/ServerSideTools.d.ts +181 -0
- package/dist/tools/ServerSideTools.d.ts.map +1 -0
- package/dist/tools/ServerSideTools.js +309 -0
- package/dist/tools/ServerSideTools.js.map +1 -0
- package/dist/tools/ToolFactory.d.ts +88 -0
- package/dist/tools/ToolFactory.d.ts.map +1 -0
- package/dist/tools/ToolFactory.js +153 -0
- package/dist/tools/ToolFactory.js.map +1 -0
- package/dist/tools/context-management/InitCortexContext.d.ts +184 -0
- package/dist/tools/context-management/InitCortexContext.d.ts.map +1 -0
- package/dist/tools/context-management/InitCortexContext.js +1063 -0
- package/dist/tools/context-management/InitCortexContext.js.map +1 -0
- package/dist/tools/context-management/index.d.ts +8 -0
- package/dist/tools/context-management/index.d.ts.map +1 -0
- package/dist/tools/context-management/index.js +7 -0
- package/dist/tools/context-management/index.js.map +1 -0
- package/dist/tools/historical/GetConversationSegment.d.ts +131 -0
- package/dist/tools/historical/GetConversationSegment.d.ts.map +1 -0
- package/dist/tools/historical/GetConversationSegment.js +306 -0
- package/dist/tools/historical/GetConversationSegment.js.map +1 -0
- package/dist/tools/historical/HistoricalContextService.d.ts +229 -0
- package/dist/tools/historical/HistoricalContextService.d.ts.map +1 -0
- package/dist/tools/historical/HistoricalContextService.js +206 -0
- package/dist/tools/historical/HistoricalContextService.js.map +1 -0
- package/dist/tools/historical/ListCompactionBoundaries.d.ts +107 -0
- package/dist/tools/historical/ListCompactionBoundaries.d.ts.map +1 -0
- package/dist/tools/historical/ListCompactionBoundaries.js +175 -0
- package/dist/tools/historical/ListCompactionBoundaries.js.map +1 -0
- package/dist/tools/historical/ListSessions.d.ts +50 -0
- package/dist/tools/historical/ListSessions.d.ts.map +1 -0
- package/dist/tools/historical/ListSessions.js +93 -0
- package/dist/tools/historical/ListSessions.js.map +1 -0
- package/dist/tools/historical/LoadSession.d.ts +51 -0
- package/dist/tools/historical/LoadSession.d.ts.map +1 -0
- package/dist/tools/historical/LoadSession.js +89 -0
- package/dist/tools/historical/LoadSession.js.map +1 -0
- package/dist/tools/historical/RequestHistoricalContext.d.ts +122 -0
- package/dist/tools/historical/RequestHistoricalContext.d.ts.map +1 -0
- package/dist/tools/historical/RequestHistoricalContext.js +250 -0
- package/dist/tools/historical/RequestHistoricalContext.js.map +1 -0
- package/dist/tools/historical/SearchConversationHistory.d.ts +117 -0
- package/dist/tools/historical/SearchConversationHistory.d.ts.map +1 -0
- package/dist/tools/historical/SearchConversationHistory.js +239 -0
- package/dist/tools/historical/SearchConversationHistory.js.map +1 -0
- package/dist/tools/historical/index.d.ts +14 -0
- package/dist/tools/historical/index.d.ts.map +1 -0
- package/dist/tools/historical/index.js +14 -0
- package/dist/tools/historical/index.js.map +1 -0
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +21 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/mcp-management/ConfigureMcpServer.d.ts +41 -0
- package/dist/tools/mcp-management/ConfigureMcpServer.d.ts.map +1 -0
- package/dist/tools/mcp-management/ConfigureMcpServer.js +147 -0
- package/dist/tools/mcp-management/ConfigureMcpServer.js.map +1 -0
- package/dist/tools/mcp-management/DisableMcpServer.d.ts +39 -0
- package/dist/tools/mcp-management/DisableMcpServer.d.ts.map +1 -0
- package/dist/tools/mcp-management/DisableMcpServer.js +108 -0
- package/dist/tools/mcp-management/DisableMcpServer.js.map +1 -0
- package/dist/tools/mcp-management/EnableMcpServer.d.ts +49 -0
- package/dist/tools/mcp-management/EnableMcpServer.d.ts.map +1 -0
- package/dist/tools/mcp-management/EnableMcpServer.js +134 -0
- package/dist/tools/mcp-management/EnableMcpServer.js.map +1 -0
- package/dist/tools/mcp-management/GetMcpConfig.d.ts +52 -0
- package/dist/tools/mcp-management/GetMcpConfig.d.ts.map +1 -0
- package/dist/tools/mcp-management/GetMcpConfig.js +95 -0
- package/dist/tools/mcp-management/GetMcpConfig.js.map +1 -0
- package/dist/tools/mcp-management/InitMcpConfig.d.ts +113 -0
- package/dist/tools/mcp-management/InitMcpConfig.d.ts.map +1 -0
- package/dist/tools/mcp-management/InitMcpConfig.js +421 -0
- package/dist/tools/mcp-management/InitMcpConfig.js.map +1 -0
- package/dist/tools/mcp-management/ListAvailableMcpServers.d.ts +50 -0
- package/dist/tools/mcp-management/ListAvailableMcpServers.d.ts.map +1 -0
- package/dist/tools/mcp-management/ListAvailableMcpServers.js +92 -0
- package/dist/tools/mcp-management/ListAvailableMcpServers.js.map +1 -0
- package/dist/tools/mcp-management/SearchMcpServers.d.ts +44 -0
- package/dist/tools/mcp-management/SearchMcpServers.d.ts.map +1 -0
- package/dist/tools/mcp-management/SearchMcpServers.js +96 -0
- package/dist/tools/mcp-management/SearchMcpServers.js.map +1 -0
- package/dist/tools/mcp-management/index.d.ts +23 -0
- package/dist/tools/mcp-management/index.d.ts.map +1 -0
- package/dist/tools/mcp-management/index.js +16 -0
- package/dist/tools/mcp-management/index.js.map +1 -0
- package/dist/tools/registries/AddonToolRegistry.d.ts +70 -0
- package/dist/tools/registries/AddonToolRegistry.d.ts.map +1 -0
- package/dist/tools/registries/AddonToolRegistry.js +138 -0
- package/dist/tools/registries/AddonToolRegistry.js.map +1 -0
- package/dist/tools/registries/BaseToolRegistry.d.ts +38 -0
- package/dist/tools/registries/BaseToolRegistry.d.ts.map +1 -0
- package/dist/tools/registries/BaseToolRegistry.js +2058 -0
- package/dist/tools/registries/BaseToolRegistry.js.map +1 -0
- package/dist/tools/registries/index.d.ts +8 -0
- package/dist/tools/registries/index.d.ts.map +1 -0
- package/dist/tools/registries/index.js +8 -0
- package/dist/tools/registries/index.js.map +1 -0
- package/dist/tools/types/CanonicalTool.d.ts +130 -0
- package/dist/tools/types/CanonicalTool.d.ts.map +1 -0
- package/dist/tools/types/CanonicalTool.js +10 -0
- package/dist/tools/types/CanonicalTool.js.map +1 -0
- package/dist/tools/types/index.d.ts +7 -0
- package/dist/tools/types/index.d.ts.map +1 -0
- package/dist/tools/types/index.js +7 -0
- package/dist/tools/types/index.js.map +1 -0
- package/dist/training/AutoResearchGate.d.ts +64 -0
- package/dist/training/AutoResearchGate.d.ts.map +1 -0
- package/dist/training/AutoResearchGate.js +94 -0
- package/dist/training/AutoResearchGate.js.map +1 -0
- package/dist/training/AutoResearchStats.d.ts +128 -0
- package/dist/training/AutoResearchStats.d.ts.map +1 -0
- package/dist/training/AutoResearchStats.js +210 -0
- package/dist/training/AutoResearchStats.js.map +1 -0
- package/dist/training/BenchRunner.d.ts +177 -0
- package/dist/training/BenchRunner.d.ts.map +1 -0
- package/dist/training/BenchRunner.js +211 -0
- package/dist/training/BenchRunner.js.map +1 -0
- package/dist/training/DecisionPriorInjector.d.ts +18 -0
- package/dist/training/DecisionPriorInjector.d.ts.map +1 -0
- package/dist/training/DecisionPriorInjector.js +34 -0
- package/dist/training/DecisionPriorInjector.js.map +1 -0
- package/dist/training/DecisionStore.d.ts +78 -0
- package/dist/training/DecisionStore.d.ts.map +1 -0
- package/dist/training/DecisionStore.js +171 -0
- package/dist/training/DecisionStore.js.map +1 -0
- package/dist/training/ExperimentLedger.d.ts +138 -0
- package/dist/training/ExperimentLedger.d.ts.map +1 -0
- package/dist/training/ExperimentLedger.js +160 -0
- package/dist/training/ExperimentLedger.js.map +1 -0
- package/dist/training/ExperimentRunner.d.ts +88 -0
- package/dist/training/ExperimentRunner.d.ts.map +1 -0
- package/dist/training/ExperimentRunner.js +97 -0
- package/dist/training/ExperimentRunner.js.map +1 -0
- package/dist/training/ModelRouterMatrix.d.ts +197 -0
- package/dist/training/ModelRouterMatrix.d.ts.map +1 -0
- package/dist/training/ModelRouterMatrix.js +0 -0
- package/dist/training/ModelRouterMatrix.js.map +1 -0
- package/dist/training/ResearchBacklog.d.ts +102 -0
- package/dist/training/ResearchBacklog.d.ts.map +1 -0
- package/dist/training/ResearchBacklog.js +149 -0
- package/dist/training/ResearchBacklog.js.map +1 -0
- package/dist/training/TaskClassifier.d.ts +16 -0
- package/dist/training/TaskClassifier.d.ts.map +1 -0
- package/dist/training/TaskClassifier.js +70 -0
- package/dist/training/TaskClassifier.js.map +1 -0
- package/dist/training/ThompsonRouter.d.ts +53 -0
- package/dist/training/ThompsonRouter.d.ts.map +1 -0
- package/dist/training/ThompsonRouter.js +73 -0
- package/dist/training/ThompsonRouter.js.map +1 -0
- package/dist/training/VersionComparison.d.ts +90 -0
- package/dist/training/VersionComparison.d.ts.map +1 -0
- package/dist/training/VersionComparison.js +117 -0
- package/dist/training/VersionComparison.js.map +1 -0
- package/dist/ui/index.d.ts +13 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/menu-types.d.ts +314 -0
- package/dist/ui/menu-types.d.ts.map +1 -0
- package/dist/ui/menu-types.js +138 -0
- package/dist/ui/menu-types.js.map +1 -0
- package/dist/ui/types.d.ts +86 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +12 -0
- package/dist/ui/types.js.map +1 -0
- package/dist/utils/ContextResolver.d.ts +132 -0
- package/dist/utils/ContextResolver.d.ts.map +1 -0
- package/dist/utils/ContextResolver.js +269 -0
- package/dist/utils/ContextResolver.js.map +1 -0
- package/dist/utils/DiffParser.d.ts +101 -0
- package/dist/utils/DiffParser.d.ts.map +1 -0
- package/dist/utils/DiffParser.js +193 -0
- package/dist/utils/DiffParser.js.map +1 -0
- package/dist/utils/ErrorDetector.d.ts +99 -0
- package/dist/utils/ErrorDetector.d.ts.map +1 -0
- package/dist/utils/ErrorDetector.js +258 -0
- package/dist/utils/ErrorDetector.js.map +1 -0
- package/dist/utils/TokenCounter.d.ts +97 -0
- package/dist/utils/TokenCounter.d.ts.map +1 -0
- package/dist/utils/TokenCounter.js +193 -0
- package/dist/utils/TokenCounter.js.map +1 -0
- package/dist/utils/agentDiscovery.d.ts +12 -0
- package/dist/utils/agentDiscovery.d.ts.map +1 -0
- package/dist/utils/agentDiscovery.js +72 -0
- package/dist/utils/agentDiscovery.js.map +1 -0
- package/dist/utils/ids.d.ts +5 -0
- package/dist/utils/ids.d.ts.map +1 -0
- package/dist/utils/ids.js +14 -0
- package/dist/utils/ids.js.map +1 -0
- package/dist/utils/index.d.ts +14 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +14 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +44 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,2770 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Client
|
|
3
|
+
* Handles actual HTTP requests to AI provider APIs
|
|
4
|
+
*
|
|
5
|
+
* Phase 2.1: Real API Integration
|
|
6
|
+
*/
|
|
7
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
8
|
+
import OpenAI from 'openai';
|
|
9
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
10
|
+
import { GoogleGenAI } from '@google/genai';
|
|
11
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
12
|
+
import { anthropicCredentialService, CredentialError } from '../config/AnthropicCredentialService.js';
|
|
13
|
+
/**
|
|
14
|
+
* Anthropic families on the adaptive-thinking-only request surface — sending
|
|
15
|
+
* thinking.type 'enabled'/budget_tokens returns a 400 (sampling params are
|
|
16
|
+
* rejected too, but cards opt out by leaving temperature.default unset).
|
|
17
|
+
* Fable 5 is stricter still: an explicit {type:'disabled'} also 400s — both
|
|
18
|
+
* request paths already OMIT thinking instead of disabling it explicitly.
|
|
19
|
+
*/
|
|
20
|
+
const ANTHROPIC_ADAPTIVE_THINKING_FAMILIES = new Set([
|
|
21
|
+
'claude-4.7',
|
|
22
|
+
'claude-4.8',
|
|
23
|
+
'claude-fable-5'
|
|
24
|
+
]);
|
|
25
|
+
/**
|
|
26
|
+
* API Client for making requests to AI providers
|
|
27
|
+
*
|
|
28
|
+
* Supports: Anthropic, OpenAI, Google (Gemini + Gemma)
|
|
29
|
+
*/
|
|
30
|
+
export class APIClient {
|
|
31
|
+
anthropicClient;
|
|
32
|
+
openaiClient;
|
|
33
|
+
googleClient;
|
|
34
|
+
googleGenAIClient;
|
|
35
|
+
constructor() {
|
|
36
|
+
// Initialize clients lazily when needed
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Decode HTML entities in an object (recursive)
|
|
40
|
+
*
|
|
41
|
+
* WORKAROUND for model bugs (e.g., grok-4-1-fast) that incorrectly
|
|
42
|
+
* encode special characters in tool call arguments.
|
|
43
|
+
*
|
|
44
|
+
* @param obj - Object to decode (may contain nested objects/arrays)
|
|
45
|
+
* @returns Decoded object with HTML entities converted to plain characters
|
|
46
|
+
*/
|
|
47
|
+
decodeHtmlEntitiesInObject(obj) {
|
|
48
|
+
if (typeof obj === 'string') {
|
|
49
|
+
// Decode common HTML entities
|
|
50
|
+
return obj
|
|
51
|
+
.replace(/&/g, '&')
|
|
52
|
+
.replace(/</g, '<')
|
|
53
|
+
.replace(/>/g, '>')
|
|
54
|
+
.replace(/"/g, '"')
|
|
55
|
+
.replace(/'/g, "'")
|
|
56
|
+
.replace(/'/g, "'");
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(obj)) {
|
|
59
|
+
return obj.map(item => this.decodeHtmlEntitiesInObject(item));
|
|
60
|
+
}
|
|
61
|
+
if (obj !== null && typeof obj === 'object') {
|
|
62
|
+
const decoded = {};
|
|
63
|
+
for (const key in obj) {
|
|
64
|
+
if (obj.hasOwnProperty(key)) {
|
|
65
|
+
decoded[key] = this.decodeHtmlEntitiesInObject(obj[key]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return decoded;
|
|
69
|
+
}
|
|
70
|
+
return obj;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Attempt to repair truncated JSON from streaming tool arguments.
|
|
74
|
+
* When providers truncate output (max_tokens), the accumulated JSON is incomplete.
|
|
75
|
+
* Tries progressively aggressive repairs: closing open strings, objects, arrays.
|
|
76
|
+
*/
|
|
77
|
+
repairTruncatedJSON(raw) {
|
|
78
|
+
const trimmed = raw.trim();
|
|
79
|
+
if (!trimmed)
|
|
80
|
+
throw new Error('Empty JSON');
|
|
81
|
+
// Try direct parse first
|
|
82
|
+
try {
|
|
83
|
+
return JSON.parse(trimmed);
|
|
84
|
+
}
|
|
85
|
+
catch { /* continue */ }
|
|
86
|
+
// Try closing open string + object combinations
|
|
87
|
+
const repairs = [
|
|
88
|
+
trimmed + '"}',
|
|
89
|
+
trimmed + '"}}',
|
|
90
|
+
trimmed + '"]',
|
|
91
|
+
trimmed + '"}]',
|
|
92
|
+
trimmed + '}',
|
|
93
|
+
trimmed + '}}',
|
|
94
|
+
];
|
|
95
|
+
for (const attempt of repairs) {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = JSON.parse(attempt);
|
|
98
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
99
|
+
if (process.env.DEBUG === 'true')
|
|
100
|
+
console.log(`[APIClient] Repaired truncated JSON (${raw.length} chars) by appending ${attempt.slice(trimmed.length)}`);
|
|
101
|
+
return parsed;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch { /* continue */ }
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Cannot repair truncated JSON (${raw.length} chars)`);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Send request to provider API
|
|
110
|
+
*
|
|
111
|
+
* Routes by API PATTERN (not provider) to maintain architectural consistency.
|
|
112
|
+
* The pattern is specified in modelConfig.api.pattern and determines which
|
|
113
|
+
* adapter and API endpoint to use.
|
|
114
|
+
*
|
|
115
|
+
* @param request - Prepared request from GatewayTranslationLayer
|
|
116
|
+
* @param modelConfig - Model configuration
|
|
117
|
+
* @returns Provider response
|
|
118
|
+
*/
|
|
119
|
+
async sendRequest(request, modelConfig) {
|
|
120
|
+
// Clamp any requested temperature to THIS model's valid range before dispatch — the
|
|
121
|
+
// cross-provider gate (e.g. Anthropic is 0–1, OpenAI/DeepSeek 0–2). Lets a per-subagent
|
|
122
|
+
// temperature (an auto-research diversity lever) vary freely without 400-ing a model whose
|
|
123
|
+
// range is narrower. Applies to every API pattern below.
|
|
124
|
+
const tRange = modelConfig.parameters?.temperature;
|
|
125
|
+
const t = request.parameters?.temperature;
|
|
126
|
+
if (typeof t === 'number' && Number.isFinite(t) && tRange) {
|
|
127
|
+
const clamped = Math.max(tRange.min ?? 0, Math.min(tRange.max ?? 2, t));
|
|
128
|
+
if (clamped !== t)
|
|
129
|
+
request.parameters.temperature = clamped;
|
|
130
|
+
}
|
|
131
|
+
const apiPattern = modelConfig.api.pattern;
|
|
132
|
+
switch (apiPattern) {
|
|
133
|
+
case 'messages':
|
|
134
|
+
// Anthropic Messages API (used by Anthropic, XAI)
|
|
135
|
+
return this.sendMessagesAPI(request, modelConfig);
|
|
136
|
+
case 'chat/completions':
|
|
137
|
+
// OpenAI Chat Completions API (used by OpenAI, XAI, DeepSeek, Groq)
|
|
138
|
+
return this.sendChatCompletionsAPI(request, modelConfig);
|
|
139
|
+
case 'responses':
|
|
140
|
+
// OpenAI Responses API (used by OpenAI gpt-5-codex, XAI stateful)
|
|
141
|
+
return this.sendResponsesAPI(request, modelConfig);
|
|
142
|
+
case 'generateContent':
|
|
143
|
+
// Google Gemini GenerateContent API
|
|
144
|
+
return this.sendGenerateContentAPI(request, modelConfig);
|
|
145
|
+
case 'google-genai':
|
|
146
|
+
// Google GenAI API (FREE Gemma models)
|
|
147
|
+
return this.sendGoogleGenAIRequest(request, modelConfig);
|
|
148
|
+
case 'google-sdk':
|
|
149
|
+
// Google SDK with full tool support
|
|
150
|
+
return this.sendGoogleSDK(request, modelConfig);
|
|
151
|
+
default:
|
|
152
|
+
throw new Error(`Unsupported API pattern: ${apiPattern}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Stream request to provider API (with real-time chunks)
|
|
157
|
+
*
|
|
158
|
+
* Routes by API PATTERN (not provider) to maintain architectural consistency
|
|
159
|
+
* with non-streaming sendRequest() method
|
|
160
|
+
*
|
|
161
|
+
* @param request - Prepared request from GatewayTranslationLayer
|
|
162
|
+
* @param modelConfig - Model configuration
|
|
163
|
+
* @returns Streaming response with chunks and final message
|
|
164
|
+
*/
|
|
165
|
+
streamRequest(request, modelConfig) {
|
|
166
|
+
const apiPattern = modelConfig.api.pattern;
|
|
167
|
+
switch (apiPattern) {
|
|
168
|
+
case 'messages':
|
|
169
|
+
// Anthropic Messages API (used by Anthropic, XAI)
|
|
170
|
+
return this.streamMessagesAPI(request, modelConfig);
|
|
171
|
+
case 'chat/completions':
|
|
172
|
+
// OpenAI Chat Completions API (used by OpenAI, XAI, DeepSeek, Groq)
|
|
173
|
+
return this.streamChatCompletionsAPI(request, modelConfig);
|
|
174
|
+
case 'responses':
|
|
175
|
+
// OpenAI Responses API (used by OpenAI gpt-5-codex, XAI stateful)
|
|
176
|
+
return this.streamResponsesAPI(request, modelConfig);
|
|
177
|
+
case 'generateContent':
|
|
178
|
+
// Google Gemini GenerateContent API
|
|
179
|
+
return this.streamGenerateContentAPI(request, modelConfig);
|
|
180
|
+
case 'google-genai':
|
|
181
|
+
throw new Error('Streaming not supported for google-genai pattern');
|
|
182
|
+
case 'google-sdk':
|
|
183
|
+
// Google SDK with full tool support (experimental)
|
|
184
|
+
return this.streamGoogleSDK(request, modelConfig);
|
|
185
|
+
default:
|
|
186
|
+
throw new Error(`Unsupported API pattern for streaming: ${apiPattern}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Send request using Messages API pattern
|
|
191
|
+
*
|
|
192
|
+
* Used by: Anthropic (native), XAI (Anthropic-compatible)
|
|
193
|
+
* Format: Anthropic Messages API
|
|
194
|
+
*/
|
|
195
|
+
async sendMessagesAPI(request, modelConfig) {
|
|
196
|
+
const provider = modelConfig.provider.toLowerCase();
|
|
197
|
+
// Anthropic: Use native SDK
|
|
198
|
+
if (provider === 'anthropic') {
|
|
199
|
+
return this.sendAnthropicMessagesAPI(request, modelConfig);
|
|
200
|
+
}
|
|
201
|
+
// XAI: Use Anthropic-compatible Messages API
|
|
202
|
+
if (provider === 'xai') {
|
|
203
|
+
return this.sendXAIMessagesAPI(request, modelConfig);
|
|
204
|
+
}
|
|
205
|
+
throw new Error(`Messages API not supported for provider: ${provider}`);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Send request to Anthropic Messages API (native)
|
|
209
|
+
*
|
|
210
|
+
* Supports:
|
|
211
|
+
* - OAuth authentication (Claude.ai Max subscriptions)
|
|
212
|
+
* - API key authentication (traditional)
|
|
213
|
+
* - Prompt caching for 90% cost reduction
|
|
214
|
+
*/
|
|
215
|
+
async sendAnthropicMessagesAPI(request, modelConfig) {
|
|
216
|
+
// Initialize Anthropic client with OAuth or API key
|
|
217
|
+
if (!this.anthropicClient) {
|
|
218
|
+
this.anthropicClient = this.initializeAnthropicClient();
|
|
219
|
+
}
|
|
220
|
+
// Phase 2.8: Extract internal flags before building request
|
|
221
|
+
// Note: Anthropic requires thinking blocks to precede tool_use in assistant messages
|
|
222
|
+
// when thinking is enabled. Continuations after tool execution must respect this ordering.
|
|
223
|
+
const disableThinking = request.parameters.disableThinking;
|
|
224
|
+
const reasoningEffort = request.parameters.reasoningEffort;
|
|
225
|
+
// Exclude internal flags from API request parameters
|
|
226
|
+
const { disableThinking: _, reasoningEffort: _re, ...apiParameters } = request.parameters;
|
|
227
|
+
// Check if prompt caching is enabled
|
|
228
|
+
const enableCaching = process.env.ANTHROPIC_PROMPT_CACHING !== 'false';
|
|
229
|
+
// Build Anthropic request
|
|
230
|
+
const anthropicRequest = {
|
|
231
|
+
model: request.modelId,
|
|
232
|
+
messages: request.messages,
|
|
233
|
+
max_tokens: request.parameters.max_tokens || request.parameters.maxTokens || 4096,
|
|
234
|
+
...apiParameters
|
|
235
|
+
};
|
|
236
|
+
// Add system message if present (with cache_control for caching)
|
|
237
|
+
if (request.systemMessage) {
|
|
238
|
+
if (enableCaching) {
|
|
239
|
+
// Use array format with cache_control for prompt caching
|
|
240
|
+
anthropicRequest.system = [{
|
|
241
|
+
type: 'text',
|
|
242
|
+
text: request.systemMessage,
|
|
243
|
+
cache_control: { type: 'ephemeral' }
|
|
244
|
+
}];
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
anthropicRequest.system = request.systemMessage;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Add tools if present (with cache_control on last tool for caching)
|
|
251
|
+
if (request.tools && request.tools.length > 0) {
|
|
252
|
+
if (enableCaching) {
|
|
253
|
+
// Clone tools and add cache_control to last tool
|
|
254
|
+
const tools = request.tools.map((tool, index) => {
|
|
255
|
+
if (index === request.tools.length - 1) {
|
|
256
|
+
return { ...tool, cache_control: { type: 'ephemeral' } };
|
|
257
|
+
}
|
|
258
|
+
return tool;
|
|
259
|
+
});
|
|
260
|
+
anthropicRequest.tools = tools;
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
anthropicRequest.tools = request.tools;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Phase 2.8: Enable extended thinking for Claude 4+ models with reasoning support
|
|
267
|
+
// Requires beta header: anthropic-beta: interleaved-thinking-2025-05-14
|
|
268
|
+
// Skip if disableThinking option is set (used for continuation requests)
|
|
269
|
+
// reasoningEffort scales budget_tokens: none/undefined=10000 (default), low=5000, medium=10000, high=50000
|
|
270
|
+
// Note: 'none' means "no extra effort" = default budget. Thinking is always on for supported models.
|
|
271
|
+
if (modelConfig.reasoning?.supported && modelConfig.reasoning?.pattern === 'interleaved' && !disableThinking) {
|
|
272
|
+
// Opus 4.7/4.8 and Fable 5 REMOVED thinking.type=enabled / budget_tokens (400).
|
|
273
|
+
// They require thinking.type=adaptive (+ optional output_config.effort). Older
|
|
274
|
+
// Claude families still accept enabled.
|
|
275
|
+
const useAdaptive = ANTHROPIC_ADAPTIVE_THINKING_FAMILIES.has(modelConfig.family);
|
|
276
|
+
if (useAdaptive) {
|
|
277
|
+
// Opus 4.7/4.8 adaptive thinking defaults to display:'omitted' (empty thinking
|
|
278
|
+
// blocks, $0 reasoning tokens). DEBUG_THINKING opts into 'summarized' to surface
|
|
279
|
+
// reasoning in the CLI — costs extra output tokens, so it's off by default.
|
|
280
|
+
anthropicRequest.thinking =
|
|
281
|
+
process.env.DEBUG_THINKING === 'true'
|
|
282
|
+
? { type: 'adaptive', display: 'summarized' }
|
|
283
|
+
: { type: 'adaptive' };
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
const budgetMap = {
|
|
287
|
+
none: 10000,
|
|
288
|
+
low: 5000,
|
|
289
|
+
medium: 10000,
|
|
290
|
+
high: 50000,
|
|
291
|
+
};
|
|
292
|
+
const budgetTokens = reasoningEffort ? (budgetMap[reasoningEffort] || 10000) : 10000;
|
|
293
|
+
anthropicRequest.thinking = {
|
|
294
|
+
type: 'enabled',
|
|
295
|
+
budget_tokens: budgetTokens
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Build beta headers
|
|
300
|
+
const betaFeatures = [];
|
|
301
|
+
if (anthropicRequest.thinking) {
|
|
302
|
+
betaFeatures.push('interleaved-thinking-2025-05-14');
|
|
303
|
+
}
|
|
304
|
+
if (enableCaching) {
|
|
305
|
+
betaFeatures.push('prompt-caching-2024-07-31');
|
|
306
|
+
}
|
|
307
|
+
const headers = betaFeatures.length > 0
|
|
308
|
+
? { 'anthropic-beta': betaFeatures.join(',') }
|
|
309
|
+
: undefined;
|
|
310
|
+
const response = await this.anthropicClient.messages.create(anthropicRequest, { headers });
|
|
311
|
+
return {
|
|
312
|
+
data: response,
|
|
313
|
+
status: 200,
|
|
314
|
+
headers: {}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Initialize Anthropic client with OAuth or API key
|
|
319
|
+
*
|
|
320
|
+
* Priority:
|
|
321
|
+
* 1. ~/.claude/.credentials.json (OAuth from Claude.ai Max)
|
|
322
|
+
* 2. CLAUDE_CODE_OAUTH_TOKEN env var (OAuth override)
|
|
323
|
+
* 3. ANTHROPIC_API_KEY env var (API key fallback)
|
|
324
|
+
*/
|
|
325
|
+
initializeAnthropicClient() {
|
|
326
|
+
const authMethod = (process.env.ANTHROPIC_AUTH_METHOD || 'auto');
|
|
327
|
+
try {
|
|
328
|
+
const credential = anthropicCredentialService.loadCredential(authMethod);
|
|
329
|
+
if (process.env.DEBUG === 'true') {
|
|
330
|
+
console.log(`[APIClient] Using Anthropic credential: ${anthropicCredentialService.getCredentialSummary(credential)}`);
|
|
331
|
+
}
|
|
332
|
+
// Check for token expiry warning (7 days)
|
|
333
|
+
const daysUntilExpiry = anthropicCredentialService.getDaysUntilExpiry(credential);
|
|
334
|
+
if (daysUntilExpiry !== null && daysUntilExpiry <= 7) {
|
|
335
|
+
console.warn(`[APIClient] OAuth token expires in ${daysUntilExpiry} days. Run \`claude login\` to refresh.`);
|
|
336
|
+
}
|
|
337
|
+
// For OAuth tokens, use authToken parameter (Bearer auth)
|
|
338
|
+
// For API keys, use apiKey parameter (x-api-key header)
|
|
339
|
+
if (credential.type === 'oauth') {
|
|
340
|
+
return new Anthropic({
|
|
341
|
+
authToken: credential.token, // Uses Authorization: Bearer header
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
return new Anthropic({
|
|
346
|
+
apiKey: credential.token, // Uses x-api-key header
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
if (error instanceof CredentialError) {
|
|
352
|
+
throw new Error(`Anthropic authentication failed: ${error.message}`);
|
|
353
|
+
}
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Send request to XAI Messages API (Anthropic-compatible)
|
|
359
|
+
*/
|
|
360
|
+
async sendXAIMessagesAPI(request, modelConfig) {
|
|
361
|
+
const apiKey = process.env[modelConfig.api.apiKeyEnvVar];
|
|
362
|
+
if (!apiKey) {
|
|
363
|
+
throw new Error(`Missing API key: ${modelConfig.api.apiKeyEnvVar}`);
|
|
364
|
+
}
|
|
365
|
+
// Extract internal flags before spreading into API request
|
|
366
|
+
// XAI Messages API does NOT support Anthropic's thinking parameter —
|
|
367
|
+
// XAI models think natively (embedded <thinking> tags or thinking content blocks)
|
|
368
|
+
const { disableThinking, reasoningEffort, ...apiParameters } = request.parameters;
|
|
369
|
+
// Build request (Anthropic Messages API format)
|
|
370
|
+
const messagesRequest = {
|
|
371
|
+
model: request.modelId,
|
|
372
|
+
messages: request.messages,
|
|
373
|
+
max_tokens: apiParameters.max_tokens || apiParameters.maxTokens || 4096,
|
|
374
|
+
...apiParameters
|
|
375
|
+
};
|
|
376
|
+
if (request.systemMessage) {
|
|
377
|
+
messagesRequest.system = request.systemMessage;
|
|
378
|
+
}
|
|
379
|
+
if (request.tools && request.tools.length > 0) {
|
|
380
|
+
messagesRequest.tools = request.tools;
|
|
381
|
+
}
|
|
382
|
+
const debugPayloadXai = process.env.DEBUG_PAYLOAD;
|
|
383
|
+
if (debugPayloadXai === '1' || debugPayloadXai === '2') {
|
|
384
|
+
try {
|
|
385
|
+
const msgBytes = JSON.stringify(messagesRequest.messages || []).length;
|
|
386
|
+
const toolBytes = JSON.stringify(messagesRequest.tools || []).length;
|
|
387
|
+
const totalBytes = JSON.stringify(messagesRequest).length;
|
|
388
|
+
const sysBytes = messagesRequest.system ? JSON.stringify(messagesRequest.system).length : 0;
|
|
389
|
+
const toolCount = messagesRequest.tools?.length || 0;
|
|
390
|
+
const msgCount = messagesRequest.messages?.length || 0;
|
|
391
|
+
const lastUserMsg = messagesRequest.messages?.filter((m) => m.role === 'user').slice(-1)[0];
|
|
392
|
+
const lastUserBytes = lastUserMsg ? JSON.stringify(lastUserMsg).length : 0;
|
|
393
|
+
console.error(`[DEBUG_PAYLOAD-xai] model=${request.modelId} total=${totalBytes}B msgs=${msgCount}/${msgBytes}B tools=${toolCount}/${toolBytes}B sysMsg=${sysBytes}B lastUserMsg=${lastUserBytes}B`);
|
|
394
|
+
if (debugPayloadXai === '2') {
|
|
395
|
+
const breakdown = (messagesRequest.messages || []).map((m, i) => `${i}:${m.role}=${JSON.stringify(m).length}B`).join(' ');
|
|
396
|
+
console.error(`[DEBUG_PAYLOAD-xai] msg breakdown: ${breakdown}`);
|
|
397
|
+
if (messagesRequest.system) {
|
|
398
|
+
const sysContent = typeof messagesRequest.system === 'string' ? messagesRequest.system : JSON.stringify(messagesRequest.system);
|
|
399
|
+
console.error(`[DEBUG_PAYLOAD-xai] sysMsg first 600c: ${sysContent.slice(0, 600)}`);
|
|
400
|
+
console.error(`[DEBUG_PAYLOAD-xai] sysMsg last 400c: ${sysContent.slice(-400)}`);
|
|
401
|
+
}
|
|
402
|
+
if (lastUserMsg) {
|
|
403
|
+
const lastUserContent = typeof lastUserMsg.content === 'string' ? lastUserMsg.content : JSON.stringify(lastUserMsg.content);
|
|
404
|
+
// Find user prompts vs system-reminder boundaries
|
|
405
|
+
const blocks = [];
|
|
406
|
+
if (Array.isArray(lastUserMsg.content)) {
|
|
407
|
+
for (const block of lastUserMsg.content) {
|
|
408
|
+
const text = block.text || block.content || JSON.stringify(block);
|
|
409
|
+
const trimmed = text.length > 200 ? text.slice(0, 100) + '...[' + (text.length - 200) + 'B truncated]...' + text.slice(-100) : text;
|
|
410
|
+
const cacheMarker = block.cache_control ? ` cache=${JSON.stringify(block.cache_control)}` : '';
|
|
411
|
+
blocks.push(`{type=${block.type}${cacheMarker}, len=${text.length}: ${trimmed}}`);
|
|
412
|
+
}
|
|
413
|
+
console.error(`[DEBUG_PAYLOAD-xai] lastUserMsg blocks (${lastUserMsg.content.length}): ${blocks.join(' | ')}`);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
console.error(`[DEBUG_PAYLOAD-xai] lastUserMsg first 1500c: ${lastUserContent.slice(0, 1500)}`);
|
|
417
|
+
console.error(`[DEBUG_PAYLOAD-xai] lastUserMsg last 500c: ${lastUserContent.slice(-500)}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// Also dump first user message to see what user originally sent
|
|
421
|
+
const firstUserMsg = messagesRequest.messages?.find((m) => m.role === 'user');
|
|
422
|
+
if (firstUserMsg && firstUserMsg !== lastUserMsg) {
|
|
423
|
+
const firstUserContent = typeof firstUserMsg.content === 'string' ? firstUserMsg.content : JSON.stringify(firstUserMsg.content);
|
|
424
|
+
console.error(`[DEBUG_PAYLOAD-xai] firstUserMsg: ${firstUserContent.slice(0, 400)}`);
|
|
425
|
+
}
|
|
426
|
+
if (messagesRequest.tools && messagesRequest.tools.length > 0) {
|
|
427
|
+
const toolNames = messagesRequest.tools.map((t) => (t.name || t.function?.name) + '=' + JSON.stringify(t).length + 'B').join(', ');
|
|
428
|
+
console.error(`[DEBUG_PAYLOAD-xai] tools: ${toolNames}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
catch (e) {
|
|
433
|
+
console.error(`[DEBUG_PAYLOAD-xai] failed to log:`, e);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// Make raw HTTP request to XAI Messages API.
|
|
437
|
+
// `x-grok-conv-id` routes same-session requests to same server for cache hits —
|
|
438
|
+
// per XAI docs (advanced-api-usage/prompt-caching/maximizing-cache-hits).
|
|
439
|
+
const headers = {
|
|
440
|
+
'Content-Type': 'application/json',
|
|
441
|
+
[modelConfig.api.authHeader]: apiKey,
|
|
442
|
+
'anthropic-version': '2023-06-01'
|
|
443
|
+
};
|
|
444
|
+
if (request.conversationId) {
|
|
445
|
+
headers['x-grok-conv-id'] = request.conversationId;
|
|
446
|
+
}
|
|
447
|
+
const httpResponse = await fetch(modelConfig.api.endpoint, {
|
|
448
|
+
method: 'POST',
|
|
449
|
+
headers,
|
|
450
|
+
body: JSON.stringify(messagesRequest)
|
|
451
|
+
});
|
|
452
|
+
if (!httpResponse.ok) {
|
|
453
|
+
const errorText = await httpResponse.text();
|
|
454
|
+
throw new Error(`XAI Messages API error (${httpResponse.status}): ${errorText}`);
|
|
455
|
+
}
|
|
456
|
+
const data = await httpResponse.json();
|
|
457
|
+
// Post-process: extract <thinking> tags from text content into separate thinking blocks
|
|
458
|
+
// grok-4-1-fast-reasoning embeds thinking in text as <thinking>...</thinking> tags
|
|
459
|
+
if (data.content && Array.isArray(data.content)) {
|
|
460
|
+
data.content = this.extractThinkingFromTextBlocks(data.content);
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
data,
|
|
464
|
+
status: httpResponse.status,
|
|
465
|
+
headers: Object.fromEntries(httpResponse.headers.entries())
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Extract <thinking> tags from text content blocks into separate thinking content blocks.
|
|
470
|
+
* XAI grok-4-1-fast-reasoning embeds native thinking in text as <thinking>...</thinking> tags.
|
|
471
|
+
* This converts them to proper thinking blocks so the UI can display them correctly.
|
|
472
|
+
*/
|
|
473
|
+
extractThinkingFromTextBlocks(content) {
|
|
474
|
+
const result = [];
|
|
475
|
+
for (const block of content) {
|
|
476
|
+
if (block.type !== 'text' || !block.text) {
|
|
477
|
+
result.push(block);
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const text = block.text;
|
|
481
|
+
const thinkingRegex = /<thinking>([\s\S]*?)<\/thinking>/g;
|
|
482
|
+
let lastIndex = 0;
|
|
483
|
+
let match;
|
|
484
|
+
let hasThinking = false;
|
|
485
|
+
while ((match = thinkingRegex.exec(text)) !== null) {
|
|
486
|
+
hasThinking = true;
|
|
487
|
+
// Add any text before the thinking tag
|
|
488
|
+
const beforeText = text.slice(lastIndex, match.index).trim();
|
|
489
|
+
if (beforeText) {
|
|
490
|
+
result.push({ type: 'text', text: beforeText });
|
|
491
|
+
}
|
|
492
|
+
// Add thinking block
|
|
493
|
+
result.push({
|
|
494
|
+
type: 'thinking',
|
|
495
|
+
thinking: (match[1] ?? '').trim()
|
|
496
|
+
});
|
|
497
|
+
lastIndex = match.index + match[0].length;
|
|
498
|
+
}
|
|
499
|
+
if (hasThinking) {
|
|
500
|
+
// Add any remaining text after the last thinking tag
|
|
501
|
+
const afterText = text.slice(lastIndex).trim();
|
|
502
|
+
if (afterText) {
|
|
503
|
+
result.push({ type: 'text', text: afterText });
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
// No thinking tags — keep original block
|
|
508
|
+
result.push(block);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return result;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Round 7 (parallel-bench output): shared request-construction helper.
|
|
515
|
+
*
|
|
516
|
+
* `sendChatCompletionsAPI` and `streamChatCompletionsAPI` previously
|
|
517
|
+
* duplicated ~100 lines each (API key check, baseURL strip, OpenRouter
|
|
518
|
+
* headers, client construction, max_tokens→max_completion_tokens rename,
|
|
519
|
+
* reasoning_effort handling, tool attachment, DEBUG_PAYLOAD logging).
|
|
520
|
+
* This helper builds {client, chatRequest} for both callers; the streaming
|
|
521
|
+
* caller passes `stream: true` and continues into its generator loop.
|
|
522
|
+
*
|
|
523
|
+
* Behavior note (called out in commit): non-streaming previously did NOT
|
|
524
|
+
* reuse `this.openaiClient` for the openai provider — the unified helper
|
|
525
|
+
* does. This reduces socket churn and matches the streaming path; no
|
|
526
|
+
* external semantics change.
|
|
527
|
+
*/
|
|
528
|
+
buildChatCompletionsRequest(request, modelConfig, opts) {
|
|
529
|
+
const apiKey = process.env[modelConfig.api.apiKeyEnvVar];
|
|
530
|
+
if (!apiKey) {
|
|
531
|
+
throw new Error(`Missing API key: ${modelConfig.api.apiKeyEnvVar}`);
|
|
532
|
+
}
|
|
533
|
+
// Extract base URL (remove endpoint-specific paths)
|
|
534
|
+
const baseURL = modelConfig.api.endpoint
|
|
535
|
+
.replace('/chat/completions', '')
|
|
536
|
+
.replace('/messages', '')
|
|
537
|
+
.replace('/responses', '');
|
|
538
|
+
const defaultHeaders = {};
|
|
539
|
+
// Reuse cached openai client when possible (matches the streaming path's
|
|
540
|
+
// pre-refactor behavior; now applied uniformly).
|
|
541
|
+
const client = modelConfig.provider.toLowerCase() === 'openai' && this.openaiClient
|
|
542
|
+
? this.openaiClient
|
|
543
|
+
: new OpenAI({
|
|
544
|
+
apiKey,
|
|
545
|
+
baseURL,
|
|
546
|
+
...(Object.keys(defaultHeaders).length > 0 && { defaultHeaders })
|
|
547
|
+
});
|
|
548
|
+
// Phase 2.8: Exclude internal flags from API request parameters
|
|
549
|
+
const { disableThinking, ...apiParameters } = request.parameters;
|
|
550
|
+
// Transform max_tokens to max_completion_tokens if model requires it (GPT-5 family)
|
|
551
|
+
const maxTokensParamName = modelConfig.parameters?.maxTokens?.paramName || 'max_tokens';
|
|
552
|
+
let transformedParams = { ...apiParameters };
|
|
553
|
+
if (maxTokensParamName === 'max_completion_tokens') {
|
|
554
|
+
if ('max_tokens' in transformedParams) {
|
|
555
|
+
transformedParams.max_completion_tokens = transformedParams.max_tokens;
|
|
556
|
+
delete transformedParams.max_tokens;
|
|
557
|
+
}
|
|
558
|
+
if ('maxTokens' in transformedParams) {
|
|
559
|
+
transformedParams.max_completion_tokens = transformedParams.maxTokens;
|
|
560
|
+
delete transformedParams.maxTokens;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
// Extract reasoningEffort before spreading (custom param, not OpenAI)
|
|
564
|
+
const reasoningEffort = transformedParams.reasoningEffort || 'medium';
|
|
565
|
+
delete transformedParams.reasoningEffort;
|
|
566
|
+
// Build the request body
|
|
567
|
+
const chatRequest = {
|
|
568
|
+
model: request.modelId,
|
|
569
|
+
messages: request.messages,
|
|
570
|
+
...transformedParams,
|
|
571
|
+
...(opts.stream ? { stream: true } : {}),
|
|
572
|
+
};
|
|
573
|
+
// R19b: OpenAI chat/completions REJECTS `reasoning_effort` + tools for
|
|
574
|
+
// some gpt-5 variants (`gpt-5.4`, possibly others) with
|
|
575
|
+
// "400 Function tools with reasoning_effort are not supported for ... in /v1/chat/completions".
|
|
576
|
+
// Drop reasoning_effort silently when both are present on chat/completions;
|
|
577
|
+
// the model still works without explicit reasoning effort (defaults apply).
|
|
578
|
+
const willAttachTools = !!(request.tools && request.tools.length > 0);
|
|
579
|
+
const isChatCompletionsRoute = modelConfig.api.pattern === 'chat/completions';
|
|
580
|
+
const dropReasoningForToolCompat = willAttachTools && isChatCompletionsRoute && modelConfig.provider === 'openai';
|
|
581
|
+
// Enable reasoning for OpenAI models that support it (GPT-5 family, o-series)
|
|
582
|
+
if (modelConfig.reasoning?.supported &&
|
|
583
|
+
!disableThinking &&
|
|
584
|
+
reasoningEffort !== 'none' &&
|
|
585
|
+
!dropReasoningForToolCompat) {
|
|
586
|
+
chatRequest.reasoning_effort = reasoningEffort;
|
|
587
|
+
delete chatRequest.temperature;
|
|
588
|
+
delete chatRequest.top_p;
|
|
589
|
+
delete chatRequest.logprobs;
|
|
590
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
591
|
+
const tag = opts.stream ? 'streaming ' : '';
|
|
592
|
+
console.log(`[DEBUG APIClient] Reasoning ENABLED for ${tag}${modelConfig.id} (effort: ${reasoningEffort}, removed temperature/top_p)`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else if (dropReasoningForToolCompat && (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true')) {
|
|
596
|
+
console.log(`[DEBUG APIClient] Reasoning DROPPED for ${modelConfig.id} — chat/completions doesn't support reasoning_effort+tools combo (R19b)`);
|
|
597
|
+
}
|
|
598
|
+
else if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
599
|
+
const tag = opts.stream ? 'streaming ' : '';
|
|
600
|
+
console.log(`[DEBUG APIClient] Reasoning DISABLED for ${tag}${modelConfig.id} (effort: ${reasoningEffort}, disableThinking: ${disableThinking})`);
|
|
601
|
+
}
|
|
602
|
+
// Attach tools
|
|
603
|
+
if (request.tools && request.tools.length > 0) {
|
|
604
|
+
chatRequest.tools = request.tools;
|
|
605
|
+
}
|
|
606
|
+
this.logChatCompletionsPayload(chatRequest, request.modelId, opts.stream);
|
|
607
|
+
return { client, chatRequest };
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* DEBUG_PAYLOAD logging extracted from both send/stream methods.
|
|
611
|
+
* Same observable output as before; suffix `-stream` on the log tag
|
|
612
|
+
* distinguishes streaming-side breakdowns.
|
|
613
|
+
*/
|
|
614
|
+
logChatCompletionsPayload(chatRequest, modelId, streaming) {
|
|
615
|
+
const debugPayload = process.env.DEBUG_PAYLOAD;
|
|
616
|
+
if (debugPayload !== '1' && debugPayload !== '2')
|
|
617
|
+
return;
|
|
618
|
+
const tag = streaming ? 'DEBUG_PAYLOAD-stream' : 'DEBUG_PAYLOAD';
|
|
619
|
+
try {
|
|
620
|
+
const msgBytes = JSON.stringify(chatRequest.messages || []).length;
|
|
621
|
+
const toolBytes = JSON.stringify(chatRequest.tools || []).length;
|
|
622
|
+
const totalBytes = JSON.stringify(chatRequest).length;
|
|
623
|
+
const sysMsg = chatRequest.messages?.find((m) => m.role === 'system');
|
|
624
|
+
const sysBytes = sysMsg ? JSON.stringify(sysMsg).length : 0;
|
|
625
|
+
const toolCount = chatRequest.tools?.length || 0;
|
|
626
|
+
const msgCount = chatRequest.messages?.length || 0;
|
|
627
|
+
const lastUserMsg = chatRequest.messages?.filter((m) => m.role === 'user').slice(-1)[0];
|
|
628
|
+
const lastUserBytes = lastUserMsg ? JSON.stringify(lastUserMsg).length : 0;
|
|
629
|
+
console.error(`[${tag}] model=${modelId} total=${totalBytes}B msgs=${msgCount}/${msgBytes}B tools=${toolCount}/${toolBytes}B sysMsg=${sysBytes}B lastUserMsg=${lastUserBytes}B`);
|
|
630
|
+
if (debugPayload === '2') {
|
|
631
|
+
const breakdown = (chatRequest.messages || []).map((m, i) => `${i}:${m.role}=${JSON.stringify(m).length}B`).join(' ');
|
|
632
|
+
console.error(`[${tag}] msg breakdown: ${breakdown}`);
|
|
633
|
+
if (sysMsg) {
|
|
634
|
+
const previewLen = streaming ? 800 : 500;
|
|
635
|
+
const sysPreview = (typeof sysMsg.content === 'string' ? sysMsg.content : JSON.stringify(sysMsg.content)).slice(0, previewLen);
|
|
636
|
+
console.error(`[${tag}] sysMsg preview: ${sysPreview}`);
|
|
637
|
+
}
|
|
638
|
+
if (streaming && chatRequest.tools && chatRequest.tools.length > 0) {
|
|
639
|
+
const toolNames = chatRequest.tools.map((t) => (t.function?.name || t.name) + '=' + JSON.stringify(t).length + 'B').join(', ');
|
|
640
|
+
console.error(`[${tag}] tools: ${toolNames}`);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
catch (e) {
|
|
645
|
+
console.error(`[${tag}] failed to log:`, e);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Send request using Chat Completions API pattern
|
|
650
|
+
*
|
|
651
|
+
* Used by: OpenAI, XAI, DeepSeek, Groq
|
|
652
|
+
* Format: OpenAI Chat Completions API
|
|
653
|
+
*/
|
|
654
|
+
async sendChatCompletionsAPI(request, modelConfig) {
|
|
655
|
+
const { client, chatRequest } = this.buildChatCompletionsRequest(request, modelConfig, { stream: false });
|
|
656
|
+
// Send request
|
|
657
|
+
const response = await client.chat.completions.create(chatRequest);
|
|
658
|
+
return {
|
|
659
|
+
data: response,
|
|
660
|
+
status: 200,
|
|
661
|
+
headers: {}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Extract <system-reminder> content from Responses API input items.
|
|
666
|
+
*
|
|
667
|
+
* System messages are injected as <system-reminder> tags inside user message
|
|
668
|
+
* content by SystemMessageMiddleware. For the Responses API, we extract these
|
|
669
|
+
* into the dedicated `instructions` parameter so the provider can cache them
|
|
670
|
+
* separately. The tags are stripped from the user message to avoid duplication.
|
|
671
|
+
*
|
|
672
|
+
* Other API methods (Messages, Chat Completions) are unaffected — they continue
|
|
673
|
+
* to receive system context embedded in user content as before.
|
|
674
|
+
*/
|
|
675
|
+
extractSystemRemindersForResponsesAPI(inputItems) {
|
|
676
|
+
const systemReminderRegex = /<system-reminder>\n([\s\S]*?)\n<\/system-reminder>/g;
|
|
677
|
+
const extractedParts = [];
|
|
678
|
+
const cleanedItems = [];
|
|
679
|
+
for (const item of inputItems) {
|
|
680
|
+
if (item.type === 'message' && item.role === 'user' && Array.isArray(item.content)) {
|
|
681
|
+
const cleanedContent = [];
|
|
682
|
+
for (const block of item.content) {
|
|
683
|
+
if (block.type === 'input_text' && block.text) {
|
|
684
|
+
// Extract all <system-reminder> blocks from this text
|
|
685
|
+
let text = block.text;
|
|
686
|
+
let match;
|
|
687
|
+
const reminders = [];
|
|
688
|
+
// Reset regex state
|
|
689
|
+
systemReminderRegex.lastIndex = 0;
|
|
690
|
+
while ((match = systemReminderRegex.exec(text)) !== null) {
|
|
691
|
+
reminders.push(match[1] ?? '');
|
|
692
|
+
}
|
|
693
|
+
if (reminders.length > 0) {
|
|
694
|
+
extractedParts.push(...reminders);
|
|
695
|
+
// Strip the tags from the text
|
|
696
|
+
const strippedText = text.replace(systemReminderRegex, '').trim();
|
|
697
|
+
if (strippedText) {
|
|
698
|
+
cleanedContent.push({ ...block, text: strippedText });
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
cleanedContent.push(block);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
cleanedContent.push(block);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (cleanedContent.length > 0) {
|
|
710
|
+
cleanedItems.push({ ...item, content: cleanedContent });
|
|
711
|
+
}
|
|
712
|
+
// If all content was system-reminder, skip the empty message
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
cleanedItems.push(item);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
instructions: extractedParts.length > 0 ? extractedParts.join('\n\n') : undefined,
|
|
720
|
+
cleanedItems
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Send request using Responses API pattern
|
|
725
|
+
*
|
|
726
|
+
* Used by: OpenAI (gpt-5-codex), XAI (stateful)
|
|
727
|
+
* Format: OpenAI Responses API with server-side tools
|
|
728
|
+
*/
|
|
729
|
+
async sendResponsesAPI(request, modelConfig) {
|
|
730
|
+
const apiKey = process.env[modelConfig.api.apiKeyEnvVar];
|
|
731
|
+
if (!apiKey) {
|
|
732
|
+
throw new Error(`Missing API key: ${modelConfig.api.apiKeyEnvVar}`);
|
|
733
|
+
}
|
|
734
|
+
// Use provider-formatted input items directly from ResponsesAPIAdapter.toProviderMessages()
|
|
735
|
+
// The adapter already creates correctly typed items (message, function_call_output, etc.)
|
|
736
|
+
// Re-wrapping them would strip tool results and function calls, breaking the tool loop.
|
|
737
|
+
//
|
|
738
|
+
// Extract <system-reminder> content from user messages into `instructions` parameter.
|
|
739
|
+
// This lets the Responses API cache system context separately from conversation content.
|
|
740
|
+
const { instructions, cleanedItems: inputItems } = this.extractSystemRemindersForResponsesAPI(request.messages);
|
|
741
|
+
// Check reasoning support
|
|
742
|
+
const supportsReasoning = modelConfig.reasoning?.supported;
|
|
743
|
+
const { disableThinking, reasoningEffort, ...apiParameters } = request.parameters;
|
|
744
|
+
// Transform parameters: max_tokens / max_completion_tokens / maxTokens →
|
|
745
|
+
// max_output_tokens for Responses API.
|
|
746
|
+
// R20c: GPT-5 cards default to chat/completions and use
|
|
747
|
+
// max_completion_tokens. On a dynamic switch to Responses, that key was
|
|
748
|
+
// forwarded unchanged and OpenAI 400'd ("Unsupported parameter:
|
|
749
|
+
// 'max_completion_tokens'. In the Responses API, this parameter has moved
|
|
750
|
+
// to 'max_output_tokens'"). Normalize all three input forms.
|
|
751
|
+
let transformedParams = { ...apiParameters };
|
|
752
|
+
for (const oldKey of ['max_tokens', 'max_completion_tokens', 'maxTokens']) {
|
|
753
|
+
if (oldKey in transformedParams) {
|
|
754
|
+
transformedParams.max_output_tokens = transformedParams[oldKey];
|
|
755
|
+
delete transformedParams[oldKey];
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
// Remove model from params to prevent overwriting
|
|
759
|
+
const { model: _ignoredModel, ...paramsWithoutModel } = transformedParams;
|
|
760
|
+
// Use modelId from config (for alias resolution)
|
|
761
|
+
const actualModelId = modelConfig.modelId || request.modelId;
|
|
762
|
+
const isXAI = modelConfig.provider?.toLowerCase() === 'xai';
|
|
763
|
+
const responsesRequest = {
|
|
764
|
+
model: actualModelId,
|
|
765
|
+
input: inputItems,
|
|
766
|
+
...paramsWithoutModel
|
|
767
|
+
};
|
|
768
|
+
// Stateful chaining: use previous_response_id when available
|
|
769
|
+
// This lets the server preserve reasoning state across tool loop iterations,
|
|
770
|
+
// dramatically improving coherence for long tool chains (XAI, OpenAI).
|
|
771
|
+
if (request.previousResponseId) {
|
|
772
|
+
responsesRequest.previous_response_id = request.previousResponseId;
|
|
773
|
+
}
|
|
774
|
+
// XAI stores conversations server-side by default (store: true).
|
|
775
|
+
// Being explicit ensures server-side reasoning preservation.
|
|
776
|
+
if (isXAI) {
|
|
777
|
+
responsesRequest.store = true;
|
|
778
|
+
// Cache-routing: `prompt_cache_key` routes requests with same session id to
|
|
779
|
+
// the same server, maximizing cache hits. Per XAI docs
|
|
780
|
+
// (advanced-api-usage/prompt-caching/maximizing-cache-hits).
|
|
781
|
+
if (request.conversationId) {
|
|
782
|
+
responsesRequest.prompt_cache_key = request.conversationId;
|
|
783
|
+
}
|
|
784
|
+
// NOTE: tried adding `include: ["reasoning.encrypted_content"]` to capture
|
|
785
|
+
// encrypted reasoning for JSONL preservation (per XAI docs_guides_responses-api.md
|
|
786
|
+
// lines 93-95), but that triggered a regression where visible output tokens
|
|
787
|
+
// dropped to near-zero (~6 visible vs ~220 reasoning) on a basic 3-turn test.
|
|
788
|
+
// Deferred until we can investigate: possibly needs to be paired with
|
|
789
|
+
// different content-block wiring in ResponsesAPIAdapter, or only applied
|
|
790
|
+
// when we've explicitly asked for reasoning via the `reasoning` param.
|
|
791
|
+
}
|
|
792
|
+
// Set extracted system context as instructions (enables provider-side caching)
|
|
793
|
+
// XAI does NOT support the `instructions` parameter — skip for XAI provider.
|
|
794
|
+
if (instructions && !isXAI) {
|
|
795
|
+
responsesRequest.instructions = instructions;
|
|
796
|
+
}
|
|
797
|
+
// Add reasoning if supported and not disabled.
|
|
798
|
+
// Per-request reasoningEffort takes priority; fall back to card's defaultEffort.
|
|
799
|
+
// Models without defaultEffort (e.g. grok-4) get no reasoning block — grok-4
|
|
800
|
+
// rejects reasoning_effort per XAI docs.
|
|
801
|
+
const effectiveReasoningEffort = reasoningEffort || modelConfig.reasoning?.defaultEffort;
|
|
802
|
+
if (supportsReasoning && !disableThinking && effectiveReasoningEffort && effectiveReasoningEffort !== 'none') {
|
|
803
|
+
// R20: OpenAI Responses API rejects 'xhigh' (XAI-only effort level).
|
|
804
|
+
// Cross-provider sessions where the prior turn was XAI at xhigh would
|
|
805
|
+
// 400 on switch. Clamp at egress — standard cross-provider guard.
|
|
806
|
+
const effort = (!isXAI && effectiveReasoningEffort === 'xhigh') ? 'high' : effectiveReasoningEffort;
|
|
807
|
+
responsesRequest.reasoning = { effort, summary: 'auto' };
|
|
808
|
+
}
|
|
809
|
+
// Add tools if present (already in correct format from ResponsesAPIAdapter)
|
|
810
|
+
if (request.tools && request.tools.length > 0) {
|
|
811
|
+
responsesRequest.tools = request.tools;
|
|
812
|
+
}
|
|
813
|
+
if (process.env.DEBUG === 'true') {
|
|
814
|
+
const { input, tools, instructions: instr, ...requestSummary } = responsesRequest;
|
|
815
|
+
const inputTypes = (input || []).map((i) => i.type);
|
|
816
|
+
console.log(`[DEBUG APIClient] Responses API (non-streaming) request:`, JSON.stringify({
|
|
817
|
+
...requestSummary,
|
|
818
|
+
input: `[${input?.length || 0} items: ${inputTypes.join(', ')}]`,
|
|
819
|
+
tools: tools ? `[${tools.length} tools]` : undefined,
|
|
820
|
+
max_output_tokens: responsesRequest.max_output_tokens,
|
|
821
|
+
has_instructions: !!responsesRequest.instructions,
|
|
822
|
+
has_previous_response_id: !!responsesRequest.previous_response_id
|
|
823
|
+
}, null, 2));
|
|
824
|
+
}
|
|
825
|
+
// R27: XAI's /v1/responses route rejects the OpenAI SDK's request shape
|
|
826
|
+
// (404 "No handler found on route") and, without server-side tools, returns
|
|
827
|
+
// a body the SDK path surfaces as empty content. Use a raw fetch with
|
|
828
|
+
// Bearer auth — standard Responses API transport auth.
|
|
829
|
+
// OpenAI's own Responses API still uses the SDK (R20-proven, unaffected).
|
|
830
|
+
if (isXAI) {
|
|
831
|
+
const httpResponse = await fetch(modelConfig.api.endpoint, {
|
|
832
|
+
method: 'POST',
|
|
833
|
+
headers: {
|
|
834
|
+
'Content-Type': 'application/json',
|
|
835
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
836
|
+
},
|
|
837
|
+
body: JSON.stringify(responsesRequest),
|
|
838
|
+
});
|
|
839
|
+
if (!httpResponse.ok) {
|
|
840
|
+
const errorText = await httpResponse.text().catch(() => 'Unknown error');
|
|
841
|
+
throw new Error(`XAI Responses API error ${httpResponse.status}: ${errorText}`);
|
|
842
|
+
}
|
|
843
|
+
const data = await httpResponse.json();
|
|
844
|
+
return { data, status: 200, headers: {} };
|
|
845
|
+
}
|
|
846
|
+
// OpenAI Responses API — SDK path (R20-proven working)
|
|
847
|
+
const OpenAI = (await import('openai')).default;
|
|
848
|
+
const baseURL = modelConfig.api.endpoint.replace(/\/(messages|responses)$/, '');
|
|
849
|
+
const client = new OpenAI({ apiKey, baseURL });
|
|
850
|
+
const data = await client.responses.create(responsesRequest);
|
|
851
|
+
return {
|
|
852
|
+
data,
|
|
853
|
+
status: 200,
|
|
854
|
+
headers: {}
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Send request using GenerateContent API pattern
|
|
859
|
+
*
|
|
860
|
+
* Used by: Google Gemini (Vertex AI, AI Studio)
|
|
861
|
+
* Format: Google GenerativeAI GenerateContent API
|
|
862
|
+
*/
|
|
863
|
+
async sendGenerateContentAPI(request, modelConfig) {
|
|
864
|
+
const apiKey = process.env[modelConfig.api.apiKeyEnvVar];
|
|
865
|
+
if (!apiKey) {
|
|
866
|
+
throw new Error(`Missing API key: ${modelConfig.api.apiKeyEnvVar}`);
|
|
867
|
+
}
|
|
868
|
+
// For models with tools, use direct HTTP to ensure v1beta endpoint is used
|
|
869
|
+
// The SDK sometimes uses v1 which doesn't support tools for newer models
|
|
870
|
+
if (request.tools && request.tools.length > 0) {
|
|
871
|
+
return this.sendGenerateContentHTTP(request, modelConfig, apiKey);
|
|
872
|
+
}
|
|
873
|
+
// For non-tool requests, use SDK as before
|
|
874
|
+
if (!this.googleClient) {
|
|
875
|
+
this.googleClient = new GoogleGenerativeAI(apiKey);
|
|
876
|
+
}
|
|
877
|
+
// Get model - models/gemini-1.5-flash format
|
|
878
|
+
const modelName = request.modelId.startsWith('models/') ? request.modelId : `models/${request.modelId}`;
|
|
879
|
+
const model = this.googleClient.getGenerativeModel({ model: modelName });
|
|
880
|
+
// Build generation config (exclude model parameter - disableThinking is NOT an API parameter)
|
|
881
|
+
const { model: _model, ...generationConfig } = request.parameters;
|
|
882
|
+
// Build request
|
|
883
|
+
const geminiRequest = {
|
|
884
|
+
contents: request.messages,
|
|
885
|
+
generationConfig
|
|
886
|
+
};
|
|
887
|
+
// Send request
|
|
888
|
+
const result = await model.generateContent(geminiRequest);
|
|
889
|
+
const response = result.response;
|
|
890
|
+
// Return response with usageMetadata for proper token tracking
|
|
891
|
+
// The usageMetadata is at the response level, not nested
|
|
892
|
+
// Based on gemini-cli implementation (packages/core/src/core/turn.ts:308)
|
|
893
|
+
// and official docs: https://ai.google.dev/api/generate-content
|
|
894
|
+
return {
|
|
895
|
+
data: response, // response already has usageMetadata
|
|
896
|
+
status: 200,
|
|
897
|
+
headers: {}
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Send GenerateContent request via direct HTTP
|
|
902
|
+
* Ensures v1beta endpoint is used for tool support
|
|
903
|
+
*/
|
|
904
|
+
async sendGenerateContentHTTP(request, modelConfig, apiKey) {
|
|
905
|
+
// Get model ID (no models/ prefix - endpoint already includes it)
|
|
906
|
+
const modelId = request.modelId.startsWith('models/')
|
|
907
|
+
? request.modelId.replace('models/', '')
|
|
908
|
+
: request.modelId;
|
|
909
|
+
// Build URL using configured endpoint (should be v1beta)
|
|
910
|
+
// Endpoint already ends with /models, so just append modelId
|
|
911
|
+
const url = `${modelConfig.api.endpoint}/${modelId}:generateContent`;
|
|
912
|
+
// Build generation config (exclude model parameter - disableThinking is NOT an API parameter)
|
|
913
|
+
// Extract the nested generationConfig object from request.parameters
|
|
914
|
+
const { generationConfig } = request.parameters;
|
|
915
|
+
// Build request body (REST API uses snake_case)
|
|
916
|
+
const requestBody = {
|
|
917
|
+
contents: request.messages,
|
|
918
|
+
generation_config: generationConfig // Use the nested object directly
|
|
919
|
+
};
|
|
920
|
+
// Add tools if present
|
|
921
|
+
if (request.tools && request.tools.length > 0) {
|
|
922
|
+
// REST API uses snake_case "function_declarations", not camelCase "functionDeclarations"
|
|
923
|
+
requestBody.tools = [{ function_declarations: request.tools }];
|
|
924
|
+
// DEBUG: Log Gemini tool request details
|
|
925
|
+
if (process.env.DEBUG === 'true') {
|
|
926
|
+
console.log(`[DEBUG APIClient] Gemini HTTP request with ${request.tools.length} tools:`);
|
|
927
|
+
console.log(`[DEBUG APIClient] Tool names: ${request.tools.map((t) => t.name).join(', ')}`);
|
|
928
|
+
console.log(`[DEBUG APIClient] First tool structure:`, JSON.stringify(request.tools[0], null, 2));
|
|
929
|
+
console.log(`[DEBUG APIClient] Request body tools wrapper:`, JSON.stringify(requestBody.tools[0], null, 2).substring(0, 500));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
// Make HTTP request
|
|
933
|
+
const httpResponse = await fetch(url, {
|
|
934
|
+
method: 'POST',
|
|
935
|
+
headers: {
|
|
936
|
+
'Content-Type': 'application/json',
|
|
937
|
+
[modelConfig.api.authHeader]: apiKey
|
|
938
|
+
},
|
|
939
|
+
body: JSON.stringify(requestBody)
|
|
940
|
+
});
|
|
941
|
+
if (!httpResponse.ok) {
|
|
942
|
+
const errorText = await httpResponse.text();
|
|
943
|
+
throw new Error(`Google GenerateContent API error (${httpResponse.status}): ${errorText}`);
|
|
944
|
+
}
|
|
945
|
+
const data = await httpResponse.json();
|
|
946
|
+
// Return full response (same structure as SDK)
|
|
947
|
+
// The response has: candidates, usageMetadata, modelVersion, responseId
|
|
948
|
+
return {
|
|
949
|
+
data: data,
|
|
950
|
+
status: httpResponse.status,
|
|
951
|
+
headers: Object.fromEntries(httpResponse.headers.entries())
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Send request using Google GenAI API pattern
|
|
956
|
+
*
|
|
957
|
+
* Used by: Google Gemma (FREE models)
|
|
958
|
+
* Format: @google/genai simplified API
|
|
959
|
+
* Note: This is different from GenerateContent API used by Gemini
|
|
960
|
+
*/
|
|
961
|
+
async sendGoogleGenAIRequest(request, modelConfig) {
|
|
962
|
+
if (!this.googleGenAIClient) {
|
|
963
|
+
const apiKey = process.env[modelConfig.api.apiKeyEnvVar];
|
|
964
|
+
if (!apiKey) {
|
|
965
|
+
throw new Error(`Missing API key: ${modelConfig.api.apiKeyEnvVar}`);
|
|
966
|
+
}
|
|
967
|
+
this.googleGenAIClient = new GoogleGenAI({ apiKey });
|
|
968
|
+
}
|
|
969
|
+
// Build request for GoogleGenAI (simpler format)
|
|
970
|
+
// Convert messages array to conversation history
|
|
971
|
+
const messages = request.messages.map((msg) => ({
|
|
972
|
+
role: msg.role === 'model' ? 'model' : 'user',
|
|
973
|
+
content: typeof msg.content === 'string'
|
|
974
|
+
? msg.content
|
|
975
|
+
: msg.parts?.map((p) => p.text || '').join('') || ''
|
|
976
|
+
}));
|
|
977
|
+
// Use last message as prompt
|
|
978
|
+
const lastMessage = messages[messages.length - 1];
|
|
979
|
+
const prompt = lastMessage?.content || '';
|
|
980
|
+
// Send request using simplified API
|
|
981
|
+
const response = await this.googleGenAIClient.models.generateContent({
|
|
982
|
+
model: request.modelId,
|
|
983
|
+
contents: prompt
|
|
984
|
+
});
|
|
985
|
+
// Return response in standard format
|
|
986
|
+
// Note: GoogleGenAI doesn't provide detailed usage metadata
|
|
987
|
+
return {
|
|
988
|
+
data: {
|
|
989
|
+
text: response.text,
|
|
990
|
+
// Estimate token usage (no actual counts from this API)
|
|
991
|
+
usage: {
|
|
992
|
+
inputTokens: Math.ceil((prompt.length) / 4),
|
|
993
|
+
outputTokens: Math.ceil((response.text?.length || 0) / 4),
|
|
994
|
+
}
|
|
995
|
+
},
|
|
996
|
+
status: 200,
|
|
997
|
+
headers: {}
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Stream request using Messages API pattern
|
|
1002
|
+
*
|
|
1003
|
+
* Used by: Anthropic (native), XAI (Anthropic-compatible)
|
|
1004
|
+
* Format: Anthropic Messages API with SSE events
|
|
1005
|
+
*/
|
|
1006
|
+
streamMessagesAPI(request, modelConfig) {
|
|
1007
|
+
const provider = modelConfig.provider.toLowerCase();
|
|
1008
|
+
// Anthropic: Use native SDK with OAuth or API key + prompt caching
|
|
1009
|
+
if (provider === 'anthropic') {
|
|
1010
|
+
if (!this.anthropicClient) {
|
|
1011
|
+
this.anthropicClient = this.initializeAnthropicClient();
|
|
1012
|
+
}
|
|
1013
|
+
// Phase 2.8: Extract internal flags before building request
|
|
1014
|
+
const disableThinking = request.parameters.disableThinking;
|
|
1015
|
+
const reasoningEffort = request.parameters.reasoningEffort;
|
|
1016
|
+
// Exclude internal flags from API request parameters
|
|
1017
|
+
const { disableThinking: _, reasoningEffort: _re, ...apiParameters } = request.parameters;
|
|
1018
|
+
// Check if prompt caching is enabled
|
|
1019
|
+
const enableCaching = process.env.ANTHROPIC_PROMPT_CACHING !== 'false';
|
|
1020
|
+
// Build request (same as non-streaming)
|
|
1021
|
+
const anthropicRequest = {
|
|
1022
|
+
model: request.modelId,
|
|
1023
|
+
messages: request.messages,
|
|
1024
|
+
max_tokens: request.parameters.max_tokens || request.parameters.maxTokens || 4096,
|
|
1025
|
+
...apiParameters
|
|
1026
|
+
};
|
|
1027
|
+
// Add system message with cache_control for prompt caching
|
|
1028
|
+
if (request.systemMessage) {
|
|
1029
|
+
if (enableCaching) {
|
|
1030
|
+
anthropicRequest.system = [{
|
|
1031
|
+
type: 'text',
|
|
1032
|
+
text: request.systemMessage,
|
|
1033
|
+
cache_control: { type: 'ephemeral' }
|
|
1034
|
+
}];
|
|
1035
|
+
}
|
|
1036
|
+
else {
|
|
1037
|
+
anthropicRequest.system = request.systemMessage;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
// Add tools with cache_control on last tool for caching
|
|
1041
|
+
if (request.tools && request.tools.length > 0) {
|
|
1042
|
+
if (enableCaching) {
|
|
1043
|
+
const tools = request.tools.map((tool, index) => {
|
|
1044
|
+
if (index === request.tools.length - 1) {
|
|
1045
|
+
return { ...tool, cache_control: { type: 'ephemeral' } };
|
|
1046
|
+
}
|
|
1047
|
+
return tool;
|
|
1048
|
+
});
|
|
1049
|
+
anthropicRequest.tools = tools;
|
|
1050
|
+
}
|
|
1051
|
+
else {
|
|
1052
|
+
anthropicRequest.tools = request.tools;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
// Phase 2.8: Enable extended thinking for Claude 4+ models with reasoning support
|
|
1056
|
+
// Requires beta header: anthropic-beta: interleaved-thinking-2025-05-14
|
|
1057
|
+
// Skip if disableThinking option is set (used for continuation requests)
|
|
1058
|
+
// reasoningEffort scales budget_tokens: none/undefined=10000 (default), low=5000, medium=10000, high=50000
|
|
1059
|
+
// 'none' = default budget (thinking stays on). Anthropic thinking always enabled for supported models.
|
|
1060
|
+
if (modelConfig.reasoning?.supported && modelConfig.reasoning?.pattern === 'interleaved' && !disableThinking) {
|
|
1061
|
+
// Opus 4.7/4.8 and Fable 5 require adaptive thinking — enabled/budget_tokens
|
|
1062
|
+
// 400s (parity with the non-streaming path above).
|
|
1063
|
+
const useAdaptive = ANTHROPIC_ADAPTIVE_THINKING_FAMILIES.has(modelConfig.family);
|
|
1064
|
+
if (useAdaptive) {
|
|
1065
|
+
// display:'summarized' surfaces reasoning in the CLI but costs extra output
|
|
1066
|
+
// tokens; default 'omitted' streams empty thinking blocks for $0. Gated on
|
|
1067
|
+
// DEBUG_THINKING (parity with the non-streaming path above).
|
|
1068
|
+
anthropicRequest.thinking =
|
|
1069
|
+
process.env.DEBUG_THINKING === 'true'
|
|
1070
|
+
? { type: 'adaptive', display: 'summarized' }
|
|
1071
|
+
: { type: 'adaptive' };
|
|
1072
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1073
|
+
console.log(`[DEBUG APIClient] Adaptive thinking for ${modelConfig.id}${process.env.DEBUG_THINKING === 'true' ? ' (display: summarized)' : ''}`);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
const budgetMap = {
|
|
1078
|
+
none: 10000, // default budget — thinking stays on
|
|
1079
|
+
low: 5000,
|
|
1080
|
+
medium: 10000,
|
|
1081
|
+
high: 50000,
|
|
1082
|
+
};
|
|
1083
|
+
const budgetTokens = reasoningEffort ? (budgetMap[reasoningEffort] || 10000) : 10000;
|
|
1084
|
+
anthropicRequest.thinking = {
|
|
1085
|
+
type: 'enabled',
|
|
1086
|
+
budget_tokens: budgetTokens
|
|
1087
|
+
};
|
|
1088
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1089
|
+
console.log(`[DEBUG APIClient] Thinking ENABLED for ${modelConfig.id} (budget: ${budgetTokens}, effort: ${reasoningEffort || 'default'})`);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
else if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1094
|
+
console.log(`[DEBUG APIClient] Thinking NOT enabled: supported=${modelConfig.reasoning?.supported}, pattern=${modelConfig.reasoning?.pattern}, disableThinking=${disableThinking}`);
|
|
1095
|
+
}
|
|
1096
|
+
// PTC: Add container for session persistence
|
|
1097
|
+
const enablePTC = request.parameters.enablePTC;
|
|
1098
|
+
const ptcContainerId = request.parameters.containerId;
|
|
1099
|
+
if (enablePTC && modelConfig.supportsPTC) {
|
|
1100
|
+
if (ptcContainerId) {
|
|
1101
|
+
anthropicRequest.container = { id: ptcContainerId };
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
anthropicRequest.container = {};
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
// Build beta headers for thinking, tool streaming, and caching
|
|
1108
|
+
const betaFeatures = [];
|
|
1109
|
+
if (anthropicRequest.thinking) {
|
|
1110
|
+
betaFeatures.push('interleaved-thinking-2025-05-14');
|
|
1111
|
+
betaFeatures.push('fine-grained-tool-streaming-2025-05-14');
|
|
1112
|
+
}
|
|
1113
|
+
if (enablePTC && modelConfig.supportsPTC) {
|
|
1114
|
+
betaFeatures.push('code-execution-2026-01-20');
|
|
1115
|
+
}
|
|
1116
|
+
if (enableCaching) {
|
|
1117
|
+
betaFeatures.push('prompt-caching-2024-07-31');
|
|
1118
|
+
}
|
|
1119
|
+
const headers = betaFeatures.length > 0
|
|
1120
|
+
? { 'anthropic-beta': betaFeatures.join(',') }
|
|
1121
|
+
: undefined;
|
|
1122
|
+
const stream = this.anthropicClient.messages.stream(anthropicRequest, { headers });
|
|
1123
|
+
// Capture 'this' context before entering async generator
|
|
1124
|
+
const self = this;
|
|
1125
|
+
// Track thinking blocks and signatures for proper accumulation
|
|
1126
|
+
// SDK's finalMessage() doesn't include signatures streamed via signature_delta
|
|
1127
|
+
// This map is shared between the generator and finalMessage handler
|
|
1128
|
+
const thinkingSignatures = new Map();
|
|
1129
|
+
// SDK's finalMessage() might not accumulate tool inputs correctly with beta features
|
|
1130
|
+
// Track accumulated inputs by block index for injection into finalMessage
|
|
1131
|
+
const toolInputs = new Map();
|
|
1132
|
+
// Create async generator for chunks
|
|
1133
|
+
const chunks = async function* () {
|
|
1134
|
+
// Phase 2.8: Track tool use accumulation for streaming tool execution
|
|
1135
|
+
let currentToolUse = null;
|
|
1136
|
+
let partialJson = '';
|
|
1137
|
+
let currentToolUseIndex = -1;
|
|
1138
|
+
// Track current thinking block for signature association
|
|
1139
|
+
let currentThinkingIndex = -1;
|
|
1140
|
+
let signatureAccumulator = '';
|
|
1141
|
+
try {
|
|
1142
|
+
// Use SDK's async iteration for events
|
|
1143
|
+
for await (const event of stream) {
|
|
1144
|
+
// DEBUG: Log important event types (skip repetitive text_delta)
|
|
1145
|
+
if (process.env.DEBUG_THINKING === 'true') {
|
|
1146
|
+
const delta = event.delta;
|
|
1147
|
+
if (event.type === 'content_block_delta') {
|
|
1148
|
+
// Only log non-text delta types to reduce noise
|
|
1149
|
+
if (delta?.type && delta.type !== 'text_delta') {
|
|
1150
|
+
console.log(`[DEBUG APIClient] Claude event: ${event.type}, delta.type: ${delta?.type}`);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
else if (event.type === 'content_block_start') {
|
|
1154
|
+
if (process.env.DEBUG === 'true')
|
|
1155
|
+
console.log(`[DEBUG APIClient] Claude event: ${event.type}, content_block.type: ${event.content_block?.type}, index: ${event.index}`);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
|
|
1159
|
+
yield {
|
|
1160
|
+
type: 'text_delta',
|
|
1161
|
+
delta: event.delta.text,
|
|
1162
|
+
data: event
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
else if (event.type === 'content_block_delta' && event.delta.type === 'thinking_delta') {
|
|
1166
|
+
// Anthropic Claude extended thinking (interleaved reasoning)
|
|
1167
|
+
yield {
|
|
1168
|
+
type: 'content_block_delta',
|
|
1169
|
+
delta: event.delta.thinking,
|
|
1170
|
+
data: { ...event, reasoning: true }
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
else if (event.type === 'content_block_start') {
|
|
1174
|
+
// Track content block index from event
|
|
1175
|
+
const blockIndex = event.index;
|
|
1176
|
+
// Phase 2.8: Check if this is a tool_use block
|
|
1177
|
+
if (event.content_block?.type === 'tool_use') {
|
|
1178
|
+
currentToolUse = {
|
|
1179
|
+
id: event.content_block.id,
|
|
1180
|
+
name: event.content_block.name,
|
|
1181
|
+
input: {}
|
|
1182
|
+
};
|
|
1183
|
+
currentToolUseIndex = blockIndex;
|
|
1184
|
+
partialJson = '';
|
|
1185
|
+
}
|
|
1186
|
+
else if (event.content_block?.type === 'server_tool_use') {
|
|
1187
|
+
// PTC: Server-side tool use — accumulate same as tool_use
|
|
1188
|
+
currentToolUse = {
|
|
1189
|
+
id: event.content_block.id,
|
|
1190
|
+
name: event.content_block.name,
|
|
1191
|
+
input: {}
|
|
1192
|
+
};
|
|
1193
|
+
currentToolUseIndex = blockIndex;
|
|
1194
|
+
partialJson = '';
|
|
1195
|
+
}
|
|
1196
|
+
// Track thinking blocks for signature association
|
|
1197
|
+
if (event.content_block?.type === 'thinking') {
|
|
1198
|
+
currentThinkingIndex = blockIndex;
|
|
1199
|
+
signatureAccumulator = '';
|
|
1200
|
+
}
|
|
1201
|
+
yield {
|
|
1202
|
+
type: 'content_block_start',
|
|
1203
|
+
data: event
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
else if (event.type === 'content_block_delta' && event.delta.type === 'signature_delta') {
|
|
1207
|
+
// Accumulate signature for current thinking block
|
|
1208
|
+
signatureAccumulator += event.delta.signature || '';
|
|
1209
|
+
}
|
|
1210
|
+
else if (event.type === 'content_block_delta' && event.delta.type === 'input_json_delta') {
|
|
1211
|
+
// Phase 2.8: Accumulate tool input JSON incrementally
|
|
1212
|
+
partialJson += event.delta.partial_json;
|
|
1213
|
+
// Try parsing accumulated JSON
|
|
1214
|
+
try {
|
|
1215
|
+
currentToolUse.input = JSON.parse(partialJson);
|
|
1216
|
+
}
|
|
1217
|
+
catch {
|
|
1218
|
+
// JSON not complete yet, continue accumulating
|
|
1219
|
+
}
|
|
1220
|
+
yield {
|
|
1221
|
+
type: 'content_block_delta',
|
|
1222
|
+
delta: event.delta.partial_json,
|
|
1223
|
+
data: event
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
else if (event.type === 'content_block_stop') {
|
|
1227
|
+
// Phase 2.8: If we have a complete tool use, emit it
|
|
1228
|
+
if (currentToolUse) {
|
|
1229
|
+
// If incremental JSON parsing never succeeded, try repair on the full accumulated string
|
|
1230
|
+
if ((!currentToolUse.input || Object.keys(currentToolUse.input).length === 0) && partialJson.length > 0) {
|
|
1231
|
+
try {
|
|
1232
|
+
currentToolUse.input = self.repairTruncatedJSON(partialJson);
|
|
1233
|
+
}
|
|
1234
|
+
catch {
|
|
1235
|
+
console.error(`[APIClient Anthropic] Failed to repair tool JSON (${partialJson.length} chars): ${partialJson.slice(0, 200)}...`);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
const decodedInput = self.decodeHtmlEntitiesInObject(currentToolUse.input);
|
|
1239
|
+
currentToolUse.input = decodedInput;
|
|
1240
|
+
// Save accumulated input for injection into finalMessage
|
|
1241
|
+
// SDK's finalMessage might not accumulate input_json_delta correctly with beta features
|
|
1242
|
+
if (currentToolUseIndex >= 0 && decodedInput && Object.keys(decodedInput).length > 0) {
|
|
1243
|
+
toolInputs.set(currentToolUseIndex, decodedInput);
|
|
1244
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1245
|
+
console.log(`[DEBUG APIClient] Captured tool input for block ${currentToolUseIndex}, keys: ${Object.keys(decodedInput).join(', ')}`);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
yield {
|
|
1249
|
+
type: 'tool_use_complete',
|
|
1250
|
+
toolUse: currentToolUse,
|
|
1251
|
+
data: event
|
|
1252
|
+
};
|
|
1253
|
+
// Reset for next tool use
|
|
1254
|
+
currentToolUse = null;
|
|
1255
|
+
currentToolUseIndex = -1;
|
|
1256
|
+
partialJson = '';
|
|
1257
|
+
}
|
|
1258
|
+
// Save accumulated signature for thinking block
|
|
1259
|
+
if (currentThinkingIndex >= 0 && signatureAccumulator) {
|
|
1260
|
+
thinkingSignatures.set(currentThinkingIndex, signatureAccumulator);
|
|
1261
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1262
|
+
console.log(`[DEBUG APIClient] Captured signature for thinking block ${currentThinkingIndex}, length: ${signatureAccumulator.length}`);
|
|
1263
|
+
}
|
|
1264
|
+
currentThinkingIndex = -1;
|
|
1265
|
+
signatureAccumulator = '';
|
|
1266
|
+
}
|
|
1267
|
+
yield {
|
|
1268
|
+
type: 'content_block_stop',
|
|
1269
|
+
data: event
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
else if (event.type === 'message_start') {
|
|
1273
|
+
yield {
|
|
1274
|
+
type: 'message_start',
|
|
1275
|
+
data: event
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
else if (event.type === 'message_delta') {
|
|
1279
|
+
yield {
|
|
1280
|
+
type: 'message_delta',
|
|
1281
|
+
data: event
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
else if (event.type === 'message_stop') {
|
|
1285
|
+
yield {
|
|
1286
|
+
type: 'message_stop',
|
|
1287
|
+
data: event
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
catch (streamError) {
|
|
1293
|
+
// Handle stream errors gracefully (e.g., "request ended without sending any chunks")
|
|
1294
|
+
const errorMessage = streamError?.message || 'Unknown streaming error';
|
|
1295
|
+
console.error(`[APIClient Anthropic] Stream error: ${errorMessage}`);
|
|
1296
|
+
// Yield an error event that the orchestrator can handle
|
|
1297
|
+
yield {
|
|
1298
|
+
type: 'error',
|
|
1299
|
+
error: {
|
|
1300
|
+
type: 'stream_error',
|
|
1301
|
+
message: `API stream failed: ${errorMessage}. This is usually a transient error - please retry.`
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
}();
|
|
1306
|
+
// Get final message promise (SDK accumulates internally)
|
|
1307
|
+
// Inject signatures that were captured from signature_delta events
|
|
1308
|
+
const finalMessage = stream.finalMessage().then(msg => {
|
|
1309
|
+
// Inject captured signatures into thinking blocks
|
|
1310
|
+
// The SDK doesn't accumulate signature_delta into the final message
|
|
1311
|
+
if (msg.content && thinkingSignatures.size > 0) {
|
|
1312
|
+
let thinkingBlockIndex = 0;
|
|
1313
|
+
for (let i = 0; i < msg.content.length; i++) {
|
|
1314
|
+
const block = msg.content[i];
|
|
1315
|
+
if (block.type === 'thinking') {
|
|
1316
|
+
const signature = thinkingSignatures.get(i);
|
|
1317
|
+
if (signature) {
|
|
1318
|
+
block.signature = signature;
|
|
1319
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1320
|
+
console.log(`[DEBUG APIClient] Injected signature into thinking block ${i}`);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
thinkingBlockIndex++;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
// Inject captured tool inputs into tool_use blocks
|
|
1328
|
+
// SDK's finalMessage might not accumulate input_json_delta correctly with beta features
|
|
1329
|
+
if (msg.content && toolInputs.size > 0) {
|
|
1330
|
+
for (let i = 0; i < msg.content.length; i++) {
|
|
1331
|
+
const block = msg.content[i];
|
|
1332
|
+
if (block.type === 'tool_use') {
|
|
1333
|
+
const accumulatedInput = toolInputs.get(i);
|
|
1334
|
+
// Only inject if SDK's input is empty but we have accumulated input
|
|
1335
|
+
if (accumulatedInput && (!block.input || Object.keys(block.input).length === 0)) {
|
|
1336
|
+
block.input = accumulatedInput;
|
|
1337
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1338
|
+
console.log(`[DEBUG APIClient] Injected accumulated tool input into block ${i}, keys: ${Object.keys(accumulatedInput).join(', ')}`);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
// DEBUG: Log final message content to diagnose thinking block signatures
|
|
1345
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1346
|
+
console.log(`[DEBUG APIClient] Claude finalMessage content types:`, msg.content?.map((b) => `${b.type}${b.type === 'thinking' ? ` (sig: ${b.signature ? 'yes' : 'no'})` : ''}`));
|
|
1347
|
+
// Log tool_use input to debug parameter extraction
|
|
1348
|
+
for (const block of msg.content || []) {
|
|
1349
|
+
if (block.type === 'tool_use') {
|
|
1350
|
+
console.log(`[DEBUG APIClient] Tool use block:`, {
|
|
1351
|
+
id: block.id,
|
|
1352
|
+
name: block.name,
|
|
1353
|
+
input: block.input,
|
|
1354
|
+
inputKeys: Object.keys(block.input || {})
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return msg;
|
|
1360
|
+
});
|
|
1361
|
+
return {
|
|
1362
|
+
chunks,
|
|
1363
|
+
finalMessage
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
// XAI Messages API: Anthropic-compatible, use Anthropic SDK with custom baseURL
|
|
1367
|
+
if (provider === 'xai') {
|
|
1368
|
+
const apiKey = process.env[modelConfig.api.apiKeyEnvVar];
|
|
1369
|
+
if (!apiKey) {
|
|
1370
|
+
throw new Error(`Missing API key: ${modelConfig.api.apiKeyEnvVar}`);
|
|
1371
|
+
}
|
|
1372
|
+
// Create Anthropic client with XAI base URL.
|
|
1373
|
+
// `x-grok-conv-id` default header routes same-session requests to same server
|
|
1374
|
+
// for cache hits — per XAI docs (advanced-api-usage/prompt-caching/maximizing-cache-hits).
|
|
1375
|
+
const xaiDefaultHeaders = {};
|
|
1376
|
+
if (request.conversationId) {
|
|
1377
|
+
xaiDefaultHeaders['x-grok-conv-id'] = request.conversationId;
|
|
1378
|
+
}
|
|
1379
|
+
const xaiClient = new Anthropic({
|
|
1380
|
+
apiKey,
|
|
1381
|
+
baseURL: modelConfig.api.endpoint.replace('/v1/messages', ''), // Strip path, keep base
|
|
1382
|
+
defaultHeaders: xaiDefaultHeaders
|
|
1383
|
+
});
|
|
1384
|
+
// Extract internal flags before spreading into API request
|
|
1385
|
+
// XAI Messages API does NOT support Anthropic's thinking parameter —
|
|
1386
|
+
// XAI models think natively (embedded <thinking> tags or thinking content blocks)
|
|
1387
|
+
const { disableThinking: _xaiDisableThinking, reasoningEffort: _xaiReasoningEffort, ...xaiApiParameters } = request.parameters;
|
|
1388
|
+
// Build request (same format as Anthropic)
|
|
1389
|
+
const anthropicRequest = {
|
|
1390
|
+
model: request.modelId,
|
|
1391
|
+
messages: request.messages,
|
|
1392
|
+
max_tokens: xaiApiParameters.max_tokens || xaiApiParameters.maxTokens || 4096,
|
|
1393
|
+
...xaiApiParameters
|
|
1394
|
+
};
|
|
1395
|
+
if (request.systemMessage) {
|
|
1396
|
+
anthropicRequest.system = request.systemMessage;
|
|
1397
|
+
}
|
|
1398
|
+
if (request.tools && request.tools.length > 0) {
|
|
1399
|
+
// XAI requires complete tool schemas with descriptions for ALL tools and parameters
|
|
1400
|
+
anthropicRequest.tools = this.validateXAITools(request.tools);
|
|
1401
|
+
}
|
|
1402
|
+
// Use SDK streaming (same as Anthropic!)
|
|
1403
|
+
const stream = xaiClient.messages.stream(anthropicRequest);
|
|
1404
|
+
// Phase 2.8: XAI Bug Fix - Build our own finalMessage because SDK doesn't accumulate tool inputs
|
|
1405
|
+
const accumulatedContent = [];
|
|
1406
|
+
let textAccumulator = '';
|
|
1407
|
+
let thinkingAccumulator = '';
|
|
1408
|
+
let streamCompleteResolve;
|
|
1409
|
+
const streamCompletePromise = new Promise(resolve => {
|
|
1410
|
+
streamCompleteResolve = resolve;
|
|
1411
|
+
});
|
|
1412
|
+
// Create async generator for chunks (same pattern as Anthropic)
|
|
1413
|
+
const chunks = async function* () {
|
|
1414
|
+
// Phase 2.8: Track tool use accumulation
|
|
1415
|
+
let currentToolUse = null;
|
|
1416
|
+
let partialJson = '';
|
|
1417
|
+
let currentBlockType = null;
|
|
1418
|
+
let redactedThinkingData = '';
|
|
1419
|
+
// R28c (Cache-Hit Contract Rule 2): capture the thinking-block
|
|
1420
|
+
// signature from signature_delta so it round-trips to XAI. Omitting
|
|
1421
|
+
// reasoning signature is xAI's documented #1 cause of cache misses.
|
|
1422
|
+
// The Anthropic path already does this (signatureAccumulator); the
|
|
1423
|
+
// XAI stream previously did not — that broke the XAI prompt cache.
|
|
1424
|
+
let xaiSignatureAccumulator = '';
|
|
1425
|
+
try {
|
|
1426
|
+
// Use SDK's async iteration for events (same as Anthropic)
|
|
1427
|
+
for await (const event of stream) {
|
|
1428
|
+
if (event.type === 'content_block_start') {
|
|
1429
|
+
currentBlockType = event.content_block?.type;
|
|
1430
|
+
// Phase 2.8: Check if this is a tool_use block
|
|
1431
|
+
if (event.content_block?.type === 'tool_use') {
|
|
1432
|
+
currentToolUse = {
|
|
1433
|
+
id: event.content_block.id,
|
|
1434
|
+
name: event.content_block.name,
|
|
1435
|
+
input: {}
|
|
1436
|
+
};
|
|
1437
|
+
partialJson = '';
|
|
1438
|
+
}
|
|
1439
|
+
else if (event.content_block?.type === 'server_tool_use') {
|
|
1440
|
+
// PTC: Server-side tool use — accumulate same as tool_use
|
|
1441
|
+
currentToolUse = {
|
|
1442
|
+
id: event.content_block.id,
|
|
1443
|
+
name: event.content_block.name,
|
|
1444
|
+
input: {}
|
|
1445
|
+
};
|
|
1446
|
+
partialJson = '';
|
|
1447
|
+
}
|
|
1448
|
+
else if (event.content_block?.type === 'text') {
|
|
1449
|
+
textAccumulator = '';
|
|
1450
|
+
}
|
|
1451
|
+
else if (event.content_block?.type === 'thinking') {
|
|
1452
|
+
thinkingAccumulator = '';
|
|
1453
|
+
}
|
|
1454
|
+
else if (event.content_block?.type === 'redacted_thinking') {
|
|
1455
|
+
// XAI grok-4/4.1 encrypted reasoning — accumulate data field
|
|
1456
|
+
redactedThinkingData = event.content_block?.data || '';
|
|
1457
|
+
}
|
|
1458
|
+
else if (event.content_block?.type === 'code_execution_tool_result') {
|
|
1459
|
+
// PTC: Code execution result — accumulate text output
|
|
1460
|
+
textAccumulator = '';
|
|
1461
|
+
}
|
|
1462
|
+
yield {
|
|
1463
|
+
type: 'content_block_start',
|
|
1464
|
+
data: event
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
else if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
|
|
1468
|
+
textAccumulator += event.delta.text;
|
|
1469
|
+
yield {
|
|
1470
|
+
type: 'text_delta',
|
|
1471
|
+
delta: event.delta.text,
|
|
1472
|
+
data: event
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
else if (event.type === 'content_block_delta' && event.delta.type === 'thinking_delta') {
|
|
1476
|
+
// XAI Grok reasoning traces (grok-code-fast-1 exposes plaintext thinking)
|
|
1477
|
+
thinkingAccumulator += event.delta.thinking;
|
|
1478
|
+
yield {
|
|
1479
|
+
type: 'content_block_delta',
|
|
1480
|
+
delta: event.delta.thinking,
|
|
1481
|
+
data: { ...event, reasoning: true }
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
else if (event.type === 'content_block_delta' && event.delta.type === 'redacted_thinking_delta') {
|
|
1485
|
+
// XAI grok-4/4.1 encrypted reasoning delta
|
|
1486
|
+
redactedThinkingData += event.delta.data || '';
|
|
1487
|
+
yield {
|
|
1488
|
+
type: 'content_block_delta',
|
|
1489
|
+
delta: event.delta.data || '',
|
|
1490
|
+
data: { ...event, reasoning: true, redacted: true }
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
else if (event.type === 'content_block_delta' && event.delta.type === 'signature_delta') {
|
|
1494
|
+
// R28c: accumulate the thinking-block signature (Cache-Hit Contract Rule 2)
|
|
1495
|
+
xaiSignatureAccumulator += event.delta.signature || '';
|
|
1496
|
+
}
|
|
1497
|
+
else if (event.type === 'content_block_delta' && event.delta.type === 'input_json_delta') {
|
|
1498
|
+
// Phase 2.8: Accumulate tool input JSON incrementally
|
|
1499
|
+
partialJson += event.delta.partial_json;
|
|
1500
|
+
// Try parsing accumulated JSON
|
|
1501
|
+
try {
|
|
1502
|
+
currentToolUse.input = JSON.parse(partialJson);
|
|
1503
|
+
}
|
|
1504
|
+
catch {
|
|
1505
|
+
// JSON not complete yet, continue accumulating
|
|
1506
|
+
}
|
|
1507
|
+
yield {
|
|
1508
|
+
type: 'content_block_delta',
|
|
1509
|
+
delta: event.delta.partial_json,
|
|
1510
|
+
data: event
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
else if (event.type === 'content_block_stop') {
|
|
1514
|
+
// Phase 2.8: Save completed content block to our accumulator
|
|
1515
|
+
if (currentBlockType === 'text' && textAccumulator) {
|
|
1516
|
+
accumulatedContent.push({ type: 'text', text: textAccumulator });
|
|
1517
|
+
}
|
|
1518
|
+
else if (currentBlockType === 'thinking' && thinkingAccumulator) {
|
|
1519
|
+
// R28c: attach the captured signature so it round-trips to XAI (Rule 2)
|
|
1520
|
+
accumulatedContent.push({
|
|
1521
|
+
type: 'thinking',
|
|
1522
|
+
thinking: thinkingAccumulator,
|
|
1523
|
+
...(xaiSignatureAccumulator && { signature: xaiSignatureAccumulator })
|
|
1524
|
+
});
|
|
1525
|
+
xaiSignatureAccumulator = '';
|
|
1526
|
+
}
|
|
1527
|
+
else if (currentBlockType === 'redacted_thinking' && redactedThinkingData) {
|
|
1528
|
+
accumulatedContent.push({ type: 'redacted_thinking', data: redactedThinkingData });
|
|
1529
|
+
redactedThinkingData = '';
|
|
1530
|
+
}
|
|
1531
|
+
else if (currentBlockType === 'tool_use' && currentToolUse) {
|
|
1532
|
+
// Use our accumulated input, not the SDK's empty one!
|
|
1533
|
+
accumulatedContent.push({
|
|
1534
|
+
type: 'tool_use',
|
|
1535
|
+
id: currentToolUse.id,
|
|
1536
|
+
name: currentToolUse.name,
|
|
1537
|
+
input: currentToolUse.input
|
|
1538
|
+
});
|
|
1539
|
+
yield {
|
|
1540
|
+
type: 'tool_use_complete',
|
|
1541
|
+
toolUse: currentToolUse,
|
|
1542
|
+
data: event
|
|
1543
|
+
};
|
|
1544
|
+
// Reset for next tool use
|
|
1545
|
+
currentToolUse = null;
|
|
1546
|
+
partialJson = '';
|
|
1547
|
+
}
|
|
1548
|
+
else if (currentBlockType === 'server_tool_use' && currentToolUse) {
|
|
1549
|
+
// PTC: Server-side tool use — push as server_tool_use
|
|
1550
|
+
accumulatedContent.push({
|
|
1551
|
+
type: 'server_tool_use',
|
|
1552
|
+
id: currentToolUse.id,
|
|
1553
|
+
name: currentToolUse.name,
|
|
1554
|
+
input: currentToolUse.input
|
|
1555
|
+
});
|
|
1556
|
+
yield {
|
|
1557
|
+
type: 'tool_use_complete',
|
|
1558
|
+
toolUse: currentToolUse,
|
|
1559
|
+
data: event
|
|
1560
|
+
};
|
|
1561
|
+
currentToolUse = null;
|
|
1562
|
+
partialJson = '';
|
|
1563
|
+
}
|
|
1564
|
+
else if (currentBlockType === 'code_execution_tool_result' && textAccumulator) {
|
|
1565
|
+
// PTC: Code execution result — push accumulated output
|
|
1566
|
+
accumulatedContent.push({
|
|
1567
|
+
type: 'code_execution_tool_result',
|
|
1568
|
+
tool_use_id: '',
|
|
1569
|
+
content: textAccumulator
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
yield {
|
|
1573
|
+
type: 'content_block_stop',
|
|
1574
|
+
data: event
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
else if (event.type === 'message_start') {
|
|
1578
|
+
yield {
|
|
1579
|
+
type: 'message_start',
|
|
1580
|
+
data: event
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
else if (event.type === 'message_delta') {
|
|
1584
|
+
yield {
|
|
1585
|
+
type: 'message_delta',
|
|
1586
|
+
data: event
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
else if (event.type === 'message_stop') {
|
|
1590
|
+
yield {
|
|
1591
|
+
type: 'message_stop',
|
|
1592
|
+
data: event
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
catch (streamError) {
|
|
1598
|
+
// Handle stream errors gracefully (e.g., "request ended without sending any chunks")
|
|
1599
|
+
const errorMessage = streamError?.message || 'Unknown streaming error';
|
|
1600
|
+
console.error(`[APIClient XAI] Stream error: ${errorMessage}`);
|
|
1601
|
+
// Yield an error event that the orchestrator can handle
|
|
1602
|
+
yield {
|
|
1603
|
+
type: 'error',
|
|
1604
|
+
error: {
|
|
1605
|
+
type: 'stream_error',
|
|
1606
|
+
message: `XAI API stream failed: ${errorMessage}. This is usually a transient error - please retry.`
|
|
1607
|
+
}
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
// Stream complete - resolve promise so finalMessage can build
|
|
1611
|
+
streamCompleteResolve();
|
|
1612
|
+
}();
|
|
1613
|
+
// Phase 2.8: Build our own finalMessage with correctly accumulated tool inputs
|
|
1614
|
+
// The SDK's finalMessage() doesn't accumulate tool inputs for XAI, so we build our own
|
|
1615
|
+
const finalMessage = Promise.all([stream.finalMessage(), streamCompletePromise]).then(([sdkMessage]) => {
|
|
1616
|
+
// After stream completes, replace the SDK's broken content with our correctly accumulated content
|
|
1617
|
+
let finalContent = accumulatedContent.length > 0 ? accumulatedContent : sdkMessage.content;
|
|
1618
|
+
// XAI: Check for reasoning_content field in the message
|
|
1619
|
+
// XAI can include reasoning_content or encrypted_content at the message level
|
|
1620
|
+
if (sdkMessage.reasoning_content) {
|
|
1621
|
+
// Insert reasoning content as first block (upfront thinking)
|
|
1622
|
+
finalContent = [
|
|
1623
|
+
{ type: 'thinking', thinking: sdkMessage.reasoning_content },
|
|
1624
|
+
...finalContent
|
|
1625
|
+
];
|
|
1626
|
+
}
|
|
1627
|
+
// Extract <thinking> tags from text blocks (grok-4-1 embeds thinking in text)
|
|
1628
|
+
finalContent = this.extractThinkingFromTextBlocks(finalContent);
|
|
1629
|
+
return {
|
|
1630
|
+
...sdkMessage,
|
|
1631
|
+
content: finalContent
|
|
1632
|
+
};
|
|
1633
|
+
});
|
|
1634
|
+
return {
|
|
1635
|
+
chunks,
|
|
1636
|
+
finalMessage
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
throw new Error(`Streaming not yet implemented for ${provider} with Messages API pattern`);
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Stream request using Chat Completions API pattern
|
|
1643
|
+
*
|
|
1644
|
+
* Used by: OpenAI, XAI, DeepSeek, Groq, Anthropic (OpenAI-compatible)
|
|
1645
|
+
* Format: OpenAI Chat Completions with SSE chunks
|
|
1646
|
+
* SDK: Uses OpenAI SDK's ChatCompletionStream with .finalMessage() accumulation
|
|
1647
|
+
*/
|
|
1648
|
+
streamChatCompletionsAPI(request, modelConfig) {
|
|
1649
|
+
// Round 7: shared request construction (env check, baseURL strip,
|
|
1650
|
+
// OpenRouter headers, client cache, param rename, reasoning_effort,
|
|
1651
|
+
// tool attachment, DEBUG_PAYLOAD logging).
|
|
1652
|
+
const { client, chatRequest: openaiRequest } = this.buildChatCompletionsRequest(request, modelConfig, { stream: true });
|
|
1653
|
+
// Import ChatCompletionStream dynamically (avoid top-level import issues)
|
|
1654
|
+
// Note: We'll use the client's .create() with stream: true which returns Stream<ChatCompletionChunk>
|
|
1655
|
+
// However, we want the ChatCompletionStream helper for .finalMessage()
|
|
1656
|
+
// For now, use manual iteration and accumulation (SDK's Stream doesn't have finalMessage)
|
|
1657
|
+
// TODO: Investigate if there's a better way to use ChatCompletionStream.createChatCompletion
|
|
1658
|
+
let fullContent = '';
|
|
1659
|
+
let fullReasoningContent = '';
|
|
1660
|
+
let firstChunk = null;
|
|
1661
|
+
let finalMessageResolve;
|
|
1662
|
+
const finalMessagePromise = new Promise((resolve) => {
|
|
1663
|
+
finalMessageResolve = resolve;
|
|
1664
|
+
});
|
|
1665
|
+
// Phase 2.8: Track tool calls for streaming tool execution
|
|
1666
|
+
const accumulatedToolCalls = new Map();
|
|
1667
|
+
// Capture this context before entering generator
|
|
1668
|
+
const self = this;
|
|
1669
|
+
// Create async generator that both yields chunks AND accumulates final message
|
|
1670
|
+
const chunks = async function* () {
|
|
1671
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1672
|
+
console.log(`[DEBUG APIClient] OpenAI request:`, JSON.stringify(openaiRequest, null, 2));
|
|
1673
|
+
}
|
|
1674
|
+
const stream = await client.chat.completions.create(openaiRequest);
|
|
1675
|
+
let chunkCount = 0;
|
|
1676
|
+
for await (const chunk of stream) {
|
|
1677
|
+
if (!firstChunk)
|
|
1678
|
+
firstChunk = chunk;
|
|
1679
|
+
chunkCount++;
|
|
1680
|
+
const delta = chunk.choices[0]?.delta;
|
|
1681
|
+
// Debug: Log first few chunks to see what fields are present
|
|
1682
|
+
if ((process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') && chunkCount <= 3) {
|
|
1683
|
+
console.log(`[DEBUG APIClient] Chunk ${chunkCount} delta keys:`, delta ? Object.keys(delta) : 'null');
|
|
1684
|
+
if (delta?.reasoning_content) {
|
|
1685
|
+
console.log(`[DEBUG APIClient] Found reasoning_content in chunk ${chunkCount}`);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
// Extract reasoning content (for models with native interleaved thinking)
|
|
1689
|
+
// XAI: chunk.choices[0].delta.reasoning_content
|
|
1690
|
+
// DeepSeek: chunk.choices[0].delta.reasoning_content
|
|
1691
|
+
// GPT-5: chunk.choices[0].delta.reasoning_content (when reasoning_effort != 'none')
|
|
1692
|
+
if (delta?.reasoning_content) {
|
|
1693
|
+
fullReasoningContent += delta.reasoning_content;
|
|
1694
|
+
yield {
|
|
1695
|
+
type: 'content_block_delta',
|
|
1696
|
+
delta: delta.reasoning_content,
|
|
1697
|
+
data: { ...chunk, reasoning: true }
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
// Extract regular text content
|
|
1701
|
+
if (delta?.content) {
|
|
1702
|
+
fullContent += delta.content;
|
|
1703
|
+
yield {
|
|
1704
|
+
type: 'text_delta',
|
|
1705
|
+
delta: delta.content,
|
|
1706
|
+
data: chunk
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
// Phase 2.8: Handle streaming tool_calls
|
|
1710
|
+
if (delta?.tool_calls && Array.isArray(delta.tool_calls)) {
|
|
1711
|
+
for (const toolCallDelta of delta.tool_calls) {
|
|
1712
|
+
const index = toolCallDelta.index;
|
|
1713
|
+
// Initialize tool call accumulator if needed
|
|
1714
|
+
if (!accumulatedToolCalls.has(index)) {
|
|
1715
|
+
accumulatedToolCalls.set(index, { arguments: '' });
|
|
1716
|
+
}
|
|
1717
|
+
const accumulated = accumulatedToolCalls.get(index);
|
|
1718
|
+
// Accumulate tool call properties
|
|
1719
|
+
if (toolCallDelta.id) {
|
|
1720
|
+
accumulated.id = toolCallDelta.id;
|
|
1721
|
+
}
|
|
1722
|
+
if (toolCallDelta.function?.name) {
|
|
1723
|
+
accumulated.name = toolCallDelta.function.name;
|
|
1724
|
+
}
|
|
1725
|
+
if (toolCallDelta.function?.arguments) {
|
|
1726
|
+
accumulated.arguments += toolCallDelta.function.arguments;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
// Check if finish_reason indicates tool_calls completion (or length-truncated tool call)
|
|
1731
|
+
const finishReason = chunk.choices[0]?.finish_reason;
|
|
1732
|
+
if ((finishReason === 'tool_calls' || finishReason === 'length') && accumulatedToolCalls.size > 0) {
|
|
1733
|
+
// Emit tool_use_complete events for each accumulated tool call
|
|
1734
|
+
for (const [_, toolCall] of accumulatedToolCalls) {
|
|
1735
|
+
if (toolCall.id && toolCall.name) {
|
|
1736
|
+
try {
|
|
1737
|
+
let input;
|
|
1738
|
+
try {
|
|
1739
|
+
input = JSON.parse(toolCall.arguments);
|
|
1740
|
+
}
|
|
1741
|
+
catch {
|
|
1742
|
+
input = self.repairTruncatedJSON(toolCall.arguments);
|
|
1743
|
+
}
|
|
1744
|
+
const decodedInput = self.decodeHtmlEntitiesInObject(input);
|
|
1745
|
+
yield {
|
|
1746
|
+
type: 'tool_use_complete',
|
|
1747
|
+
toolUse: {
|
|
1748
|
+
id: toolCall.id,
|
|
1749
|
+
name: toolCall.name,
|
|
1750
|
+
input: decodedInput
|
|
1751
|
+
},
|
|
1752
|
+
data: chunk
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
catch (error) {
|
|
1756
|
+
console.error(`[APIClient] Failed to parse tool arguments (${toolCall.arguments.length} chars): ${toolCall.arguments.slice(0, 200)}...`);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
// Stream complete - log summary
|
|
1763
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1764
|
+
console.log(`[DEBUG APIClient] Stream complete - total chunks: ${chunkCount}, reasoning content length: ${fullReasoningContent.length}, content length: ${fullContent.length}`);
|
|
1765
|
+
}
|
|
1766
|
+
// Stream complete - resolve final message in OpenAI Chat Completions format
|
|
1767
|
+
// Include accumulated tool_calls in the final message
|
|
1768
|
+
const finalToolCalls = Array.from(accumulatedToolCalls.values())
|
|
1769
|
+
.filter(tc => tc.id && tc.name)
|
|
1770
|
+
.map(tc => ({
|
|
1771
|
+
id: tc.id,
|
|
1772
|
+
type: 'function',
|
|
1773
|
+
function: {
|
|
1774
|
+
name: tc.name,
|
|
1775
|
+
arguments: tc.arguments
|
|
1776
|
+
}
|
|
1777
|
+
}));
|
|
1778
|
+
finalMessageResolve({
|
|
1779
|
+
id: firstChunk?.id || 'chatcmpl-' + Date.now(),
|
|
1780
|
+
object: 'chat.completion',
|
|
1781
|
+
created: firstChunk?.created || Math.floor(Date.now() / 1000),
|
|
1782
|
+
model: firstChunk?.model || request.modelId,
|
|
1783
|
+
choices: [{
|
|
1784
|
+
index: 0,
|
|
1785
|
+
message: {
|
|
1786
|
+
role: 'assistant',
|
|
1787
|
+
content: fullContent || null,
|
|
1788
|
+
...(fullReasoningContent && { reasoning_content: fullReasoningContent }),
|
|
1789
|
+
...(finalToolCalls.length > 0 && { tool_calls: finalToolCalls })
|
|
1790
|
+
},
|
|
1791
|
+
finish_reason: finalToolCalls.length > 0 ? 'tool_calls' : 'stop'
|
|
1792
|
+
}],
|
|
1793
|
+
usage: {
|
|
1794
|
+
prompt_tokens: 0,
|
|
1795
|
+
completion_tokens: 0,
|
|
1796
|
+
total_tokens: 0
|
|
1797
|
+
}
|
|
1798
|
+
});
|
|
1799
|
+
}();
|
|
1800
|
+
return {
|
|
1801
|
+
chunks,
|
|
1802
|
+
finalMessage: finalMessagePromise
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Stream request using Responses API pattern
|
|
1807
|
+
*
|
|
1808
|
+
* Used by: OpenAI (gpt-5-codex)
|
|
1809
|
+
* Format: OpenAI Responses API with structured streaming events
|
|
1810
|
+
* SDK: Uses OpenAI SDK's client.responses.create() with stream: true
|
|
1811
|
+
*/
|
|
1812
|
+
streamResponsesAPI(request, modelConfig) {
|
|
1813
|
+
const apiKey = process.env[modelConfig.api.apiKeyEnvVar];
|
|
1814
|
+
if (!apiKey) {
|
|
1815
|
+
throw new Error(`Missing API key: ${modelConfig.api.apiKeyEnvVar}`);
|
|
1816
|
+
}
|
|
1817
|
+
// Use provider-formatted input items directly from ResponsesAPIAdapter.toProviderMessages()
|
|
1818
|
+
// The adapter already creates correctly typed items (message, function_call_output, etc.)
|
|
1819
|
+
// Re-wrapping them would strip tool results and function calls, breaking the tool loop.
|
|
1820
|
+
//
|
|
1821
|
+
// Extract <system-reminder> content from user messages into `instructions` parameter.
|
|
1822
|
+
const { instructions, cleanedItems: inputItems } = this.extractSystemRemindersForResponsesAPI(request.messages);
|
|
1823
|
+
// Accumulate content from stream
|
|
1824
|
+
let fullContent = '';
|
|
1825
|
+
let fullReasoningContent = '';
|
|
1826
|
+
let responseId = null;
|
|
1827
|
+
let finalResponse = null;
|
|
1828
|
+
const accumulatedToolCalls = [];
|
|
1829
|
+
let finalMessageResolve;
|
|
1830
|
+
const finalMessagePromise = new Promise((resolve) => {
|
|
1831
|
+
finalMessageResolve = resolve;
|
|
1832
|
+
});
|
|
1833
|
+
// Check if reasoning is supported and enabled
|
|
1834
|
+
const supportsReasoning = modelConfig.reasoning?.supported;
|
|
1835
|
+
const { disableThinking, reasoningEffort, ...apiParameters } = request.parameters;
|
|
1836
|
+
// Transform parameters for Responses API.
|
|
1837
|
+
// Responses API uses max_output_tokens instead of max_tokens.
|
|
1838
|
+
// R20c: also accept max_completion_tokens (GPT-5 chat/completions default key)
|
|
1839
|
+
// — needed on dynamic switch where the card never re-resolved the param name.
|
|
1840
|
+
let transformedParams = { ...apiParameters };
|
|
1841
|
+
for (const oldKey of ['max_tokens', 'max_completion_tokens', 'maxTokens']) {
|
|
1842
|
+
if (oldKey in transformedParams) {
|
|
1843
|
+
transformedParams.max_output_tokens = transformedParams[oldKey];
|
|
1844
|
+
delete transformedParams[oldKey];
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
const isXAI = modelConfig.provider?.toLowerCase() === 'xai';
|
|
1848
|
+
const chunks = async function* () {
|
|
1849
|
+
// Import OpenAI SDK dynamically — set baseURL from model config endpoint
|
|
1850
|
+
const OpenAI = (await import('openai')).default;
|
|
1851
|
+
const streamBaseURL = modelConfig.api.endpoint.replace(/\/(messages|responses)$/, '');
|
|
1852
|
+
const client = new OpenAI({ apiKey, baseURL: streamBaseURL });
|
|
1853
|
+
// Create streaming request
|
|
1854
|
+
// Use modelConfig.modelId if available (for alias models like gpt-5.1-reasoning -> gpt-5.1)
|
|
1855
|
+
const actualModelId = modelConfig.modelId || request.modelId;
|
|
1856
|
+
// Remove model from transformedParams to prevent overwriting
|
|
1857
|
+
const { model: _ignoredModel, ...paramsWithoutModel } = transformedParams;
|
|
1858
|
+
const responsesRequest = {
|
|
1859
|
+
model: actualModelId,
|
|
1860
|
+
input: inputItems,
|
|
1861
|
+
stream: true,
|
|
1862
|
+
...paramsWithoutModel
|
|
1863
|
+
};
|
|
1864
|
+
// Stateful chaining: use previous_response_id when available
|
|
1865
|
+
if (request.previousResponseId) {
|
|
1866
|
+
responsesRequest.previous_response_id = request.previousResponseId;
|
|
1867
|
+
}
|
|
1868
|
+
// XAI stores conversations server-side by default. Being explicit ensures preservation.
|
|
1869
|
+
// `prompt_cache_key` routes same-session requests to same server for cache hits.
|
|
1870
|
+
// NOTE: `include: ["reasoning.encrypted_content"]` triggered empty-output regression —
|
|
1871
|
+
// see sendResponsesAPI comment for details. Not sending it for now.
|
|
1872
|
+
if (isXAI) {
|
|
1873
|
+
responsesRequest.store = true;
|
|
1874
|
+
if (request.conversationId) {
|
|
1875
|
+
responsesRequest.prompt_cache_key = request.conversationId;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
// Set extracted system context as instructions (enables provider-side caching)
|
|
1879
|
+
// XAI does NOT support the `instructions` parameter — skip for XAI provider.
|
|
1880
|
+
if (instructions && !isXAI) {
|
|
1881
|
+
responsesRequest.instructions = instructions;
|
|
1882
|
+
}
|
|
1883
|
+
// Add reasoning if supported and not disabled
|
|
1884
|
+
// Responses API uses 'reasoning' object with 'effort' parameter
|
|
1885
|
+
// summary: 'auto' requests visible reasoning summaries for interleaved thinking
|
|
1886
|
+
if (supportsReasoning && !disableThinking && reasoningEffort && reasoningEffort !== 'none') {
|
|
1887
|
+
// R20: OpenAI Responses API rejects 'xhigh' (XAI-only effort level). Clamp at egress.
|
|
1888
|
+
const effectiveEffort = (!isXAI && reasoningEffort === 'xhigh') ? 'high' : reasoningEffort;
|
|
1889
|
+
responsesRequest.reasoning = { effort: effectiveEffort, summary: 'auto' };
|
|
1890
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1891
|
+
console.log(`[DEBUG APIClient] Responses API reasoning ENABLED (effort: ${effectiveEffort}, summary: auto)`);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
if (request.tools && request.tools.length > 0) {
|
|
1895
|
+
responsesRequest.tools = request.tools;
|
|
1896
|
+
}
|
|
1897
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1898
|
+
// Log summary without full tools/messages to avoid excessive output
|
|
1899
|
+
const { input, tools, ...requestSummary } = responsesRequest;
|
|
1900
|
+
console.log(`[DEBUG APIClient] Responses API request:`, JSON.stringify({
|
|
1901
|
+
...requestSummary,
|
|
1902
|
+
input: `[${input?.length || 0} items]`,
|
|
1903
|
+
tools: tools ? `[${tools.length} tools]` : undefined
|
|
1904
|
+
}, null, 2));
|
|
1905
|
+
}
|
|
1906
|
+
// Stream using OpenAI SDK
|
|
1907
|
+
// client.responses.create() with stream:true returns a Promise<Stream>
|
|
1908
|
+
// We await it to get the Stream object which is async iterable
|
|
1909
|
+
const stream = await client.responses.create(responsesRequest);
|
|
1910
|
+
let chunkCount = 0;
|
|
1911
|
+
try {
|
|
1912
|
+
for await (const chunk of stream) {
|
|
1913
|
+
chunkCount++;
|
|
1914
|
+
// Store response ID and full response
|
|
1915
|
+
if (chunk.response?.id && !responseId) {
|
|
1916
|
+
responseId = chunk.response.id;
|
|
1917
|
+
}
|
|
1918
|
+
if (chunk.response) {
|
|
1919
|
+
finalResponse = chunk.response;
|
|
1920
|
+
}
|
|
1921
|
+
// Debug: Log chunks to see event types (only with DEBUG=true)
|
|
1922
|
+
if (process.env.DEBUG === 'true' && chunkCount <= 10) {
|
|
1923
|
+
console.log(`[APIClient] Responses chunk ${chunkCount}:`, chunk.type, chunk.item?.type || '');
|
|
1924
|
+
}
|
|
1925
|
+
// Handle reasoning items (when modalities includes 'reasoning')
|
|
1926
|
+
// Responses API returns reasoning as output items with type: 'reasoning'
|
|
1927
|
+
if (chunk.type === 'response.output_item.added' && chunk.item?.type === 'reasoning') {
|
|
1928
|
+
// Reasoning item started
|
|
1929
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
1930
|
+
console.log(`[DEBUG APIClient] Reasoning item started`);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
else if (chunk.type === 'response.output_item.done' && chunk.item?.type === 'reasoning') {
|
|
1934
|
+
// Reasoning item completed - extract summary
|
|
1935
|
+
const reasoningSummary = chunk.item.summary || '';
|
|
1936
|
+
if (reasoningSummary) {
|
|
1937
|
+
fullReasoningContent += reasoningSummary;
|
|
1938
|
+
yield {
|
|
1939
|
+
type: 'content_block_delta',
|
|
1940
|
+
delta: reasoningSummary,
|
|
1941
|
+
data: { ...chunk, reasoning: true }
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
// Handle reasoning content deltas (streamed reasoning text)
|
|
1946
|
+
if (chunk.type === 'response.reasoning_summary_text.delta' && chunk.delta) {
|
|
1947
|
+
fullReasoningContent += chunk.delta;
|
|
1948
|
+
yield {
|
|
1949
|
+
type: 'content_block_delta',
|
|
1950
|
+
delta: chunk.delta,
|
|
1951
|
+
data: { ...chunk, reasoning: true }
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
// Handle text content deltas
|
|
1955
|
+
if (chunk.type === 'response.output_text.delta' && chunk.delta) {
|
|
1956
|
+
fullContent += chunk.delta;
|
|
1957
|
+
yield {
|
|
1958
|
+
type: 'text_delta',
|
|
1959
|
+
delta: chunk.delta,
|
|
1960
|
+
data: chunk
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
// Handle function calls (tool use)
|
|
1964
|
+
// Responses API sends function_call items with name and arguments
|
|
1965
|
+
if (chunk.type === 'response.output_item.done' && chunk.item?.type === 'function_call') {
|
|
1966
|
+
const item = chunk.item;
|
|
1967
|
+
if (item.name && item.call_id) {
|
|
1968
|
+
// Round 18a: dedupe by call_id. Without this guard, the
|
|
1969
|
+
// OpenAI Responses streaming protocol can re-emit the same
|
|
1970
|
+
// `response.output_item.done` event multiple times (observed
|
|
1971
|
+
// in gpt-5.1-reasoning: 5 identical tool_use blocks with the
|
|
1972
|
+
// SAME call_id reached the orchestrator). Each duplicate
|
|
1973
|
+
// pushed to accumulatedToolCalls AND yielded as
|
|
1974
|
+
// tool_use_complete, causing the same tool to "execute" N
|
|
1975
|
+
// times.
|
|
1976
|
+
const alreadyEmitted = accumulatedToolCalls.some((tc) => tc.id === item.call_id);
|
|
1977
|
+
if (alreadyEmitted) {
|
|
1978
|
+
continue;
|
|
1979
|
+
}
|
|
1980
|
+
// Accumulate for finalMessage
|
|
1981
|
+
accumulatedToolCalls.push({
|
|
1982
|
+
id: item.call_id,
|
|
1983
|
+
name: item.name,
|
|
1984
|
+
arguments: item.arguments || '{}'
|
|
1985
|
+
});
|
|
1986
|
+
try {
|
|
1987
|
+
const input = typeof item.arguments === 'string'
|
|
1988
|
+
? JSON.parse(item.arguments)
|
|
1989
|
+
: item.arguments;
|
|
1990
|
+
yield {
|
|
1991
|
+
type: 'tool_use_complete',
|
|
1992
|
+
toolUse: {
|
|
1993
|
+
id: item.call_id,
|
|
1994
|
+
name: item.name,
|
|
1995
|
+
input: input
|
|
1996
|
+
},
|
|
1997
|
+
data: chunk
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
catch (error) {
|
|
2001
|
+
console.error(`[APIClient] Failed to parse function call arguments:`, item.arguments);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
// Extract text deltas from output items (fallback for non-streaming format)
|
|
2006
|
+
if (chunk.type === 'response.output_item.added' && chunk.item) {
|
|
2007
|
+
const item = chunk.item;
|
|
2008
|
+
if (item.type === 'message' && item.content) {
|
|
2009
|
+
for (const block of item.content) {
|
|
2010
|
+
if (block.type === 'output_text' && block.text) {
|
|
2011
|
+
fullContent += block.text;
|
|
2012
|
+
yield {
|
|
2013
|
+
type: 'text_delta',
|
|
2014
|
+
delta: block.text,
|
|
2015
|
+
data: chunk
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
else if (chunk.type === 'response.output_item.done' && chunk.item) {
|
|
2022
|
+
const item = chunk.item;
|
|
2023
|
+
if (item.type === 'message' && item.content) {
|
|
2024
|
+
for (const block of item.content) {
|
|
2025
|
+
if (block.type === 'output_text' && block.text) {
|
|
2026
|
+
const text = block.text;
|
|
2027
|
+
if (text && !fullContent.includes(text)) {
|
|
2028
|
+
fullContent += text;
|
|
2029
|
+
yield {
|
|
2030
|
+
type: 'text_delta',
|
|
2031
|
+
delta: text,
|
|
2032
|
+
data: chunk
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
catch (error) {
|
|
2042
|
+
throw new Error(`Responses API streaming error: ${error.message}`);
|
|
2043
|
+
}
|
|
2044
|
+
// Log summary
|
|
2045
|
+
if (process.env.DEBUG === 'true' || process.env.DEBUG_THINKING === 'true') {
|
|
2046
|
+
console.log(`[DEBUG APIClient] Responses stream complete - chunks: ${chunkCount}, reasoning: ${fullReasoningContent.length}, content: ${fullContent.length}`);
|
|
2047
|
+
}
|
|
2048
|
+
// Resolve final message in Responses API format
|
|
2049
|
+
// Build output array with message and any function calls
|
|
2050
|
+
const outputItems = [];
|
|
2051
|
+
// Add function calls first (they come before the message in the response)
|
|
2052
|
+
for (const toolCall of accumulatedToolCalls) {
|
|
2053
|
+
outputItems.push({
|
|
2054
|
+
type: 'function_call',
|
|
2055
|
+
call_id: toolCall.id,
|
|
2056
|
+
name: toolCall.name,
|
|
2057
|
+
arguments: toolCall.arguments
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
// Add message if there's content
|
|
2061
|
+
if (fullContent) {
|
|
2062
|
+
outputItems.push({
|
|
2063
|
+
type: 'message',
|
|
2064
|
+
role: 'assistant',
|
|
2065
|
+
content: [
|
|
2066
|
+
{
|
|
2067
|
+
type: 'output_text',
|
|
2068
|
+
text: fullContent
|
|
2069
|
+
}
|
|
2070
|
+
]
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
finalMessageResolve({
|
|
2074
|
+
id: responseId || 'resp-' + Date.now(),
|
|
2075
|
+
object: 'response',
|
|
2076
|
+
created: finalResponse?.created_at || Math.floor(Date.now() / 1000),
|
|
2077
|
+
model: request.modelId,
|
|
2078
|
+
output: outputItems.length > 0 ? outputItems : [
|
|
2079
|
+
{
|
|
2080
|
+
type: 'message',
|
|
2081
|
+
role: 'assistant',
|
|
2082
|
+
content: [{ type: 'output_text', text: '' }]
|
|
2083
|
+
}
|
|
2084
|
+
],
|
|
2085
|
+
usage: finalResponse?.usage || {
|
|
2086
|
+
prompt_tokens: 0,
|
|
2087
|
+
completion_tokens: 0,
|
|
2088
|
+
total_tokens: 0
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
}();
|
|
2092
|
+
return {
|
|
2093
|
+
chunks,
|
|
2094
|
+
finalMessage: finalMessagePromise
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
/**
|
|
2098
|
+
* Stream request using Google GenerateContent API pattern
|
|
2099
|
+
*
|
|
2100
|
+
* Used by: Google Gemini (Vertex AI, AI Studio)
|
|
2101
|
+
* Format: Google GenerateContent API
|
|
2102
|
+
* SDK: Uses Google SDK's generateContentStream()
|
|
2103
|
+
*/
|
|
2104
|
+
streamGenerateContentAPI(request, modelConfig) {
|
|
2105
|
+
const apiKey = process.env[modelConfig.api.apiKeyEnvVar];
|
|
2106
|
+
if (!apiKey) {
|
|
2107
|
+
throw new Error(`Missing API key: ${modelConfig.api.apiKeyEnvVar}`);
|
|
2108
|
+
}
|
|
2109
|
+
// For models with tools, use direct HTTP streaming to ensure v1beta endpoint
|
|
2110
|
+
// The SDK sometimes uses v1 which doesn't support tools for newer models
|
|
2111
|
+
if (request.tools && request.tools.length > 0) {
|
|
2112
|
+
return this.streamGenerateContentHTTP(request, modelConfig, apiKey);
|
|
2113
|
+
}
|
|
2114
|
+
// For non-tool requests, use SDK streaming
|
|
2115
|
+
if (!this.googleClient) {
|
|
2116
|
+
this.googleClient = new GoogleGenerativeAI(apiKey);
|
|
2117
|
+
}
|
|
2118
|
+
const modelName = request.modelId.startsWith('models/') ? request.modelId : `models/${request.modelId}`;
|
|
2119
|
+
const model = this.googleClient.getGenerativeModel({ model: modelName });
|
|
2120
|
+
// Filter out internal flags and non-generation parameters
|
|
2121
|
+
// NOTE: disableThinking is NOT an API parameter - it's a UI-level flag
|
|
2122
|
+
const { model: _model, stream, tools: _tools, ...validGenerationConfig } = request.parameters;
|
|
2123
|
+
const geminiRequest = {
|
|
2124
|
+
contents: request.messages,
|
|
2125
|
+
generationConfig: validGenerationConfig
|
|
2126
|
+
};
|
|
2127
|
+
// Google SDK has generateContentStream
|
|
2128
|
+
const streamPromise = model.generateContentStream(geminiRequest);
|
|
2129
|
+
const chunks = async function* () {
|
|
2130
|
+
const { stream } = await streamPromise;
|
|
2131
|
+
for await (const chunk of stream) {
|
|
2132
|
+
const text = chunk.text();
|
|
2133
|
+
if (text) {
|
|
2134
|
+
yield {
|
|
2135
|
+
type: 'text_delta',
|
|
2136
|
+
delta: text,
|
|
2137
|
+
data: chunk
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
}();
|
|
2142
|
+
const finalMessage = (async () => {
|
|
2143
|
+
const { response } = await streamPromise;
|
|
2144
|
+
return response;
|
|
2145
|
+
})();
|
|
2146
|
+
return {
|
|
2147
|
+
chunks,
|
|
2148
|
+
finalMessage
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Stream GenerateContent request via direct HTTP with tools
|
|
2153
|
+
* Ensures v1beta endpoint is used for tool support
|
|
2154
|
+
*/
|
|
2155
|
+
streamGenerateContentHTTP(request, modelConfig, apiKey) {
|
|
2156
|
+
// Get model ID (no models/ prefix - endpoint already includes it)
|
|
2157
|
+
const modelId = request.modelId.startsWith('models/')
|
|
2158
|
+
? request.modelId.replace('models/', '')
|
|
2159
|
+
: request.modelId;
|
|
2160
|
+
// Build URL using configured endpoint (should be v1beta)
|
|
2161
|
+
const url = `${modelConfig.api.endpoint}/${modelId}:streamGenerateContent?alt=sse&key=${apiKey}`;
|
|
2162
|
+
// Build generation config (exclude stream, tools, and model - these are handled separately)
|
|
2163
|
+
// NOTE: disableThinking is NOT an API parameter - it's a UI-level flag
|
|
2164
|
+
// Extract the nested generationConfig object from request.parameters
|
|
2165
|
+
const { generationConfig } = request.parameters;
|
|
2166
|
+
// Build request body (REST API uses snake_case)
|
|
2167
|
+
const requestBody = {
|
|
2168
|
+
contents: request.messages,
|
|
2169
|
+
generation_config: generationConfig // Use the nested object directly
|
|
2170
|
+
};
|
|
2171
|
+
// Add tools if present - REST API uses snake_case "function_declarations"
|
|
2172
|
+
if (request.tools && request.tools.length > 0) {
|
|
2173
|
+
requestBody.tools = [{ function_declarations: request.tools }];
|
|
2174
|
+
// DEBUG: Log Gemini streaming tool request details
|
|
2175
|
+
if (process.env.DEBUG === 'true') {
|
|
2176
|
+
console.log(`[DEBUG APIClient] Gemini streaming request with ${request.tools.length} tools:`);
|
|
2177
|
+
console.log(`[DEBUG APIClient] Tool names: ${request.tools.map((t) => t.name).join(', ')}`);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
// Add system instruction if present
|
|
2181
|
+
if (request.systemMessage) {
|
|
2182
|
+
requestBody.system_instruction = {
|
|
2183
|
+
parts: [{ text: request.systemMessage }]
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
// DEBUG: Log the request body for diagnosing empty responses
|
|
2187
|
+
if (process.env.DEBUG === 'true') {
|
|
2188
|
+
console.log(`[DEBUG APIClient HTTP] Request URL: ${url.replace(/key=[^&]+/, 'key=***')}`);
|
|
2189
|
+
console.log(`[DEBUG APIClient HTTP] generation_config: ${JSON.stringify(requestBody.generation_config)}`);
|
|
2190
|
+
console.log(`[DEBUG APIClient HTTP] tools count: ${requestBody.tools?.[0]?.function_declarations?.length ?? 0}`);
|
|
2191
|
+
console.log(`[DEBUG APIClient HTTP] system_instruction present: ${!!requestBody.system_instruction}`);
|
|
2192
|
+
console.log(`[DEBUG APIClient HTTP] contents length: ${requestBody.contents?.length ?? 0}`);
|
|
2193
|
+
// Dump contents structure (roles and parts types, not full text)
|
|
2194
|
+
for (let i = 0; i < (requestBody.contents?.length ?? 0); i++) {
|
|
2195
|
+
const c = requestBody.contents[i];
|
|
2196
|
+
const partsInfo = (c.parts || []).map((p) => {
|
|
2197
|
+
if (p.text)
|
|
2198
|
+
return `text(${p.text.length}chars)`;
|
|
2199
|
+
if (p.functionCall)
|
|
2200
|
+
return `functionCall(${p.functionCall.name})`;
|
|
2201
|
+
if (p.functionResponse)
|
|
2202
|
+
return `functionResponse(${p.functionResponse.name})`;
|
|
2203
|
+
return Object.keys(p).join(',');
|
|
2204
|
+
});
|
|
2205
|
+
if (process.env.DEBUG === 'true')
|
|
2206
|
+
console.log(`[DEBUG APIClient HTTP] contents[${i}]: role=${c.role}, parts=[${partsInfo.join(', ')}]`);
|
|
2207
|
+
}
|
|
2208
|
+
// Write full request body to file for inspection
|
|
2209
|
+
import('fs').then(fs => fs.writeFileSync('/tmp/gemini-request-' + modelId + '.json', JSON.stringify(requestBody, null, 2)));
|
|
2210
|
+
}
|
|
2211
|
+
// Start the fetch request
|
|
2212
|
+
const fetchPromise = fetch(url, {
|
|
2213
|
+
method: 'POST',
|
|
2214
|
+
headers: {
|
|
2215
|
+
'Content-Type': 'application/json'
|
|
2216
|
+
},
|
|
2217
|
+
body: JSON.stringify(requestBody)
|
|
2218
|
+
});
|
|
2219
|
+
// Shared state for collecting all chunks
|
|
2220
|
+
const allChunks = [];
|
|
2221
|
+
let fullText = '';
|
|
2222
|
+
let lastCandidate = null;
|
|
2223
|
+
let streamingError = null;
|
|
2224
|
+
// Resolve function for completion signal
|
|
2225
|
+
let resolveCompletion = null;
|
|
2226
|
+
const completionPromise = new Promise((resolve) => {
|
|
2227
|
+
resolveCompletion = resolve;
|
|
2228
|
+
});
|
|
2229
|
+
// Create async generator for streaming chunks
|
|
2230
|
+
const chunks = async function* () {
|
|
2231
|
+
try {
|
|
2232
|
+
const response = await fetchPromise;
|
|
2233
|
+
if (!response.ok) {
|
|
2234
|
+
const errorText = await response.text();
|
|
2235
|
+
console.error(`[APIClient] Gemini API error ${response.status}:`, errorText);
|
|
2236
|
+
throw new Error(`Gemini API error: ${response.status} - ${errorText}`);
|
|
2237
|
+
}
|
|
2238
|
+
if (!response.body) {
|
|
2239
|
+
throw new Error('Response body is null');
|
|
2240
|
+
}
|
|
2241
|
+
const reader = response.body.getReader();
|
|
2242
|
+
const decoder = new TextDecoder();
|
|
2243
|
+
let buffer = '';
|
|
2244
|
+
while (true) {
|
|
2245
|
+
const { done, value } = await reader.read();
|
|
2246
|
+
if (done) {
|
|
2247
|
+
if (process.env.DEBUG === 'true') {
|
|
2248
|
+
console.log('[DEBUG APIClient] Stream reader done, exiting loop');
|
|
2249
|
+
}
|
|
2250
|
+
break;
|
|
2251
|
+
}
|
|
2252
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2253
|
+
// Process complete SSE messages
|
|
2254
|
+
const lines = buffer.split('\n');
|
|
2255
|
+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
2256
|
+
for (const line of lines) {
|
|
2257
|
+
if (line.startsWith('data: ')) {
|
|
2258
|
+
const jsonStr = line.slice(6); // Remove 'data: ' prefix
|
|
2259
|
+
if (jsonStr.trim() === '')
|
|
2260
|
+
continue;
|
|
2261
|
+
try {
|
|
2262
|
+
const chunk = JSON.parse(jsonStr);
|
|
2263
|
+
// DEBUG: Log chunk structure
|
|
2264
|
+
if (process.env.DEBUG === 'true') {
|
|
2265
|
+
console.log('[DEBUG APIClient] Gemini chunk:', JSON.stringify(chunk).substring(0, 200));
|
|
2266
|
+
if (chunk.candidates?.[0]?.finishReason) {
|
|
2267
|
+
console.log(`[DEBUG APIClient] finishReason: ${chunk.candidates[0].finishReason}`);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
// Store last candidate for final response
|
|
2271
|
+
if (chunk.candidates?.[0]) {
|
|
2272
|
+
lastCandidate = chunk.candidates[0];
|
|
2273
|
+
}
|
|
2274
|
+
// Extract parts from candidates
|
|
2275
|
+
const parts = chunk.candidates?.[0]?.content?.parts;
|
|
2276
|
+
if (parts && Array.isArray(parts)) {
|
|
2277
|
+
for (const part of parts) {
|
|
2278
|
+
// Handle thinking parts (reasoning blocks)
|
|
2279
|
+
// Following gemini-cli pattern: yield ALL chunks including thinking
|
|
2280
|
+
// UI will filter based on disableThinking flag
|
|
2281
|
+
if (part.thought && part.text) {
|
|
2282
|
+
const thinkingChunk = {
|
|
2283
|
+
type: 'thinking_delta',
|
|
2284
|
+
delta: part.text,
|
|
2285
|
+
data: chunk
|
|
2286
|
+
};
|
|
2287
|
+
allChunks.push(thinkingChunk);
|
|
2288
|
+
// Don't add to fullText - thinking is filtered from final response
|
|
2289
|
+
yield thinkingChunk;
|
|
2290
|
+
}
|
|
2291
|
+
// Handle regular text (non-thinking)
|
|
2292
|
+
else if (part.text && !part.thought) {
|
|
2293
|
+
const chunkData = {
|
|
2294
|
+
type: 'text_delta',
|
|
2295
|
+
delta: part.text,
|
|
2296
|
+
data: chunk
|
|
2297
|
+
};
|
|
2298
|
+
allChunks.push(chunkData);
|
|
2299
|
+
fullText += part.text;
|
|
2300
|
+
yield chunkData;
|
|
2301
|
+
}
|
|
2302
|
+
// Handle function calls (tool uses)
|
|
2303
|
+
if (part.functionCall) {
|
|
2304
|
+
// R19c: preserve thoughtSignature for Gemini 2.5+/3+
|
|
2305
|
+
// multi-turn function calling. SDK streaming path (~line
|
|
2306
|
+
// 2944) already does this; the raw-fetch streaming path
|
|
2307
|
+
// (used by gemini-3-pro-preview via apiPattern
|
|
2308
|
+
// 'generateContent') was dropping it, causing Gemini 3 to
|
|
2309
|
+
// 400 with "Function call is missing a thought_signature"
|
|
2310
|
+
// on every continuation and looping until MAX_TOOL_ITER.
|
|
2311
|
+
const thoughtSig = part.thoughtSignature;
|
|
2312
|
+
const toolUse = {
|
|
2313
|
+
// R19a: uuidv4 instead of Date.now()+Math.random — same
|
|
2314
|
+
// collision-free reasoning as adapters.
|
|
2315
|
+
id: `call_${uuidv4()}`,
|
|
2316
|
+
name: part.functionCall.name,
|
|
2317
|
+
input: part.functionCall.args
|
|
2318
|
+
};
|
|
2319
|
+
if (thoughtSig) {
|
|
2320
|
+
toolUse.metadata = { thoughtSignature: thoughtSig };
|
|
2321
|
+
}
|
|
2322
|
+
const toolChunk = {
|
|
2323
|
+
type: 'tool_use_complete',
|
|
2324
|
+
toolUse,
|
|
2325
|
+
data: chunk
|
|
2326
|
+
};
|
|
2327
|
+
allChunks.push(toolChunk);
|
|
2328
|
+
yield toolChunk;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
catch (parseError) {
|
|
2334
|
+
console.error('Error parsing SSE chunk:', parseError);
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
catch (error) {
|
|
2341
|
+
streamingError = error;
|
|
2342
|
+
throw error;
|
|
2343
|
+
}
|
|
2344
|
+
finally {
|
|
2345
|
+
// Signal that streaming is complete (success or error)
|
|
2346
|
+
if (resolveCompletion) {
|
|
2347
|
+
resolveCompletion();
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}();
|
|
2351
|
+
// Final message promise that waits for streaming to complete
|
|
2352
|
+
const finalMessage = (async () => {
|
|
2353
|
+
// Wait for streaming to complete (without consuming the chunks)
|
|
2354
|
+
await completionPromise;
|
|
2355
|
+
// If there was an error, throw it
|
|
2356
|
+
if (streamingError) {
|
|
2357
|
+
throw streamingError;
|
|
2358
|
+
}
|
|
2359
|
+
// Return final response structure matching Google SDK format
|
|
2360
|
+
return {
|
|
2361
|
+
text: () => fullText,
|
|
2362
|
+
candidates: lastCandidate ? [lastCandidate] : [],
|
|
2363
|
+
usageMetadata: lastCandidate?.usageMetadata
|
|
2364
|
+
};
|
|
2365
|
+
})();
|
|
2366
|
+
return {
|
|
2367
|
+
chunks,
|
|
2368
|
+
finalMessage
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
/**
|
|
2372
|
+
* Validate and enhance tools for XAI Messages API
|
|
2373
|
+
* XAI requires ALL tools and parameters to have descriptions
|
|
2374
|
+
*/
|
|
2375
|
+
validateXAITools(tools) {
|
|
2376
|
+
return tools.map((tool, index) => {
|
|
2377
|
+
const enhancedTool = { ...tool };
|
|
2378
|
+
// Ensure tool has a name
|
|
2379
|
+
if (!enhancedTool.name || typeof enhancedTool.name !== 'string') {
|
|
2380
|
+
enhancedTool.name = `tool_${index}`;
|
|
2381
|
+
}
|
|
2382
|
+
// REQUIRED: Ensure tool has a description
|
|
2383
|
+
if (!enhancedTool.description || typeof enhancedTool.description !== 'string') {
|
|
2384
|
+
enhancedTool.description = enhancedTool.name ?
|
|
2385
|
+
`Execute ${enhancedTool.name} tool` :
|
|
2386
|
+
`Tool ${index}`;
|
|
2387
|
+
if (process.env.DEBUG === 'true')
|
|
2388
|
+
console.log(`[XAI Validation] Added missing description for tool: ${enhancedTool.name}`);
|
|
2389
|
+
}
|
|
2390
|
+
// REQUIRED: Ensure input_schema exists
|
|
2391
|
+
if (!enhancedTool.input_schema) {
|
|
2392
|
+
console.error(`[XAI Validation] Tool ${enhancedTool.name} missing input_schema! Creating default.`);
|
|
2393
|
+
enhancedTool.input_schema = {
|
|
2394
|
+
type: 'object',
|
|
2395
|
+
properties: {},
|
|
2396
|
+
required: []
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
// Ensure input_schema has proper structure
|
|
2400
|
+
if (!enhancedTool.input_schema.type) {
|
|
2401
|
+
enhancedTool.input_schema.type = 'object';
|
|
2402
|
+
}
|
|
2403
|
+
if (!enhancedTool.input_schema.properties) {
|
|
2404
|
+
enhancedTool.input_schema.properties = {};
|
|
2405
|
+
}
|
|
2406
|
+
// REQUIRED: Ensure all parameters have descriptions
|
|
2407
|
+
if (enhancedTool.input_schema.properties) {
|
|
2408
|
+
const properties = enhancedTool.input_schema.properties;
|
|
2409
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
2410
|
+
if (!prop.description || typeof prop.description !== 'string') {
|
|
2411
|
+
prop.description = `Parameter ${key} for ${enhancedTool.name || 'tool'}`;
|
|
2412
|
+
if (process.env.DEBUG === 'true')
|
|
2413
|
+
console.log(`[XAI Validation] Added missing description for parameter: ${key}`);
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
// Ensure required field exists (XAI expects it)
|
|
2417
|
+
if (!enhancedTool.input_schema.required) {
|
|
2418
|
+
enhancedTool.input_schema.required = [];
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
return enhancedTool;
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
/**
|
|
2425
|
+
* Send non-streaming request using Google GenAI SDK
|
|
2426
|
+
* Mirrors streamGoogleSDK() but uses generateContent() instead of generateContentStream()
|
|
2427
|
+
*/
|
|
2428
|
+
async sendGoogleSDK(request, _modelConfig) {
|
|
2429
|
+
// Initialize Google GenAI client (same as streaming path)
|
|
2430
|
+
if (!this.googleGenAIClient) {
|
|
2431
|
+
const apiKey = process.env['GEMINI_API_KEY'];
|
|
2432
|
+
if (!apiKey) {
|
|
2433
|
+
throw new Error('GEMINI_API_KEY environment variable is required for Google SDK models');
|
|
2434
|
+
}
|
|
2435
|
+
const oldGoogleKey = process.env['GOOGLE_API_KEY'];
|
|
2436
|
+
delete process.env['GOOGLE_API_KEY'];
|
|
2437
|
+
try {
|
|
2438
|
+
this.googleGenAIClient = new GoogleGenAI({ apiKey });
|
|
2439
|
+
}
|
|
2440
|
+
finally {
|
|
2441
|
+
if (oldGoogleKey) {
|
|
2442
|
+
process.env['GOOGLE_API_KEY'] = oldGoogleKey;
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
// Strip -sdk suffix for actual API model ID
|
|
2447
|
+
let modelId = request.modelId;
|
|
2448
|
+
if (modelId.endsWith('-sdk')) {
|
|
2449
|
+
modelId = modelId.replace(/-sdk$/, '');
|
|
2450
|
+
}
|
|
2451
|
+
const { generationConfig, disableThinking } = request.parameters;
|
|
2452
|
+
const config = {};
|
|
2453
|
+
if (generationConfig) {
|
|
2454
|
+
Object.assign(config, generationConfig);
|
|
2455
|
+
}
|
|
2456
|
+
// CRITICAL: thinkingConfig and tools CANNOT coexist
|
|
2457
|
+
const hasTools = request.tools && request.tools.length > 0;
|
|
2458
|
+
if (_modelConfig.reasoning?.supported && _modelConfig.reasoning?.pattern === 'interleaved' && !disableThinking && !hasTools) {
|
|
2459
|
+
config.thinkingConfig = {
|
|
2460
|
+
includeThoughts: true,
|
|
2461
|
+
thinkingBudget: 10000
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2464
|
+
// Add tools to config if present
|
|
2465
|
+
if (hasTools && request.tools) {
|
|
2466
|
+
config.tools = request.tools.map((tool) => ({
|
|
2467
|
+
functionDeclarations: [{
|
|
2468
|
+
name: tool.name,
|
|
2469
|
+
description: tool.description,
|
|
2470
|
+
parameters: tool.input_schema
|
|
2471
|
+
}]
|
|
2472
|
+
}));
|
|
2473
|
+
}
|
|
2474
|
+
const sdkRequest = {
|
|
2475
|
+
model: modelId,
|
|
2476
|
+
contents: request.messages,
|
|
2477
|
+
config
|
|
2478
|
+
};
|
|
2479
|
+
if (request.systemMessage) {
|
|
2480
|
+
sdkRequest.systemInstruction = request.systemMessage;
|
|
2481
|
+
}
|
|
2482
|
+
if (process.env.DEBUG === 'true') {
|
|
2483
|
+
console.log(`[DEBUG APIClient SDK] Non-streaming request for model: ${modelId}`);
|
|
2484
|
+
}
|
|
2485
|
+
const response = await this.googleGenAIClient.models.generateContent(sdkRequest);
|
|
2486
|
+
return {
|
|
2487
|
+
data: response,
|
|
2488
|
+
status: 200,
|
|
2489
|
+
headers: {}
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Stream using Google GenAI SDK (EXPERIMENTAL)
|
|
2494
|
+
* Uses @google/genai SDK directly instead of REST API
|
|
2495
|
+
*
|
|
2496
|
+
* Key differences from streamGenerateContentAPI:
|
|
2497
|
+
* - Uses new @google/genai package (not @google/generative-ai)
|
|
2498
|
+
* - Automatically reads GEMINI_API_KEY from environment
|
|
2499
|
+
* - Simpler API with better stability
|
|
2500
|
+
* - Matches Google's official documentation examples
|
|
2501
|
+
*/
|
|
2502
|
+
streamGoogleSDK(request, _modelConfig) {
|
|
2503
|
+
// Initialize Google GenAI client (explicitly use GEMINI_API_KEY per docs)
|
|
2504
|
+
if (!this.googleGenAIClient) {
|
|
2505
|
+
const apiKey = process.env['GEMINI_API_KEY'];
|
|
2506
|
+
if (!apiKey) {
|
|
2507
|
+
throw new Error('GEMINI_API_KEY environment variable is required for Google SDK models');
|
|
2508
|
+
}
|
|
2509
|
+
// Temporarily unset GOOGLE_API_KEY to prevent SDK from preferring it
|
|
2510
|
+
// The SDK checks both GEMINI_API_KEY and GOOGLE_API_KEY, but prefers GOOGLE_API_KEY
|
|
2511
|
+
const oldGoogleKey = process.env['GOOGLE_API_KEY'];
|
|
2512
|
+
delete process.env['GOOGLE_API_KEY'];
|
|
2513
|
+
try {
|
|
2514
|
+
this.googleGenAIClient = new GoogleGenAI({ apiKey });
|
|
2515
|
+
}
|
|
2516
|
+
finally {
|
|
2517
|
+
// Restore GOOGLE_API_KEY for other providers
|
|
2518
|
+
if (oldGoogleKey) {
|
|
2519
|
+
process.env['GOOGLE_API_KEY'] = oldGoogleKey;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
// Use actual Google model ID (strip our custom suffix if present)
|
|
2524
|
+
// e.g., "gemini-2.5-flash-sdk" -> "gemini-2.5-flash"
|
|
2525
|
+
let modelId = request.modelId;
|
|
2526
|
+
if (modelId.endsWith('-sdk')) {
|
|
2527
|
+
modelId = modelId.replace(/-sdk$/, '');
|
|
2528
|
+
}
|
|
2529
|
+
// Extract parameters
|
|
2530
|
+
const { generationConfig, disableThinking } = request.parameters;
|
|
2531
|
+
// Build config object (tools go inside config per Google SDK docs)
|
|
2532
|
+
const config = {};
|
|
2533
|
+
// Add generation parameters to config
|
|
2534
|
+
if (generationConfig) {
|
|
2535
|
+
Object.assign(config, generationConfig);
|
|
2536
|
+
}
|
|
2537
|
+
// Enable extended thinking for models with reasoning support (Gemini 2.5+)
|
|
2538
|
+
// Following Claude/Grok pattern: Skip if disableThinking flag is set (used for continuation requests)
|
|
2539
|
+
// CRITICAL: thinkingConfig and tools CANNOT coexist — Google API returns empty responses when both present
|
|
2540
|
+
const hasTools = request.tools && request.tools.length > 0;
|
|
2541
|
+
if (_modelConfig.reasoning?.supported && _modelConfig.reasoning?.pattern === 'interleaved' && !disableThinking && !hasTools) {
|
|
2542
|
+
config.thinkingConfig = {
|
|
2543
|
+
includeThoughts: true, // Enable thought summaries in response
|
|
2544
|
+
// Gemini 2.5 Flash supports thinkingBudget (0-24576)
|
|
2545
|
+
// -1 = dynamic (model decides), or set explicit budget like Claude's 10000
|
|
2546
|
+
// Set to 10000 tokens to encourage verbose reasoning like Claude
|
|
2547
|
+
thinkingBudget: 10000
|
|
2548
|
+
};
|
|
2549
|
+
if (process.env.DEBUG === 'true') {
|
|
2550
|
+
console.log(`[DEBUG APIClient SDK] Extended thinking enabled for ${modelId} with budget: 10000`);
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
else if (hasTools && process.env.DEBUG === 'true') {
|
|
2554
|
+
console.log(`[DEBUG APIClient SDK] Skipping thinkingConfig for ${modelId} — incompatible with tools`);
|
|
2555
|
+
}
|
|
2556
|
+
// Add tools to config if present (per SDK docs, tools are inside config)
|
|
2557
|
+
if (request.tools && request.tools.length > 0) {
|
|
2558
|
+
config.tools = request.tools.map((tool) => ({
|
|
2559
|
+
functionDeclarations: [{
|
|
2560
|
+
name: tool.name,
|
|
2561
|
+
description: tool.description,
|
|
2562
|
+
parameters: tool.input_schema
|
|
2563
|
+
}]
|
|
2564
|
+
}));
|
|
2565
|
+
if (process.env.DEBUG === 'true') {
|
|
2566
|
+
console.log(`[DEBUG APIClient SDK] Gemini SDK request with ${request.tools.length} tools`);
|
|
2567
|
+
console.log(`[DEBUG APIClient SDK] Tool names: ${request.tools.map((t) => t.name).join(', ')}`);
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
// Build request for new SDK
|
|
2571
|
+
const sdkRequest = {
|
|
2572
|
+
model: modelId,
|
|
2573
|
+
contents: request.messages,
|
|
2574
|
+
config // Config contains both generation params and tools
|
|
2575
|
+
};
|
|
2576
|
+
// Add system instruction if present (top level, not in config)
|
|
2577
|
+
if (request.systemMessage) {
|
|
2578
|
+
sdkRequest.systemInstruction = request.systemMessage;
|
|
2579
|
+
}
|
|
2580
|
+
if (process.env.DEBUG === 'true') {
|
|
2581
|
+
console.log(`[DEBUG APIClient SDK] Using model: ${modelId}`);
|
|
2582
|
+
console.log(`[DEBUG APIClient SDK] Request keys: ${Object.keys(sdkRequest).join(', ')}`);
|
|
2583
|
+
if (config.tools) {
|
|
2584
|
+
console.log(`[DEBUG APIClient SDK] Config has ${config.tools.length} tool(s)`);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
// Shared state between chunks generator and finalMessage
|
|
2588
|
+
let lastChunk = null;
|
|
2589
|
+
let fullText = '';
|
|
2590
|
+
let chunkCount = 0; // Track number of chunks for debugging
|
|
2591
|
+
let resolveComplete;
|
|
2592
|
+
const streamCompletePromise = new Promise((resolve) => {
|
|
2593
|
+
resolveComplete = resolve;
|
|
2594
|
+
});
|
|
2595
|
+
const chunks = async function* (client) {
|
|
2596
|
+
try {
|
|
2597
|
+
// Stream using new SDK (SINGLE API call)
|
|
2598
|
+
const stream = await client.models.generateContentStream(sdkRequest);
|
|
2599
|
+
if (process.env.DEBUG === 'true') {
|
|
2600
|
+
console.log(`[DEBUG APIClient SDK] Stream started for model: ${modelId}`);
|
|
2601
|
+
}
|
|
2602
|
+
for await (const chunk of stream) {
|
|
2603
|
+
// Store for final message
|
|
2604
|
+
lastChunk = chunk;
|
|
2605
|
+
chunkCount++;
|
|
2606
|
+
if (process.env.DEBUG === 'true') {
|
|
2607
|
+
console.log(`[DEBUG APIClient SDK] Chunk ${chunkCount}: candidates=${chunk.candidates?.length || 0}, finishReason=${chunk.candidates?.[0]?.finishReason || 'none'}`);
|
|
2608
|
+
}
|
|
2609
|
+
// Process candidates array (similar to REST API approach)
|
|
2610
|
+
const candidates = chunk.candidates;
|
|
2611
|
+
if (candidates && Array.isArray(candidates)) {
|
|
2612
|
+
for (const candidate of candidates) {
|
|
2613
|
+
const parts = candidate.content?.parts;
|
|
2614
|
+
if (parts && Array.isArray(parts)) {
|
|
2615
|
+
for (const part of parts) {
|
|
2616
|
+
// Handle thinking parts (extended reasoning) - similar to Claude's extended thinking
|
|
2617
|
+
// Following gemini-cli pattern: yield ALL chunks including thinking
|
|
2618
|
+
if (part.thought && part.text) {
|
|
2619
|
+
if (process.env.DEBUG === 'true') {
|
|
2620
|
+
console.log(`[DEBUG APIClient SDK] Thinking chunk length: ${part.text.length}`);
|
|
2621
|
+
}
|
|
2622
|
+
yield {
|
|
2623
|
+
type: 'thinking_delta',
|
|
2624
|
+
delta: part.text,
|
|
2625
|
+
data: chunk
|
|
2626
|
+
};
|
|
2627
|
+
continue; // Skip adding to fullText (thinking is separate)
|
|
2628
|
+
}
|
|
2629
|
+
// Handle regular text
|
|
2630
|
+
if (part.text && !part.functionCall) {
|
|
2631
|
+
const text = part.text;
|
|
2632
|
+
fullText += text;
|
|
2633
|
+
if (process.env.DEBUG === 'true') {
|
|
2634
|
+
console.log(`[DEBUG APIClient SDK] Chunk text length: ${text.length}`);
|
|
2635
|
+
}
|
|
2636
|
+
// BREAK DOWN LARGE TEXT CHUNKS FOR SMOOTH STREAMING
|
|
2637
|
+
// SDK yields larger chunks (full lines/sentences) unlike HTTP SSE which is more granular
|
|
2638
|
+
// Split into smaller pieces to provide smoother streaming experience
|
|
2639
|
+
if (text.length > 50) {
|
|
2640
|
+
// For large chunks, yield in smaller pieces
|
|
2641
|
+
const words = text.split(/(\s+)/); // Split on whitespace but keep delimiters
|
|
2642
|
+
let currentChunk = '';
|
|
2643
|
+
for (const word of words) {
|
|
2644
|
+
currentChunk += word;
|
|
2645
|
+
// Yield when we have a reasonable chunk size or hit natural breaks
|
|
2646
|
+
if (currentChunk.length >= 20 || word.includes('\n') || word.includes('.') || word.includes('?') || word.includes('!')) {
|
|
2647
|
+
yield {
|
|
2648
|
+
type: 'text_delta',
|
|
2649
|
+
delta: currentChunk,
|
|
2650
|
+
data: chunk
|
|
2651
|
+
};
|
|
2652
|
+
currentChunk = '';
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
// Yield any remaining text
|
|
2656
|
+
if (currentChunk.length > 0) {
|
|
2657
|
+
yield {
|
|
2658
|
+
type: 'text_delta',
|
|
2659
|
+
delta: currentChunk,
|
|
2660
|
+
data: chunk
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
else {
|
|
2665
|
+
// For smaller chunks, yield as-is
|
|
2666
|
+
yield {
|
|
2667
|
+
type: 'text_delta',
|
|
2668
|
+
delta: text,
|
|
2669
|
+
data: chunk
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
// Handle function calls (tool uses)
|
|
2674
|
+
if (part.functionCall) {
|
|
2675
|
+
if (process.env.DEBUG === 'true') {
|
|
2676
|
+
console.log(`[DEBUG APIClient SDK] Function call: ${part.functionCall.name}`);
|
|
2677
|
+
if (part.thoughtSignature) {
|
|
2678
|
+
console.log(`[DEBUG APIClient SDK] Has thought signature: ${part.thoughtSignature.substring(0, 50)}...`);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
// Build toolUse with thought signature preserved in metadata
|
|
2682
|
+
const rand = Math.random();
|
|
2683
|
+
const toolUse = {
|
|
2684
|
+
id: `call_${Date.now()}_${rand.toString(36).substring(2, 11)}`,
|
|
2685
|
+
name: part.functionCall.name || 'unknown', // Ensure name is always string
|
|
2686
|
+
input: part.functionCall.args || {} // Ensure input is always object
|
|
2687
|
+
};
|
|
2688
|
+
// CRITICAL: Preserve thought signature for multi-turn function calling (Gemini 2.5+/3+)
|
|
2689
|
+
const thoughtSig = part.thoughtSignature;
|
|
2690
|
+
if (thoughtSig) {
|
|
2691
|
+
toolUse.metadata = { thoughtSignature: thoughtSig };
|
|
2692
|
+
}
|
|
2693
|
+
yield {
|
|
2694
|
+
type: 'tool_use_complete',
|
|
2695
|
+
toolUse,
|
|
2696
|
+
data: chunk
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
if (process.env.DEBUG === 'true') {
|
|
2705
|
+
console.log(`[DEBUG APIClient SDK] Stream complete after ${chunkCount} chunks`);
|
|
2706
|
+
console.log(`[DEBUG APIClient SDK] Full text length: ${fullText.length}`);
|
|
2707
|
+
console.log(`[DEBUG APIClient SDK] lastChunk keys: ${Object.keys(lastChunk || {}).join(', ')}`);
|
|
2708
|
+
if (lastChunk?.candidates) {
|
|
2709
|
+
console.log(`[DEBUG APIClient SDK] candidates length: ${lastChunk.candidates.length}`);
|
|
2710
|
+
if (lastChunk.candidates.length > 0) {
|
|
2711
|
+
if (process.env.DEBUG === 'true')
|
|
2712
|
+
console.log(`[DEBUG APIClient SDK] candidates[0] keys: ${Object.keys(lastChunk.candidates[0] || {}).join(', ')}`);
|
|
2713
|
+
console.log(`[DEBUG APIClient SDK] finishReason: ${lastChunk.candidates[0]?.finishReason || 'none'}`);
|
|
2714
|
+
if (lastChunk.candidates[0]?.content) {
|
|
2715
|
+
console.log(`[DEBUG APIClient SDK] content.parts length: ${lastChunk.candidates[0].content.parts?.length || 0}`);
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
else {
|
|
2720
|
+
console.warn(`[DEBUG APIClient SDK] No candidates in final chunk!`);
|
|
2721
|
+
}
|
|
2722
|
+
if (lastChunk?.usageMetadata) {
|
|
2723
|
+
if (process.env.DEBUG === 'true')
|
|
2724
|
+
console.log(`[DEBUG APIClient SDK] Token usage: ${JSON.stringify(lastChunk.usageMetadata)}`);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
// Check for problematic finish reasons
|
|
2728
|
+
const finishReason = lastChunk?.candidates?.[0]?.finishReason;
|
|
2729
|
+
if (finishReason && finishReason !== 'STOP') {
|
|
2730
|
+
console.warn(`[APIClient SDK] Unexpected finishReason: ${finishReason}`);
|
|
2731
|
+
if (finishReason === 'SAFETY' || finishReason === 'RECITATION') {
|
|
2732
|
+
console.warn(`[APIClient SDK] Response was blocked. Check safety ratings.`);
|
|
2733
|
+
}
|
|
2734
|
+
else if (finishReason === 'MAX_TOKENS') {
|
|
2735
|
+
console.warn(`[APIClient SDK] Response truncated due to token limit.`);
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
// Yield message_stop chunk to signal completion (triggers markdown flush in CLI)
|
|
2739
|
+
yield {
|
|
2740
|
+
type: 'message_stop',
|
|
2741
|
+
data: lastChunk
|
|
2742
|
+
};
|
|
2743
|
+
// Signal that stream is complete
|
|
2744
|
+
resolveComplete();
|
|
2745
|
+
}
|
|
2746
|
+
catch (error) {
|
|
2747
|
+
console.error('[APIClient SDK] Streaming error:', error);
|
|
2748
|
+
// Signal completion even on error
|
|
2749
|
+
resolveComplete();
|
|
2750
|
+
throw error;
|
|
2751
|
+
}
|
|
2752
|
+
}(this.googleGenAIClient);
|
|
2753
|
+
const finalMessage = (async () => {
|
|
2754
|
+
// Wait for chunks generator to complete
|
|
2755
|
+
await streamCompletePromise;
|
|
2756
|
+
// Return final response structure matching Google SDK format
|
|
2757
|
+
// This matches the structure expected by the orchestrator
|
|
2758
|
+
return {
|
|
2759
|
+
text: () => fullText,
|
|
2760
|
+
candidates: lastChunk?.candidates || [],
|
|
2761
|
+
usageMetadata: lastChunk?.usageMetadata
|
|
2762
|
+
};
|
|
2763
|
+
})();
|
|
2764
|
+
return {
|
|
2765
|
+
chunks,
|
|
2766
|
+
finalMessage
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
//# sourceMappingURL=APIClient.js.map
|