@gguf/coder 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +16 -0
- package/.env.example +63 -0
- package/.gitattributes +1 -0
- package/.semgrepignore +19 -0
- package/coder-dummy-file.ts +52 -0
- package/coder.config.example.json +59 -0
- package/coder.config.json +13 -0
- package/color_picker.html +36 -0
- package/package.json +2 -14
- package/scripts/extract-changelog.js +73 -0
- package/scripts/fetch-models.js +143 -0
- package/scripts/test.sh +40 -0
- package/scripts/update-homebrew-formula.sh +125 -0
- package/scripts/update-nix-version.sh +157 -0
- package/source/ai-sdk-client/AISDKClient.spec.ts +117 -0
- package/source/ai-sdk-client/AISDKClient.ts +155 -0
- package/source/ai-sdk-client/chat/chat-handler.spec.ts +121 -0
- package/source/ai-sdk-client/chat/chat-handler.ts +276 -0
- package/source/ai-sdk-client/chat/streaming-handler.spec.ts +173 -0
- package/source/ai-sdk-client/chat/streaming-handler.ts +110 -0
- package/source/ai-sdk-client/chat/tool-processor.spec.ts +92 -0
- package/source/ai-sdk-client/chat/tool-processor.ts +70 -0
- package/source/ai-sdk-client/converters/message-converter.spec.ts +220 -0
- package/source/ai-sdk-client/converters/message-converter.ts +113 -0
- package/source/ai-sdk-client/converters/tool-converter.spec.ts +90 -0
- package/source/ai-sdk-client/converters/tool-converter.ts +46 -0
- package/source/ai-sdk-client/error-handling/error-extractor.spec.ts +55 -0
- package/source/ai-sdk-client/error-handling/error-extractor.ts +15 -0
- package/source/ai-sdk-client/error-handling/error-parser.spec.ts +169 -0
- package/source/ai-sdk-client/error-handling/error-parser.ts +161 -0
- package/source/ai-sdk-client/index.ts +7 -0
- package/source/ai-sdk-client/providers/provider-factory.spec.ts +71 -0
- package/source/ai-sdk-client/providers/provider-factory.ts +41 -0
- package/source/ai-sdk-client/types.ts +9 -0
- package/source/ai-sdk-client-empty-message.spec.ts +141 -0
- package/source/ai-sdk-client-error-handling.spec.ts +186 -0
- package/source/ai-sdk-client-maxretries.spec.ts +114 -0
- package/source/ai-sdk-client-preparestep.spec.ts +279 -0
- package/source/app/App.spec.tsx +32 -0
- package/source/app/App.tsx +480 -0
- package/source/app/components/AppContainer.spec.tsx +96 -0
- package/source/app/components/AppContainer.tsx +56 -0
- package/source/app/components/ChatInterface.spec.tsx +163 -0
- package/source/app/components/ChatInterface.tsx +144 -0
- package/source/app/components/ModalSelectors.spec.tsx +141 -0
- package/source/app/components/ModalSelectors.tsx +135 -0
- package/source/app/helpers.spec.ts +97 -0
- package/source/app/helpers.ts +63 -0
- package/source/app/index.ts +4 -0
- package/source/app/types.ts +39 -0
- package/source/app/utils/appUtils.ts +294 -0
- package/source/app/utils/conversationState.ts +310 -0
- package/source/app.spec.tsx +244 -0
- package/source/cli.spec.ts +73 -0
- package/source/cli.tsx +51 -0
- package/source/client-factory.spec.ts +48 -0
- package/source/client-factory.ts +178 -0
- package/source/command-parser.spec.ts +127 -0
- package/source/command-parser.ts +36 -0
- package/source/commands/checkpoint.spec.tsx +277 -0
- package/source/commands/checkpoint.tsx +366 -0
- package/source/commands/clear.tsx +22 -0
- package/source/commands/custom-commands.tsx +121 -0
- package/source/commands/exit.ts +21 -0
- package/source/commands/export.spec.tsx +131 -0
- package/source/commands/export.tsx +79 -0
- package/source/commands/help.tsx +120 -0
- package/source/commands/index.ts +17 -0
- package/source/commands/init.tsx +339 -0
- package/source/commands/lsp-command.spec.tsx +281 -0
- package/source/commands/lsp.tsx +120 -0
- package/source/commands/mcp-command.spec.tsx +313 -0
- package/source/commands/mcp.tsx +162 -0
- package/source/commands/model-database.spec.tsx +758 -0
- package/source/commands/model-database.tsx +418 -0
- package/source/commands/model.ts +12 -0
- package/source/commands/provider.ts +12 -0
- package/source/commands/setup-config.tsx +16 -0
- package/source/commands/simple-commands.spec.tsx +175 -0
- package/source/commands/status.ts +12 -0
- package/source/commands/theme.ts +12 -0
- package/source/commands/update.spec.tsx +261 -0
- package/source/commands/update.tsx +201 -0
- package/source/commands/usage.spec.tsx +495 -0
- package/source/commands/usage.tsx +100 -0
- package/source/commands.spec.ts +436 -0
- package/source/commands.ts +83 -0
- package/source/components/assistant-message.spec.tsx +796 -0
- package/source/components/assistant-message.tsx +34 -0
- package/source/components/bash-execution-indicator.tsx +21 -0
- package/source/components/cancelling-indicator.tsx +16 -0
- package/source/components/chat-queue.spec.tsx +83 -0
- package/source/components/chat-queue.tsx +36 -0
- package/source/components/checkpoint-display.spec.tsx +219 -0
- package/source/components/checkpoint-display.tsx +126 -0
- package/source/components/checkpoint-selector.spec.tsx +173 -0
- package/source/components/checkpoint-selector.tsx +173 -0
- package/source/components/development-mode-indicator.spec.tsx +268 -0
- package/source/components/development-mode-indicator.tsx +38 -0
- package/source/components/message-box.spec.tsx +427 -0
- package/source/components/message-box.tsx +87 -0
- package/source/components/model-selector.tsx +132 -0
- package/source/components/provider-selector.tsx +75 -0
- package/source/components/random-spinner.tsx +19 -0
- package/source/components/security-disclaimer.tsx +73 -0
- package/source/components/status-connection-display.spec.tsx +133 -0
- package/source/components/status.tsx +267 -0
- package/source/components/theme-selector.tsx +126 -0
- package/source/components/tool-confirmation.tsx +190 -0
- package/source/components/tool-execution-indicator.tsx +33 -0
- package/source/components/tool-message.tsx +85 -0
- package/source/components/ui/titled-box.spec.tsx +207 -0
- package/source/components/ui/titled-box.tsx +57 -0
- package/source/components/usage/progress-bar.spec.tsx +398 -0
- package/source/components/usage/progress-bar.tsx +30 -0
- package/source/components/usage/usage-display.spec.tsx +780 -0
- package/source/components/usage/usage-display.tsx +291 -0
- package/source/components/user-input.spec.tsx +327 -0
- package/source/components/user-input.tsx +533 -0
- package/source/components/user-message.spec.tsx +230 -0
- package/source/components/user-message.tsx +84 -0
- package/source/components/welcome-message.tsx +76 -0
- package/source/config/env-substitution.ts +65 -0
- package/source/config/index.spec.ts +171 -0
- package/source/config/index.ts +154 -0
- package/source/config/paths.spec.ts +241 -0
- package/source/config/paths.ts +55 -0
- package/source/config/preferences.ts +51 -0
- package/source/config/themes.ts +315 -0
- package/source/constants.ts +130 -0
- package/source/context/mode-context.spec.ts +79 -0
- package/source/context/mode-context.ts +24 -0
- package/source/custom-commands/executor.spec.ts +142 -0
- package/source/custom-commands/executor.ts +64 -0
- package/source/custom-commands/loader.spec.ts +314 -0
- package/source/custom-commands/loader.ts +153 -0
- package/source/custom-commands/parser.ts +196 -0
- package/source/hooks/chat-handler/conversation/conversation-loop.spec.ts +39 -0
- package/source/hooks/chat-handler/conversation/conversation-loop.tsx +511 -0
- package/source/hooks/chat-handler/conversation/tool-executor.spec.ts +50 -0
- package/source/hooks/chat-handler/conversation/tool-executor.tsx +109 -0
- package/source/hooks/chat-handler/index.ts +12 -0
- package/source/hooks/chat-handler/state/streaming-state.spec.ts +26 -0
- package/source/hooks/chat-handler/state/streaming-state.ts +19 -0
- package/source/hooks/chat-handler/types.ts +38 -0
- package/source/hooks/chat-handler/useChatHandler.spec.tsx +321 -0
- package/source/hooks/chat-handler/useChatHandler.tsx +194 -0
- package/source/hooks/chat-handler/utils/context-checker.spec.ts +60 -0
- package/source/hooks/chat-handler/utils/context-checker.tsx +73 -0
- package/source/hooks/chat-handler/utils/message-helpers.spec.ts +42 -0
- package/source/hooks/chat-handler/utils/message-helpers.tsx +36 -0
- package/source/hooks/chat-handler/utils/tool-filters.spec.ts +109 -0
- package/source/hooks/chat-handler/utils/tool-filters.ts +64 -0
- package/source/hooks/useAppHandlers.tsx +291 -0
- package/source/hooks/useAppInitialization.tsx +422 -0
- package/source/hooks/useAppState.tsx +311 -0
- package/source/hooks/useDirectoryTrust.tsx +98 -0
- package/source/hooks/useInputState.ts +414 -0
- package/source/hooks/useModeHandlers.tsx +302 -0
- package/source/hooks/useNonInteractiveMode.ts +140 -0
- package/source/hooks/useTerminalWidth.tsx +81 -0
- package/source/hooks/useTheme.ts +18 -0
- package/source/hooks/useToolHandler.tsx +349 -0
- package/source/hooks/useUIState.ts +61 -0
- package/source/init/agents-template-generator.ts +421 -0
- package/source/init/existing-rules-extractor.ts +319 -0
- package/source/init/file-scanner.spec.ts +227 -0
- package/source/init/file-scanner.ts +238 -0
- package/source/init/framework-detector.ts +382 -0
- package/source/init/language-detector.ts +269 -0
- package/source/init/project-analyzer.spec.ts +231 -0
- package/source/init/project-analyzer.ts +458 -0
- package/source/lsp/index.ts +31 -0
- package/source/lsp/lsp-client.spec.ts +508 -0
- package/source/lsp/lsp-client.ts +487 -0
- package/source/lsp/lsp-manager.spec.ts +477 -0
- package/source/lsp/lsp-manager.ts +419 -0
- package/source/lsp/protocol.spec.ts +502 -0
- package/source/lsp/protocol.ts +360 -0
- package/source/lsp/server-discovery.spec.ts +654 -0
- package/source/lsp/server-discovery.ts +515 -0
- package/source/markdown-parser/html-entities.spec.ts +88 -0
- package/source/markdown-parser/html-entities.ts +45 -0
- package/source/markdown-parser/index.spec.ts +281 -0
- package/source/markdown-parser/index.ts +126 -0
- package/source/markdown-parser/table-parser.spec.ts +133 -0
- package/source/markdown-parser/table-parser.ts +114 -0
- package/source/markdown-parser/utils.spec.ts +70 -0
- package/source/markdown-parser/utils.ts +13 -0
- package/source/mcp/mcp-client.spec.ts +81 -0
- package/source/mcp/mcp-client.ts +625 -0
- package/source/mcp/transport-factory.spec.ts +406 -0
- package/source/mcp/transport-factory.ts +312 -0
- package/source/message-handler.ts +67 -0
- package/source/model-database/database-engine.spec.ts +494 -0
- package/source/model-database/database-engine.ts +50 -0
- package/source/model-database/model-database.spec.ts +363 -0
- package/source/model-database/model-database.ts +91 -0
- package/source/model-database/model-engine.spec.ts +447 -0
- package/source/model-database/model-engine.ts +65 -0
- package/source/model-database/model-fetcher.spec.ts +583 -0
- package/source/model-database/model-fetcher.ts +330 -0
- package/source/models/index.ts +1 -0
- package/source/models/models-cache.spec.ts +214 -0
- package/source/models/models-cache.ts +78 -0
- package/source/models/models-dev-client.spec.ts +379 -0
- package/source/models/models-dev-client.ts +329 -0
- package/source/models/models-types.ts +68 -0
- package/source/prompt-history.ts +155 -0
- package/source/security/command-injection.spec.ts +240 -0
- package/source/services/checkpoint-manager.spec.ts +523 -0
- package/source/services/checkpoint-manager.ts +466 -0
- package/source/services/file-snapshot.spec.ts +569 -0
- package/source/services/file-snapshot.ts +220 -0
- package/source/test-utils/render-with-theme.tsx +48 -0
- package/source/tokenization/index.ts +1 -0
- package/source/tokenization/tokenizer-factory.spec.ts +170 -0
- package/source/tokenization/tokenizer-factory.ts +125 -0
- package/source/tokenization/tokenizers/anthropic-tokenizer.spec.ts +200 -0
- package/source/tokenization/tokenizers/anthropic-tokenizer.ts +43 -0
- package/source/tokenization/tokenizers/fallback-tokenizer.spec.ts +236 -0
- package/source/tokenization/tokenizers/fallback-tokenizer.ts +26 -0
- package/source/tokenization/tokenizers/llama-tokenizer.spec.ts +224 -0
- package/source/tokenization/tokenizers/llama-tokenizer.ts +41 -0
- package/source/tokenization/tokenizers/openai-tokenizer.spec.ts +184 -0
- package/source/tokenization/tokenizers/openai-tokenizer.ts +57 -0
- package/source/tool-calling/index.ts +5 -0
- package/source/tool-calling/json-parser.spec.ts +639 -0
- package/source/tool-calling/json-parser.ts +247 -0
- package/source/tool-calling/tool-parser.spec.ts +395 -0
- package/source/tool-calling/tool-parser.ts +120 -0
- package/source/tool-calling/xml-parser.spec.ts +662 -0
- package/source/tool-calling/xml-parser.ts +289 -0
- package/source/tools/execute-bash.spec.tsx +353 -0
- package/source/tools/execute-bash.tsx +219 -0
- package/source/tools/execute-function.spec.ts +130 -0
- package/source/tools/fetch-url.spec.tsx +342 -0
- package/source/tools/fetch-url.tsx +172 -0
- package/source/tools/find-files.spec.tsx +924 -0
- package/source/tools/find-files.tsx +293 -0
- package/source/tools/index.ts +102 -0
- package/source/tools/lsp-get-diagnostics.tsx +192 -0
- package/source/tools/needs-approval.spec.ts +282 -0
- package/source/tools/read-file.spec.tsx +801 -0
- package/source/tools/read-file.tsx +387 -0
- package/source/tools/search-file-contents.spec.tsx +1273 -0
- package/source/tools/search-file-contents.tsx +293 -0
- package/source/tools/string-replace.spec.tsx +730 -0
- package/source/tools/string-replace.tsx +548 -0
- package/source/tools/tool-manager.ts +210 -0
- package/source/tools/tool-registry.spec.ts +415 -0
- package/source/tools/tool-registry.ts +228 -0
- package/source/tools/web-search.tsx +223 -0
- package/source/tools/write-file.spec.tsx +559 -0
- package/source/tools/write-file.tsx +228 -0
- package/source/types/app.ts +37 -0
- package/source/types/checkpoint.ts +48 -0
- package/source/types/commands.ts +46 -0
- package/source/types/components.ts +27 -0
- package/source/types/config.ts +103 -0
- package/source/types/core-connection-status.spec.ts +67 -0
- package/source/types/core.ts +181 -0
- package/source/types/hooks.ts +50 -0
- package/source/types/index.ts +12 -0
- package/source/types/markdown-parser.ts +11 -0
- package/source/types/mcp.ts +52 -0
- package/source/types/system.ts +16 -0
- package/source/types/tokenization.ts +41 -0
- package/source/types/ui.ts +40 -0
- package/source/types/usage.ts +58 -0
- package/source/types/utils.ts +16 -0
- package/source/usage/calculator.spec.ts +385 -0
- package/source/usage/calculator.ts +104 -0
- package/source/usage/storage.spec.ts +703 -0
- package/source/usage/storage.ts +238 -0
- package/source/usage/tracker.spec.ts +456 -0
- package/source/usage/tracker.ts +102 -0
- package/source/utils/atomic-deletion.spec.ts +194 -0
- package/source/utils/atomic-deletion.ts +127 -0
- package/source/utils/bounded-map.spec.ts +300 -0
- package/source/utils/bounded-map.ts +193 -0
- package/source/utils/checkpoint-utils.spec.ts +222 -0
- package/source/utils/checkpoint-utils.ts +92 -0
- package/source/utils/error-formatter.spec.ts +169 -0
- package/source/utils/error-formatter.ts +194 -0
- package/source/utils/file-autocomplete.spec.ts +173 -0
- package/source/utils/file-autocomplete.ts +196 -0
- package/source/utils/file-cache.spec.ts +309 -0
- package/source/utils/file-cache.ts +195 -0
- package/source/utils/file-content-loader.spec.ts +180 -0
- package/source/utils/file-content-loader.ts +179 -0
- package/source/utils/file-mention-handler.spec.ts +261 -0
- package/source/utils/file-mention-handler.ts +84 -0
- package/source/utils/file-mention-parser.spec.ts +182 -0
- package/source/utils/file-mention-parser.ts +170 -0
- package/source/utils/fuzzy-matching.spec.ts +149 -0
- package/source/utils/fuzzy-matching.ts +146 -0
- package/source/utils/indentation-normalizer.spec.ts +216 -0
- package/source/utils/indentation-normalizer.ts +76 -0
- package/source/utils/installation-detector.spec.ts +178 -0
- package/source/utils/installation-detector.ts +153 -0
- package/source/utils/logging/config.spec.ts +311 -0
- package/source/utils/logging/config.ts +210 -0
- package/source/utils/logging/console-facade.spec.ts +184 -0
- package/source/utils/logging/console-facade.ts +384 -0
- package/source/utils/logging/correlation.spec.ts +679 -0
- package/source/utils/logging/correlation.ts +474 -0
- package/source/utils/logging/formatters.spec.ts +464 -0
- package/source/utils/logging/formatters.ts +207 -0
- package/source/utils/logging/health-monitor/alerts/alert-manager.spec.ts +93 -0
- package/source/utils/logging/health-monitor/alerts/alert-manager.ts +79 -0
- package/source/utils/logging/health-monitor/checks/configuration-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/configuration-check.ts +43 -0
- package/source/utils/logging/health-monitor/checks/logging-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/logging-check.ts +58 -0
- package/source/utils/logging/health-monitor/checks/memory-check.spec.ts +100 -0
- package/source/utils/logging/health-monitor/checks/memory-check.ts +78 -0
- package/source/utils/logging/health-monitor/checks/performance-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/performance-check.ts +56 -0
- package/source/utils/logging/health-monitor/checks/request-check.spec.ts +56 -0
- package/source/utils/logging/health-monitor/checks/request-check.ts +76 -0
- package/source/utils/logging/health-monitor/core/health-check-runner.spec.ts +70 -0
- package/source/utils/logging/health-monitor/core/health-check-runner.ts +138 -0
- package/source/utils/logging/health-monitor/core/health-monitor.spec.ts +58 -0
- package/source/utils/logging/health-monitor/core/health-monitor.ts +344 -0
- package/source/utils/logging/health-monitor/core/scoring.spec.ts +65 -0
- package/source/utils/logging/health-monitor/core/scoring.ts +91 -0
- package/source/utils/logging/health-monitor/index.ts +15 -0
- package/source/utils/logging/health-monitor/instances.ts +48 -0
- package/source/utils/logging/health-monitor/middleware/http-middleware.spec.ts +141 -0
- package/source/utils/logging/health-monitor/middleware/http-middleware.ts +75 -0
- package/source/utils/logging/health-monitor/types.ts +126 -0
- package/source/utils/logging/index.spec.ts +284 -0
- package/source/utils/logging/index.ts +236 -0
- package/source/utils/logging/integration.spec.ts +441 -0
- package/source/utils/logging/log-method-factory.spec.ts +573 -0
- package/source/utils/logging/log-method-factory.ts +233 -0
- package/source/utils/logging/log-query/aggregation/aggregator.spec.ts +277 -0
- package/source/utils/logging/log-query/aggregation/aggregator.ts +159 -0
- package/source/utils/logging/log-query/aggregation/facet-generator.spec.ts +159 -0
- package/source/utils/logging/log-query/aggregation/facet-generator.ts +47 -0
- package/source/utils/logging/log-query/index.ts +23 -0
- package/source/utils/logging/log-query/query/filter-predicates.spec.ts +247 -0
- package/source/utils/logging/log-query/query/filter-predicates.ts +154 -0
- package/source/utils/logging/log-query/query/query-builder.spec.ts +182 -0
- package/source/utils/logging/log-query/query/query-builder.ts +151 -0
- package/source/utils/logging/log-query/query/query-engine.spec.ts +214 -0
- package/source/utils/logging/log-query/query/query-engine.ts +45 -0
- package/source/utils/logging/log-query/storage/circular-buffer.spec.ts +143 -0
- package/source/utils/logging/log-query/storage/circular-buffer.ts +75 -0
- package/source/utils/logging/log-query/storage/index-manager.spec.ts +150 -0
- package/source/utils/logging/log-query/storage/index-manager.ts +71 -0
- package/source/utils/logging/log-query/storage/log-storage.spec.ts +257 -0
- package/source/utils/logging/log-query/storage/log-storage.ts +80 -0
- package/source/utils/logging/log-query/types.ts +163 -0
- package/source/utils/logging/log-query/utils/helpers.spec.ts +263 -0
- package/source/utils/logging/log-query/utils/helpers.ts +72 -0
- package/source/utils/logging/log-query/utils/sorting.spec.ts +182 -0
- package/source/utils/logging/log-query/utils/sorting.ts +61 -0
- package/source/utils/logging/logger-provider.spec.ts +262 -0
- package/source/utils/logging/logger-provider.ts +362 -0
- package/source/utils/logging/performance.spec.ts +209 -0
- package/source/utils/logging/performance.ts +757 -0
- package/source/utils/logging/pino-logger.spec.ts +425 -0
- package/source/utils/logging/pino-logger.ts +514 -0
- package/source/utils/logging/redaction.spec.ts +490 -0
- package/source/utils/logging/redaction.ts +267 -0
- package/source/utils/logging/request-tracker.spec.ts +1198 -0
- package/source/utils/logging/request-tracker.ts +803 -0
- package/source/utils/logging/transports.spec.ts +505 -0
- package/source/utils/logging/transports.ts +305 -0
- package/source/utils/logging/types.ts +216 -0
- package/source/utils/message-builder.spec.ts +179 -0
- package/source/utils/message-builder.ts +101 -0
- package/source/utils/message-queue.tsx +486 -0
- package/source/utils/paste-detection.spec.ts +69 -0
- package/source/utils/paste-detection.ts +124 -0
- package/source/utils/paste-roundtrip.spec.ts +442 -0
- package/source/utils/paste-utils.spec.ts +128 -0
- package/source/utils/paste-utils.ts +52 -0
- package/source/utils/programming-language-helper.spec.ts +74 -0
- package/source/utils/programming-language-helper.ts +32 -0
- package/source/utils/prompt-assembly.spec.ts +221 -0
- package/source/utils/prompt-processor.ts +173 -0
- package/source/utils/tool-args-parser.spec.ts +136 -0
- package/source/utils/tool-args-parser.ts +54 -0
- package/source/utils/tool-cancellation.spec.ts +230 -0
- package/source/utils/tool-cancellation.ts +28 -0
- package/source/utils/tool-result-display.spec.tsx +469 -0
- package/source/utils/tool-result-display.tsx +90 -0
- package/source/utils/update-checker.spec.ts +383 -0
- package/source/utils/update-checker.ts +183 -0
- package/source/wizard/config-wizard.spec.tsx +103 -0
- package/source/wizard/config-wizard.tsx +382 -0
- package/source/wizard/steps/location-step.spec.tsx +186 -0
- package/source/wizard/steps/location-step.tsx +147 -0
- package/source/wizard/steps/mcp-step.spec.tsx +607 -0
- package/source/wizard/steps/mcp-step.tsx +632 -0
- package/source/wizard/steps/provider-step.spec.tsx +342 -0
- package/source/wizard/steps/provider-step.tsx +957 -0
- package/source/wizard/steps/summary-step.spec.tsx +749 -0
- package/source/wizard/steps/summary-step.tsx +228 -0
- package/source/wizard/templates/mcp-templates.spec.ts +613 -0
- package/source/wizard/templates/mcp-templates.ts +570 -0
- package/source/wizard/templates/provider-templates.spec.ts +152 -0
- package/source/wizard/templates/provider-templates.ts +485 -0
- package/source/wizard/utils/fetch-cloud-models.spec.ts +428 -0
- package/source/wizard/utils/fetch-cloud-models.ts +223 -0
- package/source/wizard/utils/fetch-local-models.spec.ts +297 -0
- package/source/wizard/utils/fetch-local-models.ts +192 -0
- package/source/wizard/validation-array.spec.ts +264 -0
- package/source/wizard/validation.spec.ts +373 -0
- package/source/wizard/validation.ts +232 -0
- package/source/app/prompts/main-prompt.md +0 -122
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import {MAX_TOOL_STEPS} from '@/constants';
|
|
2
|
+
import type {
|
|
3
|
+
AIProviderConfig,
|
|
4
|
+
AISDKCoreTool,
|
|
5
|
+
LLMChatResponse,
|
|
6
|
+
Message,
|
|
7
|
+
StreamCallbacks,
|
|
8
|
+
ToolCall,
|
|
9
|
+
} from '@/types/index';
|
|
10
|
+
import {
|
|
11
|
+
endMetrics,
|
|
12
|
+
formatMemoryUsage,
|
|
13
|
+
generateCorrelationId,
|
|
14
|
+
getCorrelationId,
|
|
15
|
+
getLogger,
|
|
16
|
+
startMetrics,
|
|
17
|
+
withNewCorrelationContext,
|
|
18
|
+
} from '@/utils/logging';
|
|
19
|
+
import type {LanguageModel} from 'ai';
|
|
20
|
+
import {generateText, stepCountIs} from 'ai';
|
|
21
|
+
import {convertToModelMessages} from '../converters/message-converter.js';
|
|
22
|
+
import {
|
|
23
|
+
convertAISDKToolCalls,
|
|
24
|
+
getToolResultOutput,
|
|
25
|
+
} from '../converters/tool-converter.js';
|
|
26
|
+
import {extractRootError} from '../error-handling/error-extractor.js';
|
|
27
|
+
import {parseAPIError} from '../error-handling/error-parser.js';
|
|
28
|
+
import {
|
|
29
|
+
createOnStepFinishHandler,
|
|
30
|
+
createPrepareStepHandler,
|
|
31
|
+
} from './streaming-handler.js';
|
|
32
|
+
import {processXMLToolCalls} from './tool-processor.js';
|
|
33
|
+
|
|
34
|
+
export interface ChatHandlerParams {
|
|
35
|
+
model: LanguageModel;
|
|
36
|
+
currentModel: string;
|
|
37
|
+
providerConfig: AIProviderConfig;
|
|
38
|
+
messages: Message[];
|
|
39
|
+
tools: Record<string, AISDKCoreTool>;
|
|
40
|
+
callbacks: StreamCallbacks;
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
maxRetries: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Main chat handler - orchestrates the entire chat flow
|
|
47
|
+
*/
|
|
48
|
+
export async function handleChat(
|
|
49
|
+
params: ChatHandlerParams,
|
|
50
|
+
): Promise<LLMChatResponse> {
|
|
51
|
+
const {
|
|
52
|
+
model,
|
|
53
|
+
currentModel,
|
|
54
|
+
providerConfig,
|
|
55
|
+
messages,
|
|
56
|
+
tools,
|
|
57
|
+
callbacks,
|
|
58
|
+
signal,
|
|
59
|
+
maxRetries,
|
|
60
|
+
} = params;
|
|
61
|
+
const logger = getLogger();
|
|
62
|
+
|
|
63
|
+
// Check if already aborted before starting
|
|
64
|
+
if (signal?.aborted) {
|
|
65
|
+
logger.debug('Chat request already aborted');
|
|
66
|
+
throw new Error('Operation was cancelled');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Start performance tracking
|
|
70
|
+
const metrics = startMetrics();
|
|
71
|
+
const correlationId = getCorrelationId() || generateCorrelationId();
|
|
72
|
+
|
|
73
|
+
logger.info('Chat request starting', {
|
|
74
|
+
model: currentModel,
|
|
75
|
+
messageCount: messages.length,
|
|
76
|
+
toolCount: Object.keys(tools).length,
|
|
77
|
+
correlationId,
|
|
78
|
+
provider: providerConfig.name,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return await withNewCorrelationContext(async _context => {
|
|
82
|
+
try {
|
|
83
|
+
// Tools are already in AI SDK format - use directly
|
|
84
|
+
const aiTools = Object.keys(tools).length > 0 ? tools : undefined;
|
|
85
|
+
|
|
86
|
+
// Convert messages to AI SDK v5 ModelMessage format
|
|
87
|
+
const modelMessages = convertToModelMessages(messages);
|
|
88
|
+
|
|
89
|
+
logger.debug('AI SDK request prepared', {
|
|
90
|
+
messageCount: modelMessages.length,
|
|
91
|
+
hasTools: !!aiTools,
|
|
92
|
+
toolCount: aiTools ? Object.keys(aiTools).length : 0,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Tools with needsApproval: false auto-execute in the loop
|
|
96
|
+
// Tools with needsApproval: true cause interruptions for manual approval
|
|
97
|
+
// stopWhen controls when the tool loop stops (max MAX_TOOL_STEPS steps)
|
|
98
|
+
const result = await generateText({
|
|
99
|
+
model,
|
|
100
|
+
messages: modelMessages,
|
|
101
|
+
tools: aiTools,
|
|
102
|
+
abortSignal: signal,
|
|
103
|
+
maxRetries,
|
|
104
|
+
stopWhen: stepCountIs(MAX_TOOL_STEPS), // Allow up to MAX_TOOL_STEPS tool execution steps
|
|
105
|
+
// Can be used to add custom logging, metrics, or step tracking
|
|
106
|
+
onStepFinish: createOnStepFinishHandler(callbacks),
|
|
107
|
+
prepareStep: createPrepareStepHandler(),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Get the full text from the result
|
|
111
|
+
const fullText = result.text;
|
|
112
|
+
|
|
113
|
+
logger.debug('AI SDK response received', {
|
|
114
|
+
responseLength: fullText.length,
|
|
115
|
+
hasToolCalls: !!(result.toolCalls && result.toolCalls.length > 0),
|
|
116
|
+
toolCallCount: result.toolCalls?.length || 0,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Send the complete text to the callback
|
|
120
|
+
if (fullText) {
|
|
121
|
+
callbacks.onToken?.(fullText);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Get tool calls from result
|
|
125
|
+
const toolCallsResult = result.toolCalls;
|
|
126
|
+
|
|
127
|
+
// Extract auto-executed assistant messages and tool results from steps
|
|
128
|
+
// These need to be added to the messages array so usage tracking can count them
|
|
129
|
+
const autoExecutedMessages: Array<Message> = [];
|
|
130
|
+
const steps = result.steps;
|
|
131
|
+
for (const step of steps) {
|
|
132
|
+
if (
|
|
133
|
+
step.toolCalls &&
|
|
134
|
+
step.toolResults &&
|
|
135
|
+
step.toolCalls.length === step.toolResults.length
|
|
136
|
+
) {
|
|
137
|
+
// This step had tool calls that were auto-executed
|
|
138
|
+
// Add the assistant message with tool_calls
|
|
139
|
+
const stepToolCalls: ToolCall[] = convertAISDKToolCalls(
|
|
140
|
+
step.toolCalls,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
autoExecutedMessages.push({
|
|
144
|
+
role: 'assistant',
|
|
145
|
+
content: step.text || '',
|
|
146
|
+
tool_calls: stepToolCalls,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Add the tool result messages
|
|
150
|
+
step.toolCalls.forEach((toolCall, idx) => {
|
|
151
|
+
const toolResult = step.toolResults[idx];
|
|
152
|
+
const resultStr = getToolResultOutput(toolResult.output);
|
|
153
|
+
|
|
154
|
+
autoExecutedMessages.push({
|
|
155
|
+
role: 'tool' as const,
|
|
156
|
+
content: resultStr,
|
|
157
|
+
tool_call_id:
|
|
158
|
+
toolCall.toolCallId ||
|
|
159
|
+
`tool_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`,
|
|
160
|
+
name: toolCall.toolName,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Extract tool calls
|
|
167
|
+
const toolCalls: ToolCall[] = [];
|
|
168
|
+
if (toolCallsResult && toolCallsResult.length > 0) {
|
|
169
|
+
logger.debug('Processing tool calls from response', {
|
|
170
|
+
toolCallCount: toolCallsResult.length,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
for (const toolCall of toolCallsResult) {
|
|
174
|
+
const tc: ToolCall = convertAISDKToolCalls([toolCall])[0];
|
|
175
|
+
toolCalls.push(tc);
|
|
176
|
+
|
|
177
|
+
logger.debug('Tool call processed', {
|
|
178
|
+
toolName: tc.function.name,
|
|
179
|
+
hasArguments: !!tc.function.arguments,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Note: onToolCall already fired in onStepFinish - no need to call again
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Check for XML tool calls if no native ones
|
|
187
|
+
let content = fullText;
|
|
188
|
+
const xmlResult = processXMLToolCalls(content, tools, callbacks);
|
|
189
|
+
if (xmlResult.toolCalls.length > 0) {
|
|
190
|
+
toolCalls.push(...xmlResult.toolCalls);
|
|
191
|
+
content = xmlResult.cleanedContent;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Calculate performance metrics
|
|
195
|
+
const finalMetrics = endMetrics(metrics);
|
|
196
|
+
|
|
197
|
+
logger.info('Chat request completed successfully', {
|
|
198
|
+
model: currentModel,
|
|
199
|
+
duration: `${finalMetrics.duration.toFixed(2)}ms`,
|
|
200
|
+
responseLength: content.length,
|
|
201
|
+
toolCallsFound: toolCalls.length,
|
|
202
|
+
memoryDelta: formatMemoryUsage(
|
|
203
|
+
finalMetrics.memoryUsage || process.memoryUsage(),
|
|
204
|
+
),
|
|
205
|
+
correlationId,
|
|
206
|
+
provider: providerConfig.name,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
callbacks.onFinish?.();
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
choices: [
|
|
213
|
+
{
|
|
214
|
+
message: {
|
|
215
|
+
role: 'assistant',
|
|
216
|
+
content,
|
|
217
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
// Include auto-executed messages so they can be added to message history
|
|
222
|
+
autoExecutedMessages:
|
|
223
|
+
autoExecutedMessages.length > 0 ? autoExecutedMessages : undefined,
|
|
224
|
+
};
|
|
225
|
+
} catch (error) {
|
|
226
|
+
// Calculate performance metrics even for errors
|
|
227
|
+
const finalMetrics = endMetrics(metrics);
|
|
228
|
+
|
|
229
|
+
// Check if this was a user-initiated cancellation
|
|
230
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
231
|
+
logger.info('Chat request cancelled by user', {
|
|
232
|
+
model: currentModel,
|
|
233
|
+
duration: `${finalMetrics.duration.toFixed(2)}ms`,
|
|
234
|
+
correlationId,
|
|
235
|
+
provider: providerConfig.name,
|
|
236
|
+
});
|
|
237
|
+
throw new Error('Operation was cancelled');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Log the error with performance metrics
|
|
241
|
+
logger.error('Chat request failed', {
|
|
242
|
+
model: currentModel,
|
|
243
|
+
duration: `${finalMetrics.duration.toFixed(2)}ms`,
|
|
244
|
+
error: error instanceof Error ? error.message : error,
|
|
245
|
+
errorName: error instanceof Error ? error.name : 'Unknown',
|
|
246
|
+
correlationId,
|
|
247
|
+
provider: providerConfig.name,
|
|
248
|
+
memoryDelta: formatMemoryUsage(
|
|
249
|
+
finalMetrics.memoryUsage || process.memoryUsage(),
|
|
250
|
+
),
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// AI SDK wraps errors in NoOutputGeneratedError with no useful cause
|
|
254
|
+
// Check if it's a cancellation without an underlying API error
|
|
255
|
+
if (
|
|
256
|
+
error instanceof Error &&
|
|
257
|
+
(error.name === 'AI_NoOutputGeneratedError' ||
|
|
258
|
+
error.message.includes('No output generated'))
|
|
259
|
+
) {
|
|
260
|
+
// Check if there's an underlying RetryError with the real cause
|
|
261
|
+
const rootError = extractRootError(error);
|
|
262
|
+
if (rootError === error) {
|
|
263
|
+
// No underlying error - this is just a cancellation
|
|
264
|
+
throw new Error('Operation was cancelled');
|
|
265
|
+
}
|
|
266
|
+
// There's a real error underneath, parse it
|
|
267
|
+
const userMessage = parseAPIError(rootError);
|
|
268
|
+
throw new Error(userMessage);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Parse any other error (including RetryError and APICallError)
|
|
272
|
+
const userMessage = parseAPIError(error);
|
|
273
|
+
throw new Error(userMessage);
|
|
274
|
+
}
|
|
275
|
+
}, correlationId); // End of withNewCorrelationContext
|
|
276
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import type {StreamCallbacks} from '@/types/index';
|
|
3
|
+
import {createOnStepFinishHandler, createPrepareStepHandler} from './streaming-handler.js';
|
|
4
|
+
import type {TestableMessage} from '../types.js';
|
|
5
|
+
|
|
6
|
+
test('createOnStepFinishHandler calls onToolExecuted callback', t => {
|
|
7
|
+
let callbackCalled = false;
|
|
8
|
+
const callbacks: StreamCallbacks = {
|
|
9
|
+
onToolExecuted: (toolCall, result) => {
|
|
10
|
+
callbackCalled = true;
|
|
11
|
+
t.is(toolCall.function.name, 'test_tool');
|
|
12
|
+
t.is(result, 'test output');
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const handler = createOnStepFinishHandler(callbacks);
|
|
17
|
+
handler({
|
|
18
|
+
toolCalls: [
|
|
19
|
+
{
|
|
20
|
+
toolCallId: 'call_123',
|
|
21
|
+
toolName: 'test_tool',
|
|
22
|
+
input: {},
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
toolResults: [
|
|
26
|
+
{
|
|
27
|
+
output: 'test output',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
t.true(callbackCalled);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('createOnStepFinishHandler handles steps without tool calls', t => {
|
|
36
|
+
const callbacks: StreamCallbacks = {
|
|
37
|
+
onToolExecuted: () => {
|
|
38
|
+
t.fail('Should not be called');
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const handler = createOnStepFinishHandler(callbacks);
|
|
43
|
+
handler({
|
|
44
|
+
text: 'Some text',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
t.pass();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('createOnStepFinishHandler handles steps with tool calls but no results', t => {
|
|
51
|
+
const callbacks: StreamCallbacks = {
|
|
52
|
+
onToolExecuted: () => {
|
|
53
|
+
t.fail('Should not be called');
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const handler = createOnStepFinishHandler(callbacks);
|
|
58
|
+
handler({
|
|
59
|
+
toolCalls: [
|
|
60
|
+
{
|
|
61
|
+
toolCallId: 'call_123',
|
|
62
|
+
toolName: 'test_tool',
|
|
63
|
+
input: {},
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
t.pass();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('createOnStepFinishHandler converts object output to JSON string', t => {
|
|
72
|
+
let resultReceived = '';
|
|
73
|
+
const callbacks: StreamCallbacks = {
|
|
74
|
+
onToolExecuted: (_toolCall, result) => {
|
|
75
|
+
resultReceived = result;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handler = createOnStepFinishHandler(callbacks);
|
|
80
|
+
handler({
|
|
81
|
+
toolCalls: [
|
|
82
|
+
{
|
|
83
|
+
toolCallId: 'call_123',
|
|
84
|
+
toolName: 'test_tool',
|
|
85
|
+
input: {},
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
toolResults: [
|
|
89
|
+
{
|
|
90
|
+
output: {key: 'value'},
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
t.is(resultReceived, '{"key":"value"}');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('createPrepareStepHandler filters empty assistant messages', t => {
|
|
99
|
+
const handler = createPrepareStepHandler();
|
|
100
|
+
const messages = [
|
|
101
|
+
{role: 'user', content: 'Hello'},
|
|
102
|
+
{role: 'assistant', content: ''},
|
|
103
|
+
{role: 'user', content: 'World'},
|
|
104
|
+
] as unknown as TestableMessage[];
|
|
105
|
+
|
|
106
|
+
const result = handler({messages: messages as any});
|
|
107
|
+
|
|
108
|
+
t.truthy(result.messages);
|
|
109
|
+
t.is(result.messages?.length, 2);
|
|
110
|
+
t.is((result.messages?.[0] as any).content, 'Hello');
|
|
111
|
+
t.is((result.messages?.[1] as any).content, 'World');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('createPrepareStepHandler filters orphaned tool messages', t => {
|
|
115
|
+
const handler = createPrepareStepHandler();
|
|
116
|
+
const messages = [
|
|
117
|
+
{role: 'user', content: 'Hello'},
|
|
118
|
+
{role: 'assistant', content: ''},
|
|
119
|
+
{role: 'tool', content: 'Tool result'},
|
|
120
|
+
{role: 'user', content: 'World'},
|
|
121
|
+
] as unknown as TestableMessage[];
|
|
122
|
+
|
|
123
|
+
const result = handler({messages: messages as any});
|
|
124
|
+
|
|
125
|
+
t.truthy(result.messages);
|
|
126
|
+
t.is(result.messages?.length, 2);
|
|
127
|
+
t.is((result.messages?.[0] as any).content, 'Hello');
|
|
128
|
+
t.is((result.messages?.[1] as any).content, 'World');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('createPrepareStepHandler returns empty object when no filtering needed', t => {
|
|
132
|
+
const handler = createPrepareStepHandler();
|
|
133
|
+
const messages = [
|
|
134
|
+
{role: 'user', content: 'Hello'},
|
|
135
|
+
{role: 'assistant', content: 'Hi'},
|
|
136
|
+
] as unknown as TestableMessage[];
|
|
137
|
+
|
|
138
|
+
const result = handler({messages: messages as any});
|
|
139
|
+
|
|
140
|
+
t.deepEqual(result, {});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('createPrepareStepHandler filters multiple empty assistant messages', t => {
|
|
144
|
+
const handler = createPrepareStepHandler();
|
|
145
|
+
const messages = [
|
|
146
|
+
{role: 'user', content: 'Hello'},
|
|
147
|
+
{role: 'assistant', content: ''},
|
|
148
|
+
{role: 'user', content: 'World'},
|
|
149
|
+
{role: 'assistant', content: ' '},
|
|
150
|
+
{role: 'user', content: 'Test'},
|
|
151
|
+
] as unknown as TestableMessage[];
|
|
152
|
+
|
|
153
|
+
const result = handler({messages: messages as any});
|
|
154
|
+
|
|
155
|
+
t.truthy(result.messages);
|
|
156
|
+
t.is(result.messages?.length, 3);
|
|
157
|
+
t.is(result.messages?.[0].role, 'user');
|
|
158
|
+
t.is(result.messages?.[1].role, 'user');
|
|
159
|
+
t.is(result.messages?.[2].role, 'user');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('createPrepareStepHandler keeps non-empty assistant messages', t => {
|
|
163
|
+
const handler = createPrepareStepHandler();
|
|
164
|
+
const messages = [
|
|
165
|
+
{role: 'user', content: 'Hello'},
|
|
166
|
+
{role: 'assistant', content: 'Response'},
|
|
167
|
+
{role: 'user', content: 'World'},
|
|
168
|
+
] as unknown as TestableMessage[];
|
|
169
|
+
|
|
170
|
+
const result = handler({messages: messages as any});
|
|
171
|
+
|
|
172
|
+
t.deepEqual(result, {});
|
|
173
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type {StreamCallbacks, ToolCall} from '@/types/index';
|
|
2
|
+
import {getLogger} from '@/utils/logging';
|
|
3
|
+
import type {ModelMessage} from 'ai';
|
|
4
|
+
import {isEmptyAssistantMessage} from '../converters/message-converter.js';
|
|
5
|
+
import {
|
|
6
|
+
convertAISDKToolCall,
|
|
7
|
+
getToolResultOutput,
|
|
8
|
+
} from '../converters/tool-converter.js';
|
|
9
|
+
import type {TestableMessage} from '../types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates the onStepFinish callback for AI SDK generateText
|
|
13
|
+
* This handles logging and displaying tool execution results
|
|
14
|
+
*/
|
|
15
|
+
export function createOnStepFinishHandler(callbacks: StreamCallbacks): (step: {
|
|
16
|
+
toolCalls?: Array<{toolCallId?: string; toolName: string; input: unknown}>;
|
|
17
|
+
toolResults?: Array<{output: unknown}>;
|
|
18
|
+
text?: string;
|
|
19
|
+
}) => void {
|
|
20
|
+
const logger = getLogger();
|
|
21
|
+
|
|
22
|
+
return step => {
|
|
23
|
+
// Log tool execution steps
|
|
24
|
+
if (step.toolCalls && step.toolCalls.length > 0) {
|
|
25
|
+
logger.trace('AI SDK tool step', {
|
|
26
|
+
stepType: 'tool_execution',
|
|
27
|
+
toolCount: step.toolCalls.length,
|
|
28
|
+
hasResults: !!step.toolResults,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Display formatters for auto-executed tools (after execution with results)
|
|
33
|
+
if (
|
|
34
|
+
step.toolCalls &&
|
|
35
|
+
step.toolResults &&
|
|
36
|
+
step.toolCalls.length === step.toolResults.length
|
|
37
|
+
) {
|
|
38
|
+
step.toolCalls.forEach((toolCall, idx) => {
|
|
39
|
+
const toolResult = step.toolResults?.[idx];
|
|
40
|
+
if (!toolResult) return;
|
|
41
|
+
const tc: ToolCall = convertAISDKToolCall(toolCall);
|
|
42
|
+
const resultStr = getToolResultOutput(toolResult.output);
|
|
43
|
+
|
|
44
|
+
logger.debug('Tool executed', {
|
|
45
|
+
toolName: tc.function.name,
|
|
46
|
+
resultLength: resultStr.length,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
callbacks.onToolExecuted?.(tc, resultStr);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates the prepareStep callback for AI SDK generateText
|
|
57
|
+
* This filters out empty assistant messages and orphaned tool results
|
|
58
|
+
*/
|
|
59
|
+
export function createPrepareStepHandler(): (params: {
|
|
60
|
+
messages: ModelMessage[];
|
|
61
|
+
}) => {messages?: ModelMessage[]} | Record<string, never> {
|
|
62
|
+
const logger = getLogger();
|
|
63
|
+
|
|
64
|
+
return ({messages}) => {
|
|
65
|
+
// Filter out empty assistant messages that would cause API errors
|
|
66
|
+
// "Assistant message must have either content or tool_calls"
|
|
67
|
+
// Also filter out orphaned tool messages that follow empty assistant messages
|
|
68
|
+
const filteredMessages: ModelMessage[] = [];
|
|
69
|
+
const indicesToSkip = new Set<number>();
|
|
70
|
+
|
|
71
|
+
// First pass: identify empty assistant messages and their orphaned tool results
|
|
72
|
+
for (let i = 0; i < messages.length; i++) {
|
|
73
|
+
if (isEmptyAssistantMessage(messages[i] as unknown as TestableMessage)) {
|
|
74
|
+
indicesToSkip.add(i);
|
|
75
|
+
|
|
76
|
+
// Mark any immediately following tool messages as orphaned
|
|
77
|
+
let j = i + 1;
|
|
78
|
+
while (j < messages.length && messages[j].role === 'tool') {
|
|
79
|
+
indicesToSkip.add(j);
|
|
80
|
+
j++;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Second pass: build filtered array
|
|
86
|
+
for (let i = 0; i < messages.length; i++) {
|
|
87
|
+
if (!indicesToSkip.has(i)) {
|
|
88
|
+
filteredMessages.push(messages[i]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Log message filtering
|
|
93
|
+
if (filteredMessages.length !== messages.length) {
|
|
94
|
+
logger.debug(
|
|
95
|
+
'Filtered empty assistant messages and orphaned tool results',
|
|
96
|
+
{
|
|
97
|
+
originalCount: messages.length,
|
|
98
|
+
filteredCount: filteredMessages.length,
|
|
99
|
+
removedCount: messages.length - filteredMessages.length,
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Return filtered messages if any were removed, otherwise no changes
|
|
105
|
+
if (filteredMessages.length !== messages.length) {
|
|
106
|
+
return {messages: filteredMessages};
|
|
107
|
+
}
|
|
108
|
+
return {}; // No modifications needed
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import type {AISDKCoreTool, StreamCallbacks} from '@/types/index';
|
|
3
|
+
import {processXMLToolCalls} from './tool-processor.js';
|
|
4
|
+
|
|
5
|
+
test('processXMLToolCalls returns empty result when no tools available', t => {
|
|
6
|
+
const content = 'Some response text';
|
|
7
|
+
const tools: Record<string, AISDKCoreTool> = {};
|
|
8
|
+
const callbacks: StreamCallbacks = {};
|
|
9
|
+
|
|
10
|
+
const result = processXMLToolCalls(content, tools, callbacks);
|
|
11
|
+
|
|
12
|
+
t.deepEqual(result.toolCalls, []);
|
|
13
|
+
t.is(result.cleanedContent, content);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('processXMLToolCalls returns empty result when content is empty', t => {
|
|
17
|
+
const content = '';
|
|
18
|
+
const tools: Record<string, AISDKCoreTool> = {
|
|
19
|
+
test_tool: {} as AISDKCoreTool, // Type-only check, actual structure doesn't matter for empty content test
|
|
20
|
+
};
|
|
21
|
+
const callbacks: StreamCallbacks = {};
|
|
22
|
+
|
|
23
|
+
const result = processXMLToolCalls(content, tools, callbacks);
|
|
24
|
+
|
|
25
|
+
t.deepEqual(result.toolCalls, []);
|
|
26
|
+
t.is(result.cleanedContent, content);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('processXMLToolCalls handles malformed XML gracefully', t => {
|
|
30
|
+
const content = '<tool_call>\n<name>test_tool</name>\n<arguments>';
|
|
31
|
+
const tools: Record<string, AISDKCoreTool> = {
|
|
32
|
+
test_tool: {} as AISDKCoreTool,
|
|
33
|
+
};
|
|
34
|
+
const callbacks: StreamCallbacks = {
|
|
35
|
+
onToolCall: () => {},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Function should not throw, even with malformed XML
|
|
39
|
+
const result = processXMLToolCalls(content, tools, callbacks);
|
|
40
|
+
|
|
41
|
+
t.truthy(result);
|
|
42
|
+
t.true(Array.isArray(result.toolCalls));
|
|
43
|
+
t.is(typeof result.cleanedContent, 'string');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('processXMLToolCalls handles valid XML content', t => {
|
|
47
|
+
const content =
|
|
48
|
+
'<tool_call>\n<name>test_tool</name>\n<arguments>{"arg": "value"}</arguments>\n</tool_call>';
|
|
49
|
+
const tools: Record<string, AISDKCoreTool> = {
|
|
50
|
+
test_tool: {} as AISDKCoreTool,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const callbacks: StreamCallbacks = {
|
|
54
|
+
onToolCall: () => {},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Function should not throw with valid XML structure
|
|
58
|
+
const result = processXMLToolCalls(content, tools, callbacks);
|
|
59
|
+
|
|
60
|
+
t.truthy(result);
|
|
61
|
+
t.true(Array.isArray(result.toolCalls));
|
|
62
|
+
t.is(typeof result.cleanedContent, 'string');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('processXMLToolCalls handles mixed content', t => {
|
|
66
|
+
const content =
|
|
67
|
+
'Some text before\n<tool_call>\n<name>test_tool</name>\n<arguments>{}</arguments>\n</tool_call>\nSome text after';
|
|
68
|
+
const tools: Record<string, AISDKCoreTool> = {
|
|
69
|
+
test_tool: {} as AISDKCoreTool,
|
|
70
|
+
};
|
|
71
|
+
const callbacks: StreamCallbacks = {};
|
|
72
|
+
|
|
73
|
+
// Function should not throw with mixed content
|
|
74
|
+
const result = processXMLToolCalls(content, tools, callbacks);
|
|
75
|
+
|
|
76
|
+
t.truthy(result);
|
|
77
|
+
t.true(Array.isArray(result.toolCalls));
|
|
78
|
+
t.is(typeof result.cleanedContent, 'string');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('processXMLToolCalls returns original content when no XML tool calls', t => {
|
|
82
|
+
const content = 'Just some plain text response';
|
|
83
|
+
const tools: Record<string, AISDKCoreTool> = {
|
|
84
|
+
test_tool: {} as AISDKCoreTool,
|
|
85
|
+
};
|
|
86
|
+
const callbacks: StreamCallbacks = {};
|
|
87
|
+
|
|
88
|
+
const result = processXMLToolCalls(content, tools, callbacks);
|
|
89
|
+
|
|
90
|
+
t.deepEqual(result.toolCalls, []);
|
|
91
|
+
t.is(result.cleanedContent, content);
|
|
92
|
+
});
|