@tyvm/knowhow 0.0.46 → 0.0.48
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/benchmarks/results/27b0a06/2025-09-27/xai/xai-grok-code-fast-1.json +2909 -0
- package/benchmarks/results/4057aed/2025-08-14/anthropic/anthropic-claude-sonnet-4-20250514.json +1671 -0
- package/jest.config.js +2 -2
- package/package.json +8 -3
- package/src/agents/base/base.ts +30 -24
- package/src/agents/patcher/patcher.ts +26 -5
- package/src/agents/tools/agentCall.ts +4 -2
- package/src/agents/tools/aiClient.ts +3 -11
- package/src/agents/tools/ast/astAppendNode.ts +90 -0
- package/src/agents/tools/ast/astDeleteNode.ts +88 -0
- package/src/agents/tools/ast/astEditNode.ts +95 -0
- package/src/agents/tools/ast/astGetPathForLine.ts +73 -0
- package/src/agents/tools/ast/astListPaths.ts +66 -0
- package/src/agents/tools/ast/index.ts +7 -0
- package/src/agents/tools/callPlugin.ts +8 -2
- package/src/agents/tools/embeddingSearch.ts +2 -1
- package/src/agents/tools/execCommand.ts +239 -94
- package/src/agents/tools/fileSearch.ts +15 -17
- package/src/agents/tools/index.ts +1 -0
- package/src/agents/tools/language/definitions.ts +10 -2
- package/src/agents/tools/language/index.ts +3 -2
- package/src/agents/tools/lintFile.ts +4 -2
- package/src/agents/tools/list.ts +203 -62
- package/src/agents/tools/patch.ts +48 -14
- package/src/agents/tools/readBlocks.ts +34 -0
- package/src/agents/tools/readFile.ts +23 -0
- package/src/agents/tools/stringReplace.ts +33 -9
- package/src/agents/tools/writeFile.ts +55 -0
- package/src/agents/tools/ycmd/server.ts +14 -4
- package/src/chat/CliChatService.ts +6 -1
- package/src/chat/modules/AgentModule.ts +107 -64
- package/src/chat/modules/AskModule.ts +0 -1
- package/src/chat/modules/SetupModule.ts +4 -4
- package/src/chat/modules/SystemModule.ts +28 -5
- package/src/chat/types.ts +2 -0
- package/src/chat-old.ts +2 -2
- package/src/clients/anthropic.ts +22 -1
- package/src/clients/openai.ts +1 -1
- package/src/clients/types.ts +1 -1
- package/src/clients/xai.ts +15 -5
- package/src/config.ts +17 -5
- package/src/dataset/diffs/generate.ts +2 -2
- package/src/dataset/diffs/jsonl.ts +0 -1
- package/src/embeddings.ts +6 -4
- package/src/index.ts +11 -5
- package/src/plugins/GitPlugin.ts +530 -0
- package/src/plugins/LinterPlugin.ts +89 -0
- package/src/plugins/PluginBase.ts +4 -2
- package/src/plugins/asana.ts +4 -2
- package/src/plugins/downloader/plugin.ts +5 -2
- package/src/plugins/embedding.ts +24 -4
- package/src/plugins/figma.ts +7 -3
- package/src/plugins/github.ts +4 -2
- package/src/plugins/jira.ts +4 -2
- package/src/plugins/language.ts +134 -27
- package/src/plugins/linear.ts +4 -2
- package/src/plugins/notion.ts +4 -2
- package/src/plugins/plugins.ts +27 -16
- package/src/plugins/tree-sitter/editor.ts +369 -0
- package/src/plugins/tree-sitter/lang-packs/index.ts +23 -0
- package/src/plugins/tree-sitter/lang-packs/java.ts +59 -0
- package/src/plugins/tree-sitter/lang-packs/javascript.ts +57 -0
- package/src/plugins/tree-sitter/lang-packs/python.ts +45 -0
- package/src/plugins/tree-sitter/lang-packs/types.ts +79 -0
- package/src/plugins/tree-sitter/lang-packs/typescript.ts +49 -0
- package/src/plugins/tree-sitter/parser.ts +444 -0
- package/src/plugins/tree-sitter/simple-paths.ts +467 -0
- package/src/plugins/types.ts +11 -0
- package/src/plugins/url.ts +5 -3
- package/src/plugins/vim.ts +8 -5
- package/src/processors/CustomVariables.ts +19 -7
- package/src/processors/TokenCompressor.ts +13 -13
- package/src/processors/ToolResponseCache.ts +15 -6
- package/src/services/EmbeddingService.ts +18 -9
- package/src/services/EventService.ts +80 -0
- package/src/services/Mcp.ts +5 -0
- package/src/services/S3.ts +4 -3
- package/src/services/Tools.ts +125 -53
- package/src/services/index.ts +16 -11
- package/src/services/types.ts +3 -3
- package/src/types.ts +7 -2
- package/src/worker.ts +14 -1
- package/test-comprehensive.ts +31 -0
- package/tests/clients/AIClient.test.ts +490 -0
- package/tests/manual/agent-events/run-test.ts +203 -0
- package/tests/{integration → manual/file-edits}/figma.test.ts +1 -1
- package/tests/{integration → manual/file-edits}/fileblocks/readwrite.test.ts +7 -3
- package/tests/{integration → manual/file-edits}/patching.test.ts +11 -8
- package/tests/plugins/language/languagePlugin-content-triggers.test.ts +332 -0
- package/tests/plugins/language/languagePlugin-integration.test.ts +456 -0
- package/tests/plugins/language/languagePlugin.test.ts +363 -0
- package/tests/processors/Base64ImageDetector.test.ts +403 -0
- package/tests/processors/CustomVariables.test.ts +430 -0
- package/tests/processors/HarmonyToolProcessor.test.ts +490 -0
- package/tests/processors/TokenCompressor.test.ts +391 -0
- package/tests/processors/ToolResponseCache.test.ts +688 -0
- package/tests/services/Tools.test.ts +1339 -0
- package/tests/test.spec.ts +162 -117
- package/tests/tree-sitter/editor.test.ts +113 -0
- package/tests/tree-sitter/invalid.test.ts +299 -0
- package/tests/tree-sitter/paths/common-edits.test.ts +564 -0
- package/tests/tree-sitter/paths/debug-exact-position.test.ts +44 -0
- package/tests/tree-sitter/paths/debug-line-indexing.test.ts +49 -0
- package/tests/tree-sitter/paths/debug-paths.test.ts +90 -0
- package/tests/tree-sitter/paths/paths.test.ts +170 -0
- package/tests/tree-sitter/paths/simple-paths.test.ts +367 -0
- package/tests/tree-sitter/sample-after.ts +48 -0
- package/tests/tree-sitter/sample-before.ts +25 -0
- package/tests/tree-sitter/test-files/completely-broken.ts +7 -0
- package/tests/tree-sitter/test-files/duplicate-braces.ts +39 -0
- package/tests/tree-sitter/test-files/invalid-nesting.ts +39 -0
- package/tests/tree-sitter/test-files/malformed-signature.ts +39 -0
- package/tests/tree-sitter/test-files/mismatched-parens.ts +39 -0
- package/tests/tree-sitter/test-files/missing-semicolon.ts +39 -0
- package/tests/tree-sitter/test-files/partially-broken.ts +20 -0
- package/tests/tree-sitter/test-files/specific-errors.ts +14 -0
- package/tests/tree-sitter/test-files/unclosed-string.ts +39 -0
- package/tests/tree-sitter/tree-sitter.test.ts +251 -0
- package/ts_build/package.json +8 -3
- package/ts_build/src/agents/base/base.d.ts +2 -2
- package/ts_build/src/agents/base/base.js +24 -20
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/patcher/patcher.js +26 -5
- package/ts_build/src/agents/patcher/patcher.js.map +1 -1
- package/ts_build/src/agents/tools/agentCall.js +2 -1
- package/ts_build/src/agents/tools/agentCall.js.map +1 -1
- package/ts_build/src/agents/tools/aiClient.d.ts +7 -8
- package/ts_build/src/agents/tools/aiClient.js.map +1 -1
- package/ts_build/src/agents/tools/ast/astAppendNode.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astAppendNode.js +96 -0
- package/ts_build/src/agents/tools/ast/astAppendNode.js.map +1 -0
- package/ts_build/src/agents/tools/ast/astDeleteNode.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astDeleteNode.js +94 -0
- package/ts_build/src/agents/tools/ast/astDeleteNode.js.map +1 -0
- package/ts_build/src/agents/tools/ast/astEditNode.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astEditNode.js +96 -0
- package/ts_build/src/agents/tools/ast/astEditNode.js.map +1 -0
- package/ts_build/src/agents/tools/ast/astGetPathForLine.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astGetPathForLine.js +78 -0
- package/ts_build/src/agents/tools/ast/astGetPathForLine.js.map +1 -0
- package/ts_build/src/agents/tools/ast/astListPaths.d.ts +1 -0
- package/ts_build/src/agents/tools/ast/astListPaths.js +78 -0
- package/ts_build/src/agents/tools/ast/astListPaths.js.map +1 -0
- package/ts_build/src/agents/tools/ast/index.d.ts +5 -0
- package/ts_build/src/agents/tools/ast/index.js +14 -0
- package/ts_build/src/agents/tools/ast/index.js.map +1 -0
- package/ts_build/src/agents/tools/astAppendNode.d.ts +1 -0
- package/ts_build/src/agents/tools/astAppendNode.js +98 -0
- package/ts_build/src/agents/tools/astAppendNode.js.map +1 -0
- package/ts_build/src/agents/tools/astDeleteNode.d.ts +1 -0
- package/ts_build/src/agents/tools/astDeleteNode.js +95 -0
- package/ts_build/src/agents/tools/astDeleteNode.js.map +1 -0
- package/ts_build/src/agents/tools/astEditNode.d.ts +1 -0
- package/ts_build/src/agents/tools/astEditNode.js +98 -0
- package/ts_build/src/agents/tools/astEditNode.js.map +1 -0
- package/ts_build/src/agents/tools/astGetPathForLine.d.ts +1 -0
- package/ts_build/src/agents/tools/astGetPathForLine.js +89 -0
- package/ts_build/src/agents/tools/astGetPathForLine.js.map +1 -0
- package/ts_build/src/agents/tools/astListPaths.d.ts +1 -0
- package/ts_build/src/agents/tools/astListPaths.js +82 -0
- package/ts_build/src/agents/tools/astListPaths.js.map +1 -0
- package/ts_build/src/agents/tools/callPlugin.js +4 -2
- package/ts_build/src/agents/tools/callPlugin.js.map +1 -1
- package/ts_build/src/agents/tools/embeddingSearch.js +3 -2
- package/ts_build/src/agents/tools/embeddingSearch.js.map +1 -1
- package/ts_build/src/agents/tools/execCommand.d.ts +2 -2
- package/ts_build/src/agents/tools/execCommand.js +201 -67
- package/ts_build/src/agents/tools/execCommand.js.map +1 -1
- package/ts_build/src/agents/tools/fileSearch.d.ts +1 -1
- package/ts_build/src/agents/tools/fileSearch.js +11 -15
- package/ts_build/src/agents/tools/fileSearch.js.map +1 -1
- package/ts_build/src/agents/tools/github/index.d.ts +1 -1
- package/ts_build/src/agents/tools/index.d.ts +1 -0
- package/ts_build/src/agents/tools/index.js +1 -0
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/language/definitions.js +11 -2
- package/ts_build/src/agents/tools/language/definitions.js.map +1 -1
- package/ts_build/src/agents/tools/language/index.js +4 -3
- package/ts_build/src/agents/tools/language/index.js.map +1 -1
- package/ts_build/src/agents/tools/lintFile.js +4 -2
- package/ts_build/src/agents/tools/lintFile.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +185 -49
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/patch.js +33 -10
- package/ts_build/src/agents/tools/patch.js.map +1 -1
- package/ts_build/src/agents/tools/readBlocks.js +23 -0
- package/ts_build/src/agents/tools/readBlocks.js.map +1 -1
- package/ts_build/src/agents/tools/readFile.js +14 -0
- package/ts_build/src/agents/tools/readFile.js.map +1 -1
- package/ts_build/src/agents/tools/stringReplace.js +19 -2
- package/ts_build/src/agents/tools/stringReplace.js.map +1 -1
- package/ts_build/src/agents/tools/writeFile.js +40 -0
- package/ts_build/src/agents/tools/writeFile.js.map +1 -1
- package/ts_build/src/agents/tools/ycmd/server.js +5 -0
- package/ts_build/src/agents/tools/ycmd/server.js.map +1 -1
- package/ts_build/src/chat/CliChatService.d.ts +1 -0
- package/ts_build/src/chat/CliChatService.js +6 -2
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +5 -1
- package/ts_build/src/chat/modules/AgentModule.js +53 -31
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/AskModule.js.map +1 -1
- package/ts_build/src/chat/modules/SetupModule.js +4 -3
- package/ts_build/src/chat/modules/SetupModule.js.map +1 -1
- package/ts_build/src/chat/modules/SystemModule.js +19 -4
- package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
- package/ts_build/src/chat/modules/index.d.ts +5 -0
- package/ts_build/src/chat/modules/index.js +14 -0
- package/ts_build/src/chat/modules/index.js.map +1 -0
- package/ts_build/src/chat/types.d.ts +2 -0
- package/ts_build/src/chat-old.js +3 -3
- package/ts_build/src/chat-old.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +1 -0
- package/ts_build/src/clients/anthropic.js +22 -1
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/openai.js +1 -1
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +1 -1
- package/ts_build/src/clients/xai.d.ts +7 -0
- package/ts_build/src/clients/xai.js +13 -4
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/config.js +14 -3
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/dataset/diffs/generate.js +2 -2
- package/ts_build/src/dataset/diffs/generate.js.map +1 -1
- package/ts_build/src/dataset/diffs/jsonl.js.map +1 -1
- package/ts_build/src/embeddings.js +7 -9
- package/ts_build/src/embeddings.js.map +1 -1
- package/ts_build/src/index.js +10 -10
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/plugins/GitPlugin.d.ts +39 -0
- package/ts_build/src/plugins/GitPlugin.js +439 -0
- package/ts_build/src/plugins/GitPlugin.js.map +1 -0
- package/ts_build/src/plugins/LinterPlugin.d.ts +15 -0
- package/ts_build/src/plugins/LinterPlugin.js +65 -0
- package/ts_build/src/plugins/LinterPlugin.js.map +1 -0
- package/ts_build/src/plugins/PluginBase.d.ts +4 -3
- package/ts_build/src/plugins/PluginBase.js +3 -3
- package/ts_build/src/plugins/PluginBase.js.map +1 -1
- package/ts_build/src/plugins/asana.d.ts +3 -1
- package/ts_build/src/plugins/asana.js +3 -2
- package/ts_build/src/plugins/asana.js.map +1 -1
- package/ts_build/src/plugins/downloader/plugin.d.ts +3 -1
- package/ts_build/src/plugins/downloader/plugin.js +3 -2
- package/ts_build/src/plugins/downloader/plugin.js.map +1 -1
- package/ts_build/src/plugins/embedding.d.ts +5 -1
- package/ts_build/src/plugins/embedding.js +15 -3
- package/ts_build/src/plugins/embedding.js.map +1 -1
- package/ts_build/src/plugins/figma.d.ts +3 -1
- package/ts_build/src/plugins/figma.js +28 -4
- package/ts_build/src/plugins/figma.js.map +1 -1
- package/ts_build/src/plugins/github.d.ts +3 -1
- package/ts_build/src/plugins/github.js +3 -2
- package/ts_build/src/plugins/github.js.map +1 -1
- package/ts_build/src/plugins/jira.d.ts +3 -1
- package/ts_build/src/plugins/jira.js +3 -2
- package/ts_build/src/plugins/jira.js.map +1 -1
- package/ts_build/src/plugins/language.d.ts +7 -4
- package/ts_build/src/plugins/language.js +85 -20
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/plugins/linear.d.ts +3 -1
- package/ts_build/src/plugins/linear.js +3 -2
- package/ts_build/src/plugins/linear.js.map +1 -1
- package/ts_build/src/plugins/notion.d.ts +3 -1
- package/ts_build/src/plugins/notion.js +3 -2
- package/ts_build/src/plugins/notion.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +4 -3
- package/ts_build/src/plugins/plugins.js +24 -14
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/plugins/tree-sitter/editor.d.ts +34 -0
- package/ts_build/src/plugins/tree-sitter/editor.js +218 -0
- package/ts_build/src/plugins/tree-sitter/editor.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.d.ts +29 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.js +538 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths-new.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths.d.ts +22 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths.js +332 -0
- package/ts_build/src/plugins/tree-sitter/human-readable-paths.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/index.d.ts +8 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/index.js +26 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/index.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/java.d.ts +2 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/java.js +61 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/java.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.d.ts +2 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.js +59 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/javascript.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/python.d.ts +2 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/python.js +47 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/python.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/types.d.ts +43 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/types.js +3 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/types.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.d.ts +2 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.js +50 -0
- package/ts_build/src/plugins/tree-sitter/lang-packs/typescript.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/parser.d.ts +75 -0
- package/ts_build/src/plugins/tree-sitter/parser.js +306 -0
- package/ts_build/src/plugins/tree-sitter/parser.js.map +1 -0
- package/ts_build/src/plugins/tree-sitter/simple-paths.d.ts +22 -0
- package/ts_build/src/plugins/tree-sitter/simple-paths.js +332 -0
- package/ts_build/src/plugins/tree-sitter/simple-paths.js.map +1 -0
- package/ts_build/src/plugins/types.d.ts +10 -0
- package/ts_build/src/plugins/url.d.ts +3 -2
- package/ts_build/src/plugins/url.js +3 -2
- package/ts_build/src/plugins/url.js.map +1 -1
- package/ts_build/src/plugins/vim.d.ts +4 -2
- package/ts_build/src/plugins/vim.js +6 -8
- package/ts_build/src/plugins/vim.js.map +1 -1
- package/ts_build/src/processors/CustomVariables.js +12 -3
- package/ts_build/src/processors/CustomVariables.js.map +1 -1
- package/ts_build/src/processors/TokenCompressor.js +8 -11
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/processors/ToolResponseCache.d.ts +2 -2
- package/ts_build/src/processors/ToolResponseCache.js +12 -2
- package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
- package/ts_build/src/services/EmbeddingService.d.ts +10 -1
- package/ts_build/src/services/EmbeddingService.js +12 -12
- package/ts_build/src/services/EmbeddingService.js.map +1 -1
- package/ts_build/src/services/EventService.d.ts +7 -0
- package/ts_build/src/services/EventService.js +49 -0
- package/ts_build/src/services/EventService.js.map +1 -1
- package/ts_build/src/services/Mcp.js +8 -0
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/services/S3.js +4 -3
- package/ts_build/src/services/S3.js.map +1 -1
- package/ts_build/src/services/Tools.d.ts +1 -0
- package/ts_build/src/services/Tools.js +97 -35
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/index.d.ts +4 -5
- package/ts_build/src/services/index.js +14 -9
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/types.js +3 -3
- package/ts_build/src/services/types.js.map +1 -1
- package/ts_build/src/types.d.ts +7 -1
- package/ts_build/src/types.js +4 -0
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/worker.js +12 -1
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/tests/clients/AIClient.test.d.ts +1 -0
- package/ts_build/tests/clients/AIClient.test.js +377 -0
- package/ts_build/tests/clients/AIClient.test.js.map +1 -0
- package/ts_build/tests/languagePlugin.test.js +217 -11
- package/ts_build/tests/languagePlugin.test.js.map +1 -1
- package/ts_build/tests/manual/agent-events/event-handler-reliability.test.d.ts +1 -0
- package/ts_build/tests/manual/agent-events/event-handler-reliability.test.js +315 -0
- package/ts_build/tests/manual/agent-events/event-handler-reliability.test.js.map +1 -0
- package/ts_build/tests/manual/agent-events/run-test.d.ts +2 -0
- package/ts_build/tests/manual/agent-events/run-test.js +148 -0
- package/ts_build/tests/manual/agent-events/run-test.js.map +1 -0
- package/ts_build/tests/manual/file-edits/figma.test.d.ts +1 -0
- package/ts_build/tests/manual/file-edits/figma.test.js +47 -0
- package/ts_build/tests/manual/file-edits/figma.test.js.map +1 -0
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.d.ts +1 -0
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.js +100 -0
- package/ts_build/tests/manual/file-edits/fileblocks/readwrite.test.js.map +1 -0
- package/ts_build/tests/manual/file-edits/patching.test.d.ts +1 -0
- package/ts_build/tests/manual/file-edits/patching.test.js +119 -0
- package/ts_build/tests/manual/file-edits/patching.test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.d.ts +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +277 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.d.ts +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +331 -0
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin.test.d.ts +1 -0
- package/ts_build/tests/plugins/language/languagePlugin.test.js +286 -0
- package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.d.ts +1 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js +351 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js.map +1 -0
- package/ts_build/tests/processors/CustomVariables.test.d.ts +1 -0
- package/ts_build/tests/processors/CustomVariables.test.js +351 -0
- package/ts_build/tests/processors/CustomVariables.test.js.map +1 -0
- package/ts_build/tests/processors/HarmonyToolProcessor.test.d.ts +1 -0
- package/ts_build/tests/processors/HarmonyToolProcessor.test.js +382 -0
- package/ts_build/tests/processors/HarmonyToolProcessor.test.js.map +1 -0
- package/ts_build/tests/processors/TokenCompressor.test.d.ts +1 -0
- package/ts_build/tests/processors/TokenCompressor.test.js +300 -0
- package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -0
- package/ts_build/tests/processors/ToolResponseCache.test.d.ts +1 -0
- package/ts_build/tests/processors/ToolResponseCache.test.js +539 -0
- package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -0
- package/ts_build/tests/services/Plugins/plugin-event-integration.test.d.ts +1 -0
- package/ts_build/tests/services/Plugins/plugin-event-integration.test.js +232 -0
- package/ts_build/tests/services/Plugins/plugin-event-integration.test.js.map +1 -0
- package/ts_build/tests/services/Tools.test.d.ts +1 -0
- package/ts_build/tests/services/Tools.test.js +1059 -0
- package/ts_build/tests/services/Tools.test.js.map +1 -0
- package/ts_build/tests/test.spec.js +110 -68
- package/ts_build/tests/test.spec.js.map +1 -1
- package/ts_build/tests/tree-sitter/editor.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/editor.test.js +85 -0
- package/ts_build/tests/tree-sitter/editor.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/invalid.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/invalid.test.js +198 -0
- package/ts_build/tests/tree-sitter/invalid.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/common-edits.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/common-edits.test.js +347 -0
- package/ts_build/tests/tree-sitter/paths/common-edits.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.js +35 -0
- package/ts_build/tests/tree-sitter/paths/debug-exact-position.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.js +38 -0
- package/ts_build/tests/tree-sitter/paths/debug-line-indexing.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-paths.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/debug-paths.test.js +74 -0
- package/ts_build/tests/tree-sitter/paths/debug-paths.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.js +302 -0
- package/ts_build/tests/tree-sitter/paths/human-readable-paths.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/paths.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/paths.test.js +116 -0
- package/ts_build/tests/tree-sitter/paths/paths.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/paths/simple-paths.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/paths/simple-paths.test.js +302 -0
- package/ts_build/tests/tree-sitter/paths/simple-paths.test.js.map +1 -0
- package/ts_build/tests/tree-sitter/sample-after.d.ts +11 -0
- package/ts_build/tests/tree-sitter/sample-after.js +44 -0
- package/ts_build/tests/tree-sitter/sample-after.js.map +1 -0
- package/ts_build/tests/tree-sitter/sample-before.d.ts +9 -0
- package/ts_build/tests/tree-sitter/sample-before.js +28 -0
- package/ts_build/tests/tree-sitter/sample-before.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/completely-broken.d.ts +2 -0
- package/ts_build/tests/tree-sitter/test-files/completely-broken.js +17 -0
- package/ts_build/tests/tree-sitter/test-files/completely-broken.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/duplicate-braces.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/duplicate-braces.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/duplicate-braces.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/invalid-nesting.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/invalid-nesting.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/invalid-nesting.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/malformed-signature.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/malformed-signature.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/malformed-signature.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/mismatched-parens.d.ts +10 -0
- package/ts_build/tests/tree-sitter/test-files/mismatched-parens.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/mismatched-parens.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/missing-semicolon.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/missing-semicolon.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/missing-semicolon.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/partially-broken.d.ts +6 -0
- package/ts_build/tests/tree-sitter/test-files/partially-broken.js +20 -0
- package/ts_build/tests/tree-sitter/test-files/partially-broken.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/specific-errors.d.ts +7 -0
- package/ts_build/tests/tree-sitter/test-files/specific-errors.js +14 -0
- package/ts_build/tests/tree-sitter/test-files/specific-errors.js.map +1 -0
- package/ts_build/tests/tree-sitter/test-files/unclosed-string.d.ts +8 -0
- package/ts_build/tests/tree-sitter/test-files/unclosed-string.js +38 -0
- package/ts_build/tests/tree-sitter/test-files/unclosed-string.js.map +1 -0
- package/ts_build/tests/tree-sitter/tree-sitter.test.d.ts +1 -0
- package/ts_build/tests/tree-sitter/tree-sitter.test.js +185 -0
- package/ts_build/tests/tree-sitter/tree-sitter.test.js.map +1 -0
- package/tsconfig.json +2 -1
- package/tests/languagePlugin.test.ts +0 -74
- /package/src/chat/modules/{index.js → index.ts} +0 -0
- /package/tests/{integration → manual/file-edits}/patching/input.txt +0 -0
- /package/tests/{integration → manual/file-edits}/patching/output.txt +0 -0
- /package/tests/{integration → manual/file-edits}/patching/patch.txt +0 -0
- /package/tests/{integration → manual/file-edits}/patching/unseen.txt +0 -0
|
@@ -0,0 +1,1339 @@
|
|
|
1
|
+
import { ToolsService, ToolContext } from "../../src/services/Tools";
|
|
2
|
+
import { Tool, ToolCall } from "../../src/clients/types";
|
|
3
|
+
import { createPatternMatcher } from "../../src/services/types";
|
|
4
|
+
import { AgentService } from "../../src/services/AgentService";
|
|
5
|
+
import { EventService } from "../../src/services/EventService";
|
|
6
|
+
import { AIClient } from "../../src/clients";
|
|
7
|
+
import { PluginService } from "../../src/plugins/plugins";
|
|
8
|
+
|
|
9
|
+
describe("ToolsService", () => {
|
|
10
|
+
let toolsService: ToolsService;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
toolsService = new ToolsService();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("constructor", () => {
|
|
17
|
+
it("should initialize with empty context when no context is provided", () => {
|
|
18
|
+
const service = new ToolsService();
|
|
19
|
+
const context = service.getContext();
|
|
20
|
+
|
|
21
|
+
expect(context).toBeDefined();
|
|
22
|
+
expect(context.Tools).toBe(service);
|
|
23
|
+
expect(context.Agents).toBeUndefined();
|
|
24
|
+
expect(context.Events).toBeUndefined();
|
|
25
|
+
expect(context.Clients).toBeUndefined();
|
|
26
|
+
expect(context.Plugins).toBeUndefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should initialize with provided context", () => {
|
|
30
|
+
const mockAgents = {} as AgentService;
|
|
31
|
+
const mockEvents = {} as EventService;
|
|
32
|
+
const initialContext: ToolContext = {
|
|
33
|
+
Agents: mockAgents,
|
|
34
|
+
Events: mockEvents,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const service = new ToolsService(initialContext);
|
|
38
|
+
const context = service.getContext();
|
|
39
|
+
|
|
40
|
+
expect(context.Tools).toBe(service);
|
|
41
|
+
expect(context.Agents).toBe(mockAgents);
|
|
42
|
+
expect(context.Events).toBe(mockEvents);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("context management", () => {
|
|
47
|
+
it("should set and get context", () => {
|
|
48
|
+
const mockContext: ToolContext = {
|
|
49
|
+
Agents: {} as AgentService,
|
|
50
|
+
metadata: { test: "value" },
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
toolsService.setContext(mockContext);
|
|
54
|
+
const context = toolsService.getContext();
|
|
55
|
+
|
|
56
|
+
expect(context.Tools).toBe(toolsService);
|
|
57
|
+
expect(context.Agents).toBe(mockContext.Agents);
|
|
58
|
+
expect(context.metadata).toEqual({ test: "value" });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should add context properties", () => {
|
|
62
|
+
const mockAgents = {} as AgentService;
|
|
63
|
+
|
|
64
|
+
toolsService.addContext("Agents", mockAgents);
|
|
65
|
+
const context = toolsService.getContext();
|
|
66
|
+
|
|
67
|
+
expect(context.Agents).toBe(mockAgents);
|
|
68
|
+
expect(context.Tools).toBe(toolsService);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("tool management", () => {
|
|
73
|
+
const mockTool: Tool = {
|
|
74
|
+
type: "function",
|
|
75
|
+
function: {
|
|
76
|
+
name: "testTool",
|
|
77
|
+
description: "A test tool",
|
|
78
|
+
parameters: {
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {
|
|
81
|
+
input: { type: "string", description: "Test input" },
|
|
82
|
+
},
|
|
83
|
+
required: ["input"],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
it("should add a single tool", () => {
|
|
89
|
+
toolsService.addTool(mockTool);
|
|
90
|
+
|
|
91
|
+
expect(toolsService.getTools()).toContain(mockTool);
|
|
92
|
+
expect(toolsService.getToolNames()).toContain("testTool");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should add multiple tools", () => {
|
|
96
|
+
const tool2: Tool = {
|
|
97
|
+
type: "function",
|
|
98
|
+
function: {
|
|
99
|
+
name: "testTool2",
|
|
100
|
+
description: "Another test tool",
|
|
101
|
+
parameters: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {},
|
|
104
|
+
required: [],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
toolsService.addTools([mockTool, tool2]);
|
|
110
|
+
|
|
111
|
+
expect(toolsService.getTools()).toHaveLength(2);
|
|
112
|
+
expect(toolsService.getToolNames()).toEqual(["testTool", "testTool2"]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should prevent duplicate tool names", () => {
|
|
116
|
+
toolsService.addTool(mockTool);
|
|
117
|
+
toolsService.addTools([mockTool]); // Try to add same tool again
|
|
118
|
+
|
|
119
|
+
expect(toolsService.getTools()).toHaveLength(1);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should get tool by name", () => {
|
|
123
|
+
toolsService.addTool(mockTool);
|
|
124
|
+
|
|
125
|
+
const retrievedTool = toolsService.getTool("testTool");
|
|
126
|
+
expect(retrievedTool).toBe(mockTool);
|
|
127
|
+
|
|
128
|
+
const nonExistentTool = toolsService.getTool("nonExistent");
|
|
129
|
+
expect(nonExistentTool).toBeUndefined();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should get tools by names", () => {
|
|
133
|
+
const tool2: Tool = {
|
|
134
|
+
type: "function",
|
|
135
|
+
function: {
|
|
136
|
+
name: "testTool2",
|
|
137
|
+
description: "Another test tool",
|
|
138
|
+
parameters: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {},
|
|
141
|
+
required: [],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
toolsService.addTools([mockTool, tool2]);
|
|
147
|
+
|
|
148
|
+
const selectedTools = toolsService.getToolsByNames(["testTool"]);
|
|
149
|
+
expect(selectedTools).toHaveLength(1);
|
|
150
|
+
expect(selectedTools[0]).toBe(mockTool);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("function management", () => {
|
|
155
|
+
it("should set and get functions", () => {
|
|
156
|
+
const testFunction = jest.fn().mockReturnValue("test result");
|
|
157
|
+
|
|
158
|
+
toolsService.setFunction("testFunc", testFunction);
|
|
159
|
+
const retrievedFunction = toolsService.getFunction("testFunc");
|
|
160
|
+
|
|
161
|
+
expect(retrievedFunction).toBeDefined();
|
|
162
|
+
expect(typeof retrievedFunction).toBe("function");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should add multiple functions", () => {
|
|
166
|
+
const functions = {
|
|
167
|
+
func1: jest.fn(),
|
|
168
|
+
func2: jest.fn(),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
toolsService.addFunctions(functions);
|
|
172
|
+
|
|
173
|
+
expect(toolsService.getFunction("func1")).toBeDefined();
|
|
174
|
+
expect(toolsService.getFunction("func2")).toBeDefined();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should set multiple functions by names and functions arrays", () => {
|
|
178
|
+
const func1 = jest.fn();
|
|
179
|
+
const func2 = jest.fn();
|
|
180
|
+
|
|
181
|
+
toolsService.setFunctions(["test1", "test2"], [func1, func2]);
|
|
182
|
+
|
|
183
|
+
expect(toolsService.getFunction("test1")).toBeDefined();
|
|
184
|
+
expect(toolsService.getFunction("test2")).toBeDefined();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should define tools and functions together", () => {
|
|
188
|
+
const mockTool: Tool = {
|
|
189
|
+
type: "function",
|
|
190
|
+
function: {
|
|
191
|
+
name: "combinedTool",
|
|
192
|
+
parameters: {
|
|
193
|
+
type: "object",
|
|
194
|
+
properties: {},
|
|
195
|
+
required: [],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const mockFunction = jest.fn();
|
|
201
|
+
|
|
202
|
+
toolsService.defineTools([mockTool], { combinedTool: mockFunction });
|
|
203
|
+
|
|
204
|
+
expect(toolsService.getTool("combinedTool")).toBe(mockTool);
|
|
205
|
+
expect(toolsService.getFunction("combinedTool")).toBeDefined();
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe("copyToolsFrom", () => {
|
|
210
|
+
it("should copy tools and functions from another ToolsService", () => {
|
|
211
|
+
const sourceService = new ToolsService();
|
|
212
|
+
const mockTool: Tool = {
|
|
213
|
+
type: "function",
|
|
214
|
+
function: {
|
|
215
|
+
name: "sourceTool",
|
|
216
|
+
parameters: {
|
|
217
|
+
type: "object",
|
|
218
|
+
properties: {},
|
|
219
|
+
required: [],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
const mockFunction = jest.fn();
|
|
224
|
+
|
|
225
|
+
sourceService.addTool(mockTool);
|
|
226
|
+
sourceService.setFunction("sourceTool", mockFunction);
|
|
227
|
+
|
|
228
|
+
toolsService.copyToolsFrom(["sourceTool"], sourceService);
|
|
229
|
+
|
|
230
|
+
expect(toolsService.getTool("sourceTool")).toEqual(mockTool);
|
|
231
|
+
expect(toolsService.getFunction("sourceTool")).toBeDefined();
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
describe("callTool", () => {
|
|
235
|
+
const mockTool: Tool = {
|
|
236
|
+
type: "function",
|
|
237
|
+
function: {
|
|
238
|
+
name: "testTool",
|
|
239
|
+
description: "A test tool",
|
|
240
|
+
parameters: {
|
|
241
|
+
type: "object",
|
|
242
|
+
properties: {
|
|
243
|
+
input: { type: "string", description: "Test input" },
|
|
244
|
+
optional: { type: "string", description: "Optional input" },
|
|
245
|
+
},
|
|
246
|
+
required: ["input"],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const mockToolCall: ToolCall = {
|
|
252
|
+
id: "call_123",
|
|
253
|
+
type: "function",
|
|
254
|
+
function: {
|
|
255
|
+
name: "testTool",
|
|
256
|
+
arguments: JSON.stringify({ input: "test value" }),
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
beforeEach(() => {
|
|
261
|
+
toolsService.addTool(mockTool);
|
|
262
|
+
jest.clearAllMocks();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("should successfully call a tool with correct arguments", async () => {
|
|
266
|
+
const mockFunction = jest.fn().mockResolvedValue("success result");
|
|
267
|
+
toolsService.setFunction("testTool", mockFunction);
|
|
268
|
+
|
|
269
|
+
const result = await toolsService.callTool(mockToolCall);
|
|
270
|
+
|
|
271
|
+
expect(mockFunction).toHaveBeenCalledWith({ input: "test value" });
|
|
272
|
+
expect(result.toolMessages).toHaveLength(1);
|
|
273
|
+
expect(result.toolMessages[0]).toEqual({
|
|
274
|
+
tool_call_id: "call_123",
|
|
275
|
+
role: "tool",
|
|
276
|
+
name: "testTool",
|
|
277
|
+
content: "success result",
|
|
278
|
+
});
|
|
279
|
+
expect(result.functionResp).toBe("success result");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("should handle positional arguments", async () => {
|
|
283
|
+
const positionalTool: Tool = {
|
|
284
|
+
type: "function",
|
|
285
|
+
function: {
|
|
286
|
+
name: "positionalTool",
|
|
287
|
+
parameters: {
|
|
288
|
+
type: "object",
|
|
289
|
+
positional: true,
|
|
290
|
+
properties: {
|
|
291
|
+
arg1: { type: "string" },
|
|
292
|
+
arg2: { type: "number" },
|
|
293
|
+
},
|
|
294
|
+
required: ["arg1", "arg2"],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const positionalCall: ToolCall = {
|
|
300
|
+
id: "call_456",
|
|
301
|
+
type: "function",
|
|
302
|
+
function: {
|
|
303
|
+
name: "positionalTool",
|
|
304
|
+
arguments: JSON.stringify({ arg1: "hello", arg2: 42 }),
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const mockFunction = jest.fn().mockResolvedValue("positional result");
|
|
309
|
+
|
|
310
|
+
toolsService.addTool(positionalTool);
|
|
311
|
+
toolsService.setFunction("positionalTool", mockFunction);
|
|
312
|
+
|
|
313
|
+
await toolsService.callTool(positionalCall);
|
|
314
|
+
|
|
315
|
+
expect(mockFunction).toHaveBeenCalledWith("hello", 42);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it("should handle tool not enabled error", async () => {
|
|
319
|
+
const mockFunction = jest.fn();
|
|
320
|
+
toolsService.setFunction("testTool", mockFunction);
|
|
321
|
+
|
|
322
|
+
const result = await toolsService.callTool(mockToolCall, ["otherTool"]);
|
|
323
|
+
|
|
324
|
+
expect(mockFunction).not.toHaveBeenCalled();
|
|
325
|
+
expect(result.toolMessages[0].name).toBe("error");
|
|
326
|
+
expect(result.toolMessages[0].content).toContain("not enabled");
|
|
327
|
+
expect(result.functionResp).toBeUndefined();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("should handle tool definition not found", async () => {
|
|
331
|
+
const unknownToolCall: ToolCall = {
|
|
332
|
+
id: "call_unknown",
|
|
333
|
+
type: "function",
|
|
334
|
+
function: {
|
|
335
|
+
name: "unknownTool",
|
|
336
|
+
arguments: "{}",
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const result = await toolsService.callTool(unknownToolCall);
|
|
341
|
+
|
|
342
|
+
expect(result.toolMessages[0].name).toBe("error");
|
|
343
|
+
expect(result.toolMessages[0].content).toContain("not enabled");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it("should handle function implementation not found", async () => {
|
|
347
|
+
// Tool is defined but no function implementation
|
|
348
|
+
const result = await toolsService.callTool(mockToolCall);
|
|
349
|
+
|
|
350
|
+
expect(result.toolMessages[0].name).toBe("error");
|
|
351
|
+
expect(result.toolMessages[0].content).toContain("not found");
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("should handle function execution errors", async () => {
|
|
355
|
+
const mockFunction = jest
|
|
356
|
+
.fn()
|
|
357
|
+
.mockRejectedValue(new Error("Function failed"));
|
|
358
|
+
toolsService.setFunction("testTool", mockFunction);
|
|
359
|
+
|
|
360
|
+
const result = await toolsService.callTool(mockToolCall);
|
|
361
|
+
|
|
362
|
+
expect(result.toolMessages[0].name).toBe("error");
|
|
363
|
+
expect(result.toolMessages[0].content).toContain(
|
|
364
|
+
"ERROR: Function failed"
|
|
365
|
+
);
|
|
366
|
+
expect(result.functionResp).toBeUndefined();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("should handle object responses by converting to JSON", async () => {
|
|
370
|
+
const mockFunction = jest
|
|
371
|
+
.fn()
|
|
372
|
+
.mockResolvedValue({ key: "value", nested: { data: 123 } });
|
|
373
|
+
toolsService.setFunction("testTool", mockFunction);
|
|
374
|
+
|
|
375
|
+
const result = await toolsService.callTool(mockToolCall);
|
|
376
|
+
|
|
377
|
+
expect(result.toolMessages[0].content).toBe(
|
|
378
|
+
JSON.stringify({ key: "value", nested: { data: 123 } }, null, 2)
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it("should handle multi_tool_use.parallel special case", async () => {
|
|
383
|
+
const parallelTool: Tool = {
|
|
384
|
+
type: "function",
|
|
385
|
+
function: {
|
|
386
|
+
name: "multi_tool_use.parallel",
|
|
387
|
+
parameters: {
|
|
388
|
+
type: "object",
|
|
389
|
+
properties: {},
|
|
390
|
+
required: [],
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const parallelCall: ToolCall = {
|
|
396
|
+
id: "call_parallel",
|
|
397
|
+
type: "function",
|
|
398
|
+
function: {
|
|
399
|
+
name: "multi_tool_use.parallel",
|
|
400
|
+
arguments: JSON.stringify([
|
|
401
|
+
{ recipient_name: "tool1.action", parameters: {} },
|
|
402
|
+
{ recipient_name: "tool2.action", parameters: {} },
|
|
403
|
+
]),
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const mockFunction = jest.fn().mockResolvedValue(["result1", "result2"]);
|
|
408
|
+
|
|
409
|
+
toolsService.addTool(parallelTool);
|
|
410
|
+
toolsService.setFunction("multi_tool_use.parallel", mockFunction);
|
|
411
|
+
|
|
412
|
+
const result = await toolsService.callTool(parallelCall);
|
|
413
|
+
|
|
414
|
+
expect(result.toolMessages).toHaveLength(2);
|
|
415
|
+
expect(result.toolMessages[0].tool_call_id).toBe("call_parallel_0");
|
|
416
|
+
expect(result.toolMessages[1].tool_call_id).toBe("call_parallel_1");
|
|
417
|
+
expect(result.toolMessages[0].name).toBe("action");
|
|
418
|
+
expect(result.toolMessages[1].name).toBe("action");
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it("should handle string arguments that need parsing", async () => {
|
|
422
|
+
const toolCallWithStringArgs: ToolCall = {
|
|
423
|
+
id: "call_string",
|
|
424
|
+
type: "function",
|
|
425
|
+
function: {
|
|
426
|
+
name: "testTool",
|
|
427
|
+
arguments: '{"input": "escaped\\nstring"}',
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const mockFunction = jest.fn().mockResolvedValue("success");
|
|
432
|
+
toolsService.setFunction("testTool", mockFunction);
|
|
433
|
+
|
|
434
|
+
await toolsService.callTool(toolCallWithStringArgs);
|
|
435
|
+
|
|
436
|
+
expect(mockFunction).toHaveBeenCalledWith({ input: "escaped\nstring" });
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
describe("Tool Override System", () => {
|
|
440
|
+
const mockTool: Tool = {
|
|
441
|
+
type: "function",
|
|
442
|
+
function: {
|
|
443
|
+
name: "overridableTool",
|
|
444
|
+
description: "A tool that can be overridden",
|
|
445
|
+
parameters: {
|
|
446
|
+
type: "object",
|
|
447
|
+
properties: {
|
|
448
|
+
input: { type: "string" },
|
|
449
|
+
},
|
|
450
|
+
required: ["input"],
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
beforeEach(() => {
|
|
456
|
+
toolsService.addTool(mockTool);
|
|
457
|
+
jest.clearAllMocks();
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("should register override with string pattern", () => {
|
|
461
|
+
const overrideFunction = jest.fn().mockResolvedValue("override result");
|
|
462
|
+
|
|
463
|
+
toolsService.registerOverride("overridableTool", overrideFunction);
|
|
464
|
+
|
|
465
|
+
expect(() => toolsService.getFunction("overridableTool")).not.toThrow();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it("should register override with regex pattern", () => {
|
|
469
|
+
const overrideFunction = jest.fn().mockResolvedValue("regex override");
|
|
470
|
+
const pattern = /^overridable/;
|
|
471
|
+
|
|
472
|
+
toolsService.registerOverride(pattern, overrideFunction);
|
|
473
|
+
|
|
474
|
+
expect(() => toolsService.getFunction("overridableTool")).not.toThrow();
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it("should execute override function instead of original", async () => {
|
|
478
|
+
const originalFunction = jest.fn().mockResolvedValue("original result");
|
|
479
|
+
const overrideFunction = jest.fn().mockResolvedValue("override result");
|
|
480
|
+
|
|
481
|
+
toolsService.setFunction("overridableTool", originalFunction);
|
|
482
|
+
toolsService.registerOverride("overridableTool", overrideFunction);
|
|
483
|
+
|
|
484
|
+
const toolCall: ToolCall = {
|
|
485
|
+
id: "call_override",
|
|
486
|
+
type: "function",
|
|
487
|
+
function: {
|
|
488
|
+
name: "overridableTool",
|
|
489
|
+
arguments: JSON.stringify({ input: "test" }),
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const result = await toolsService.callTool(toolCall);
|
|
494
|
+
|
|
495
|
+
expect(overrideFunction).toHaveBeenCalledWith(
|
|
496
|
+
[{ input: "test" }],
|
|
497
|
+
expect.any(Object)
|
|
498
|
+
);
|
|
499
|
+
expect(originalFunction).not.toHaveBeenCalled();
|
|
500
|
+
expect(result.functionResp).toBe("override result");
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it("should handle override priority ordering", async () => {
|
|
504
|
+
const override1 = jest.fn().mockResolvedValue("override1");
|
|
505
|
+
const override2 = jest.fn().mockResolvedValue("override2");
|
|
506
|
+
|
|
507
|
+
// Register with different priorities
|
|
508
|
+
toolsService.registerOverride("overridableTool", override1, 1);
|
|
509
|
+
toolsService.registerOverride("overridableTool", override2, 2);
|
|
510
|
+
|
|
511
|
+
// Higher priority should be used
|
|
512
|
+
const func = toolsService.getFunction("overridableTool");
|
|
513
|
+
// Function should be bound version of override2, test by calling it
|
|
514
|
+
expect(await func({})).toBe("override2");
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
it("should handle pattern matching with wildcards", async () => {
|
|
518
|
+
const overrideFunction = jest.fn().mockResolvedValue("wildcard override");
|
|
519
|
+
|
|
520
|
+
toolsService.registerOverride("overrid*Tool", overrideFunction);
|
|
521
|
+
|
|
522
|
+
const func = toolsService.getFunction("overridableTool");
|
|
523
|
+
// Function should be bound version of override, test by calling it
|
|
524
|
+
expect(await func({})).toBe("wildcard override");
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it("should remove overrides", async () => {
|
|
528
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
529
|
+
const overrideFunction = jest.fn().mockResolvedValue("override");
|
|
530
|
+
|
|
531
|
+
toolsService.setFunction("overridableTool", originalFunction);
|
|
532
|
+
toolsService.registerOverride("overridableTool", overrideFunction);
|
|
533
|
+
|
|
534
|
+
// Override should be active
|
|
535
|
+
const overriddenFunc = toolsService.getFunction("overridableTool");
|
|
536
|
+
expect(await overriddenFunc({})).toBe("override");
|
|
537
|
+
|
|
538
|
+
toolsService.removeOverride("overridableTool");
|
|
539
|
+
|
|
540
|
+
// Should return to original
|
|
541
|
+
const restoredFunc = toolsService.getFunction("overridableTool");
|
|
542
|
+
expect(await restoredFunc({})).toBe("original");
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it("should preserve original functions when override is registered", async () => {
|
|
546
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
547
|
+
const overrideFunction = jest.fn().mockResolvedValue("override");
|
|
548
|
+
|
|
549
|
+
toolsService.setFunction("overridableTool", originalFunction);
|
|
550
|
+
toolsService.registerOverride("overridableTool", overrideFunction);
|
|
551
|
+
|
|
552
|
+
const originalStored =
|
|
553
|
+
toolsService.getOriginalFunction("overridableTool");
|
|
554
|
+
// Original function should still be the same function (not bound)
|
|
555
|
+
expect(await originalStored!({})).toBe("original");
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
describe("Tool Wrapper System", () => {
|
|
560
|
+
const mockTool: Tool = {
|
|
561
|
+
type: "function",
|
|
562
|
+
function: {
|
|
563
|
+
name: "wrappableTool",
|
|
564
|
+
description: "A tool that can be wrapped",
|
|
565
|
+
parameters: {
|
|
566
|
+
type: "object",
|
|
567
|
+
properties: {
|
|
568
|
+
input: { type: "string" },
|
|
569
|
+
},
|
|
570
|
+
required: ["input"],
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
beforeEach(() => {
|
|
576
|
+
toolsService.addTool(mockTool);
|
|
577
|
+
jest.clearAllMocks();
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it("should register wrapper with string pattern", () => {
|
|
581
|
+
const wrapperFunction = jest
|
|
582
|
+
.fn()
|
|
583
|
+
.mockImplementation((originalFn, args) => {
|
|
584
|
+
return originalFn(args);
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
toolsService.registerWrapper("wrappableTool", wrapperFunction);
|
|
588
|
+
|
|
589
|
+
expect(() => toolsService.getFunction("wrappableTool")).not.toThrow();
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it("should register wrapper with regex pattern", () => {
|
|
593
|
+
const wrapperFunction = jest
|
|
594
|
+
.fn()
|
|
595
|
+
.mockImplementation((originalFn, args) => {
|
|
596
|
+
return originalFn(args);
|
|
597
|
+
});
|
|
598
|
+
const pattern = /^wrappable/;
|
|
599
|
+
|
|
600
|
+
toolsService.registerWrapper(pattern, wrapperFunction);
|
|
601
|
+
|
|
602
|
+
expect(() => toolsService.getFunction("wrappableTool")).not.toThrow();
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it("should execute wrapper function with original function", async () => {
|
|
606
|
+
const originalFunction = jest.fn().mockResolvedValue("original result");
|
|
607
|
+
const wrapperFunction = jest
|
|
608
|
+
.fn()
|
|
609
|
+
.mockImplementation(async (originalFn, args) => {
|
|
610
|
+
const result = await originalFn(args);
|
|
611
|
+
return `wrapped: ${result}`;
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
toolsService.setFunction("wrappableTool", originalFunction);
|
|
615
|
+
toolsService.registerWrapper("wrappableTool", wrapperFunction);
|
|
616
|
+
|
|
617
|
+
const toolCall: ToolCall = {
|
|
618
|
+
id: "call_wrapper",
|
|
619
|
+
type: "function",
|
|
620
|
+
function: {
|
|
621
|
+
name: "wrappableTool",
|
|
622
|
+
arguments: JSON.stringify({ input: "test" }),
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
const result = await toolsService.callTool(toolCall);
|
|
627
|
+
|
|
628
|
+
expect(wrapperFunction).toHaveBeenCalled();
|
|
629
|
+
expect(originalFunction).toHaveBeenCalledWith({ input: "test" });
|
|
630
|
+
expect(result.functionResp).toBe("wrapped: original result");
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it("should handle wrapper priority ordering", () => {
|
|
634
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
635
|
+
const wrapper1 = jest.fn().mockImplementation((fn, args) => fn(args));
|
|
636
|
+
const wrapper2 = jest.fn().mockImplementation((fn, args) => fn(args));
|
|
637
|
+
|
|
638
|
+
toolsService.setFunction("wrappableTool", originalFunction);
|
|
639
|
+
toolsService.registerWrapper("wrappableTool", wrapper1, 1);
|
|
640
|
+
toolsService.registerWrapper("wrappableTool", wrapper2, 2);
|
|
641
|
+
|
|
642
|
+
// Should use higher priority wrapper
|
|
643
|
+
const func = toolsService.getFunction("wrappableTool");
|
|
644
|
+
expect(func).not.toBe(originalFunction);
|
|
645
|
+
expect(func).not.toBe(wrapper1);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it("should support multiple wrapper chaining", async () => {
|
|
649
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
650
|
+
|
|
651
|
+
const wrapper1 = jest
|
|
652
|
+
.fn()
|
|
653
|
+
.mockImplementation(async (originalFn, args) => {
|
|
654
|
+
const result = await originalFn(args);
|
|
655
|
+
return `wrapper1(${result})`;
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
const wrapper2 = jest
|
|
659
|
+
.fn()
|
|
660
|
+
.mockImplementation(async (originalFn, args) => {
|
|
661
|
+
const result = await originalFn(args);
|
|
662
|
+
return `wrapper2(${result})`;
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
toolsService.setFunction("wrappableTool", originalFunction);
|
|
666
|
+
toolsService.registerWrapper("wrappableTool", wrapper1, 1);
|
|
667
|
+
toolsService.registerWrapper("wrappableTool", wrapper2, 2);
|
|
668
|
+
|
|
669
|
+
const toolCall: ToolCall = {
|
|
670
|
+
id: "call_chained",
|
|
671
|
+
type: "function",
|
|
672
|
+
function: {
|
|
673
|
+
name: "wrappableTool",
|
|
674
|
+
arguments: JSON.stringify({ input: "test" }),
|
|
675
|
+
},
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
const result = await toolsService.callTool(toolCall);
|
|
679
|
+
|
|
680
|
+
expect(result.functionResp).toContain("wrapper1");
|
|
681
|
+
expect(result.functionResp).toContain("wrapper2");
|
|
682
|
+
expect(originalFunction).toHaveBeenCalled();
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
it("should remove wrappers", async () => {
|
|
686
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
687
|
+
const wrapperFunction = jest
|
|
688
|
+
.fn()
|
|
689
|
+
.mockImplementation((fn, args) => fn(args));
|
|
690
|
+
|
|
691
|
+
toolsService.setFunction("wrappableTool", originalFunction);
|
|
692
|
+
toolsService.registerWrapper("wrappableTool", wrapperFunction);
|
|
693
|
+
|
|
694
|
+
// Wrapper should be active
|
|
695
|
+
expect(toolsService.getFunction("wrappableTool")).not.toBe(
|
|
696
|
+
originalFunction
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
toolsService.removeWrapper("wrappableTool");
|
|
700
|
+
|
|
701
|
+
// Should return to original
|
|
702
|
+
const restoredFunc = toolsService.getFunction("wrappableTool");
|
|
703
|
+
expect(await restoredFunc!({})).toBe("original");
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
describe("Pattern Matching", () => {
|
|
707
|
+
it("should match glob patterns with wildcards", async () => {
|
|
708
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
709
|
+
const overrideFunction = jest.fn().mockResolvedValue("override");
|
|
710
|
+
|
|
711
|
+
toolsService.setFunction("testTool", originalFunction);
|
|
712
|
+
toolsService.setFunction("testHelper", originalFunction);
|
|
713
|
+
toolsService.setFunction("otherTool", originalFunction);
|
|
714
|
+
|
|
715
|
+
// Register override with glob pattern
|
|
716
|
+
toolsService.registerOverride("test*", overrideFunction);
|
|
717
|
+
|
|
718
|
+
// Should match tools starting with "test"
|
|
719
|
+
const testFunc = toolsService.getFunction("testTool");
|
|
720
|
+
const testHelperFunc = toolsService.getFunction("testHelper");
|
|
721
|
+
const otherFunc = toolsService.getFunction("otherTool");
|
|
722
|
+
expect(await testFunc!({})).toBe("override");
|
|
723
|
+
expect(await testHelperFunc!({})).toBe("override");
|
|
724
|
+
expect(await otherFunc!({})).toBe("original");
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it("should match regex patterns", async () => {
|
|
728
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
729
|
+
const overrideFunction = jest.fn().mockResolvedValue("override");
|
|
730
|
+
|
|
731
|
+
toolsService.setFunction("tool123", originalFunction);
|
|
732
|
+
toolsService.setFunction("tool456", originalFunction);
|
|
733
|
+
toolsService.setFunction("toolABC", originalFunction);
|
|
734
|
+
|
|
735
|
+
// Register override with regex pattern for tools ending with numbers
|
|
736
|
+
toolsService.registerOverride(/tool\d+$/, overrideFunction);
|
|
737
|
+
|
|
738
|
+
const tool123Func = toolsService.getFunction("tool123");
|
|
739
|
+
const tool456Func = toolsService.getFunction("tool456");
|
|
740
|
+
const toolABCFunc = toolsService.getFunction("toolABC");
|
|
741
|
+
expect(await tool123Func!({})).toBe("override");
|
|
742
|
+
expect(await tool456Func!({})).toBe("override");
|
|
743
|
+
expect(await toolABCFunc!({})).toBe("original");
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
it("should handle complex glob patterns", async () => {
|
|
747
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
748
|
+
const overrideFunction = jest.fn().mockResolvedValue("override");
|
|
749
|
+
|
|
750
|
+
toolsService.setFunction("api_get_user", originalFunction);
|
|
751
|
+
toolsService.setFunction("api_post_user", originalFunction);
|
|
752
|
+
toolsService.setFunction("api_delete_user", originalFunction);
|
|
753
|
+
toolsService.setFunction("util_format", originalFunction);
|
|
754
|
+
|
|
755
|
+
// Match API tools with user operations
|
|
756
|
+
toolsService.registerOverride("api_*_user", overrideFunction);
|
|
757
|
+
|
|
758
|
+
const apiGetFunc = toolsService.getFunction("api_get_user");
|
|
759
|
+
const apiPostFunc = toolsService.getFunction("api_post_user");
|
|
760
|
+
const apiDeleteFunc = toolsService.getFunction("api_delete_user");
|
|
761
|
+
|
|
762
|
+
expect(await apiGetFunc!({})).toBe("override");
|
|
763
|
+
expect(await apiPostFunc!({})).toBe("override");
|
|
764
|
+
expect(await apiDeleteFunc!({})).toBe("override");
|
|
765
|
+
const utilFormatFunc = toolsService.getFunction("util_format");
|
|
766
|
+
expect(await utilFormatFunc!({})).toBe("original");
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
it("should test pattern matcher factory function directly", () => {
|
|
770
|
+
// Test string pattern matcher
|
|
771
|
+
const stringMatcher = createPatternMatcher("test*");
|
|
772
|
+
expect(stringMatcher.matches("testTool")).toBe(true);
|
|
773
|
+
expect(stringMatcher.matches("otherTool")).toBe(false);
|
|
774
|
+
|
|
775
|
+
// Test regex pattern matcher
|
|
776
|
+
const regexMatcher = createPatternMatcher(/^api_/);
|
|
777
|
+
expect(regexMatcher.matches("api_call")).toBe(true);
|
|
778
|
+
expect(regexMatcher.matches("util_call")).toBe(false);
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
describe("Error Handling and Edge Cases", () => {
|
|
783
|
+
it("should handle malformed tool call arguments", async () => {
|
|
784
|
+
const mockTool: Tool = {
|
|
785
|
+
type: "function",
|
|
786
|
+
function: {
|
|
787
|
+
name: "testTool",
|
|
788
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
789
|
+
},
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
const malformedCall: ToolCall = {
|
|
793
|
+
id: "call_malformed",
|
|
794
|
+
type: "function",
|
|
795
|
+
function: {
|
|
796
|
+
name: "testTool",
|
|
797
|
+
arguments: "invalid json {",
|
|
798
|
+
},
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
toolsService.addTool(mockTool);
|
|
802
|
+
toolsService.setFunction(
|
|
803
|
+
"testTool",
|
|
804
|
+
jest.fn().mockResolvedValue("result")
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
const result = await toolsService.callTool(malformedCall);
|
|
808
|
+
|
|
809
|
+
expect(result.toolMessages[0].name).toBe("error");
|
|
810
|
+
expect(result.toolMessages[0].content).toContain("JSON");
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
it("should handle tools with no parameters", async () => {
|
|
814
|
+
const noParamTool: Tool = {
|
|
815
|
+
type: "function",
|
|
816
|
+
function: {
|
|
817
|
+
name: "noParamTool",
|
|
818
|
+
description: "Tool with no parameters",
|
|
819
|
+
parameters: {
|
|
820
|
+
type: "object",
|
|
821
|
+
properties: {},
|
|
822
|
+
required: [],
|
|
823
|
+
},
|
|
824
|
+
},
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
const noParamCall: ToolCall = {
|
|
828
|
+
id: "call_no_param",
|
|
829
|
+
type: "function",
|
|
830
|
+
function: {
|
|
831
|
+
name: "noParamTool",
|
|
832
|
+
arguments: "{}",
|
|
833
|
+
},
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const mockFunction = jest.fn().mockResolvedValue("no param result");
|
|
837
|
+
|
|
838
|
+
toolsService.addTool(noParamTool);
|
|
839
|
+
toolsService.setFunction("noParamTool", mockFunction);
|
|
840
|
+
|
|
841
|
+
const result = await toolsService.callTool(noParamCall);
|
|
842
|
+
|
|
843
|
+
expect(mockFunction).toHaveBeenCalledWith({});
|
|
844
|
+
expect(result.functionResp).toBe("no param result");
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
it("should handle tools with complex nested parameter structures", async () => {
|
|
848
|
+
const complexTool: Tool = {
|
|
849
|
+
type: "function",
|
|
850
|
+
function: {
|
|
851
|
+
name: "complexTool",
|
|
852
|
+
parameters: {
|
|
853
|
+
type: "object",
|
|
854
|
+
properties: {
|
|
855
|
+
config: {
|
|
856
|
+
type: "object",
|
|
857
|
+
properties: {
|
|
858
|
+
settings: {
|
|
859
|
+
type: "array",
|
|
860
|
+
items: {
|
|
861
|
+
type: "object",
|
|
862
|
+
properties: {
|
|
863
|
+
key: { type: "string" },
|
|
864
|
+
value: { type: "number" },
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
},
|
|
868
|
+
},
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
required: ["config"],
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const complexCall: ToolCall = {
|
|
877
|
+
id: "call_complex",
|
|
878
|
+
type: "function",
|
|
879
|
+
function: {
|
|
880
|
+
name: "complexTool",
|
|
881
|
+
arguments: JSON.stringify({
|
|
882
|
+
config: {
|
|
883
|
+
settings: [
|
|
884
|
+
{ key: "timeout", value: 5000 },
|
|
885
|
+
{ key: "retries", value: 3 },
|
|
886
|
+
],
|
|
887
|
+
},
|
|
888
|
+
}),
|
|
889
|
+
},
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
const mockFunction = jest.fn().mockResolvedValue("complex result");
|
|
893
|
+
|
|
894
|
+
toolsService.addTool(complexTool);
|
|
895
|
+
toolsService.setFunction("complexTool", mockFunction);
|
|
896
|
+
|
|
897
|
+
const result = await toolsService.callTool(complexCall);
|
|
898
|
+
|
|
899
|
+
expect(mockFunction).toHaveBeenCalledWith({
|
|
900
|
+
config: {
|
|
901
|
+
settings: [
|
|
902
|
+
{ key: "timeout", value: 5000 },
|
|
903
|
+
{ key: "retries", value: 3 },
|
|
904
|
+
],
|
|
905
|
+
},
|
|
906
|
+
});
|
|
907
|
+
expect(result.functionResp).toBe("complex result");
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
it("should handle concurrent tool calls", async () => {
|
|
911
|
+
const concurrentTool: Tool = {
|
|
912
|
+
type: "function",
|
|
913
|
+
function: {
|
|
914
|
+
name: "concurrentTool",
|
|
915
|
+
parameters: {
|
|
916
|
+
type: "object",
|
|
917
|
+
properties: {
|
|
918
|
+
delay: { type: "number" },
|
|
919
|
+
},
|
|
920
|
+
required: ["delay"],
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
const mockFunction = jest.fn().mockImplementation(async ({ delay }) => {
|
|
926
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
927
|
+
return `completed after ${delay}ms`;
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
toolsService.addTool(concurrentTool);
|
|
931
|
+
toolsService.setFunction("concurrentTool", mockFunction);
|
|
932
|
+
|
|
933
|
+
const call1: ToolCall = {
|
|
934
|
+
id: "call_1",
|
|
935
|
+
type: "function",
|
|
936
|
+
function: {
|
|
937
|
+
name: "concurrentTool",
|
|
938
|
+
arguments: JSON.stringify({ delay: 100 }),
|
|
939
|
+
},
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
const call2: ToolCall = {
|
|
943
|
+
id: "call_2",
|
|
944
|
+
type: "function",
|
|
945
|
+
function: {
|
|
946
|
+
name: "concurrentTool",
|
|
947
|
+
arguments: JSON.stringify({ delay: 50 }),
|
|
948
|
+
},
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
const [result1, result2] = await Promise.all([
|
|
952
|
+
toolsService.callTool(call1),
|
|
953
|
+
toolsService.callTool(call2),
|
|
954
|
+
]);
|
|
955
|
+
|
|
956
|
+
expect(result1.functionResp).toBe("completed after 100ms");
|
|
957
|
+
expect(result2.functionResp).toBe("completed after 50ms");
|
|
958
|
+
expect(mockFunction).toHaveBeenCalledTimes(2);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
it("should handle undefined and null function responses", async () => {
|
|
962
|
+
// Test undefined response with separate tool
|
|
963
|
+
const undefinedTool: Tool = {
|
|
964
|
+
type: "function",
|
|
965
|
+
function: {
|
|
966
|
+
name: "undefinedTool",
|
|
967
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
968
|
+
},
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
const undefinedCall: ToolCall = {
|
|
972
|
+
id: "call_undefined",
|
|
973
|
+
type: "function",
|
|
974
|
+
function: {
|
|
975
|
+
name: "undefinedTool",
|
|
976
|
+
arguments: "{}",
|
|
977
|
+
},
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
toolsService.addTool(undefinedTool);
|
|
981
|
+
toolsService.setFunction(
|
|
982
|
+
"undefinedTool",
|
|
983
|
+
jest.fn().mockResolvedValue(undefined)
|
|
984
|
+
);
|
|
985
|
+
let result = await toolsService.callTool(undefinedCall);
|
|
986
|
+
expect(result.toolMessages[0].content).toBe("undefined");
|
|
987
|
+
|
|
988
|
+
// Test null response with separate tool
|
|
989
|
+
const nullTool: Tool = {
|
|
990
|
+
type: "function",
|
|
991
|
+
function: {
|
|
992
|
+
name: "nullTool",
|
|
993
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
994
|
+
},
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const nullCall: ToolCall = {
|
|
998
|
+
id: "call_null",
|
|
999
|
+
type: "function",
|
|
1000
|
+
function: {
|
|
1001
|
+
name: "nullTool",
|
|
1002
|
+
arguments: "{}",
|
|
1003
|
+
},
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
toolsService.addTool(nullTool);
|
|
1007
|
+
toolsService.setFunction("nullTool", jest.fn().mockResolvedValue(null));
|
|
1008
|
+
result = await toolsService.callTool(nullCall);
|
|
1009
|
+
expect(result.toolMessages[0].content).toBe("null");
|
|
1010
|
+
});
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
describe("Integration Tests", () => {
|
|
1014
|
+
it("should handle complete workflow with override and wrapper", async () => {
|
|
1015
|
+
const baseTool: Tool = {
|
|
1016
|
+
type: "function",
|
|
1017
|
+
function: {
|
|
1018
|
+
name: "workflowTool",
|
|
1019
|
+
parameters: {
|
|
1020
|
+
type: "object",
|
|
1021
|
+
properties: {
|
|
1022
|
+
input: { type: "string" },
|
|
1023
|
+
},
|
|
1024
|
+
required: ["input"],
|
|
1025
|
+
},
|
|
1026
|
+
},
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
const originalFunction = jest.fn().mockResolvedValue("original");
|
|
1030
|
+
const wrapperFunction = jest
|
|
1031
|
+
.fn()
|
|
1032
|
+
.mockImplementation(async (originalFn, args) => {
|
|
1033
|
+
const result = await originalFn(args);
|
|
1034
|
+
return `wrapped: ${result}`;
|
|
1035
|
+
});
|
|
1036
|
+
const overrideFunction = jest.fn().mockResolvedValue("override");
|
|
1037
|
+
|
|
1038
|
+
toolsService.addTool(baseTool);
|
|
1039
|
+
toolsService.setFunction("workflowTool", originalFunction);
|
|
1040
|
+
toolsService.registerWrapper("workflowTool", wrapperFunction);
|
|
1041
|
+
toolsService.registerOverride("workflowTool", overrideFunction);
|
|
1042
|
+
|
|
1043
|
+
const toolCall: ToolCall = {
|
|
1044
|
+
id: "call_workflow",
|
|
1045
|
+
type: "function",
|
|
1046
|
+
function: {
|
|
1047
|
+
name: "workflowTool",
|
|
1048
|
+
arguments: JSON.stringify({ input: "test" }),
|
|
1049
|
+
},
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
const result = await toolsService.callTool(toolCall);
|
|
1053
|
+
|
|
1054
|
+
// Override should take precedence over wrapper
|
|
1055
|
+
expect(overrideFunction).toHaveBeenCalled();
|
|
1056
|
+
expect(wrapperFunction).not.toHaveBeenCalled();
|
|
1057
|
+
expect(originalFunction).not.toHaveBeenCalled();
|
|
1058
|
+
expect(result.functionResp).toBe("override");
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
it("should maintain tool state across multiple operations", () => {
|
|
1062
|
+
const tools: Tool[] = [
|
|
1063
|
+
{
|
|
1064
|
+
type: "function",
|
|
1065
|
+
function: {
|
|
1066
|
+
name: "tool1",
|
|
1067
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
1068
|
+
},
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
type: "function",
|
|
1072
|
+
function: {
|
|
1073
|
+
name: "tool2",
|
|
1074
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
1075
|
+
},
|
|
1076
|
+
},
|
|
1077
|
+
];
|
|
1078
|
+
|
|
1079
|
+
toolsService.addTools(tools);
|
|
1080
|
+
expect(toolsService.getToolsByNames(["tool1", "tool2"])).toHaveLength(2);
|
|
1081
|
+
|
|
1082
|
+
toolsService.setFunction("tool1", jest.fn());
|
|
1083
|
+
toolsService.setFunction("tool2", jest.fn());
|
|
1084
|
+
|
|
1085
|
+
expect(toolsService.getFunction("tool1")).toBeDefined();
|
|
1086
|
+
expect(toolsService.getFunction("tool2")).toBeDefined();
|
|
1087
|
+
|
|
1088
|
+
// Copy to new service
|
|
1089
|
+
const newService = new ToolsService();
|
|
1090
|
+
newService.copyToolsFrom(["tool1", "tool2"], toolsService);
|
|
1091
|
+
|
|
1092
|
+
expect(newService.getToolsByNames(["tool1", "tool2"])).toHaveLength(2);
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
describe("Function Binding", () => {
|
|
1096
|
+
let toolsService: ToolsService;
|
|
1097
|
+
let mockContext: ToolContext;
|
|
1098
|
+
|
|
1099
|
+
beforeEach(() => {
|
|
1100
|
+
mockContext = {
|
|
1101
|
+
metadata: { testKey: "testValue" },
|
|
1102
|
+
};
|
|
1103
|
+
toolsService = new ToolsService(mockContext);
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
describe("this binding in tool functions", () => {
|
|
1107
|
+
it("should bind functions so 'this' refers to ToolsService instance", async () => {
|
|
1108
|
+
const testTool: Tool = {
|
|
1109
|
+
type: "function",
|
|
1110
|
+
function: {
|
|
1111
|
+
name: "testThisBinding",
|
|
1112
|
+
description: "Test function that accesses this",
|
|
1113
|
+
parameters: {
|
|
1114
|
+
type: "object",
|
|
1115
|
+
properties: {
|
|
1116
|
+
message: { type: "string" },
|
|
1117
|
+
},
|
|
1118
|
+
},
|
|
1119
|
+
},
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
function testFunction(this: ToolsService, args: { message: string }) {
|
|
1123
|
+
// This function should have 'this' bound to the ToolsService instance
|
|
1124
|
+
if (!this || typeof this.getContext !== "function") {
|
|
1125
|
+
throw new Error("'this' is not bound to ToolsService instance");
|
|
1126
|
+
}
|
|
1127
|
+
const context = this.getContext();
|
|
1128
|
+
return {
|
|
1129
|
+
message: args.message,
|
|
1130
|
+
contextMetadata: context.metadata,
|
|
1131
|
+
hasThisBinding: true,
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
toolsService.addTool(testTool);
|
|
1136
|
+
toolsService.setFunction("testThisBinding", testFunction);
|
|
1137
|
+
|
|
1138
|
+
const toolCall: ToolCall = {
|
|
1139
|
+
id: "test-call-1",
|
|
1140
|
+
type: "function",
|
|
1141
|
+
function: {
|
|
1142
|
+
name: "testThisBinding",
|
|
1143
|
+
arguments: JSON.stringify({ message: "test" }),
|
|
1144
|
+
},
|
|
1145
|
+
};
|
|
1146
|
+
|
|
1147
|
+
const result = await toolsService.callTool(toolCall);
|
|
1148
|
+
|
|
1149
|
+
expect(result.functionResp.hasThisBinding).toBe(true);
|
|
1150
|
+
expect(result.functionResp.contextMetadata).toEqual({
|
|
1151
|
+
testKey: "testValue",
|
|
1152
|
+
});
|
|
1153
|
+
expect(result.functionResp.message).toBe("test");
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
it("should allow tool functions to call other tools via this.callTool", async () => {
|
|
1157
|
+
const helperTool: Tool = {
|
|
1158
|
+
type: "function",
|
|
1159
|
+
function: {
|
|
1160
|
+
name: "helperTool",
|
|
1161
|
+
description: "Helper tool",
|
|
1162
|
+
parameters: {
|
|
1163
|
+
type: "object",
|
|
1164
|
+
properties: {
|
|
1165
|
+
value: { type: "string" },
|
|
1166
|
+
},
|
|
1167
|
+
},
|
|
1168
|
+
},
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
const mainTool: Tool = {
|
|
1172
|
+
type: "function",
|
|
1173
|
+
function: {
|
|
1174
|
+
name: "mainTool",
|
|
1175
|
+
description: "Main tool that calls helper",
|
|
1176
|
+
parameters: {
|
|
1177
|
+
type: "object",
|
|
1178
|
+
properties: {
|
|
1179
|
+
input: { type: "string" },
|
|
1180
|
+
},
|
|
1181
|
+
},
|
|
1182
|
+
},
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
function helperFunction(args: { value: string }) {
|
|
1186
|
+
return `Helper processed: ${args.value}`;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
async function mainFunction(
|
|
1190
|
+
this: ToolsService,
|
|
1191
|
+
args: { input: string }
|
|
1192
|
+
) {
|
|
1193
|
+
if (!this || typeof this.callTool !== "function") {
|
|
1194
|
+
throw new Error("'this' is not bound to ToolsService instance");
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const helperCall: ToolCall = {
|
|
1198
|
+
id: "helper-call",
|
|
1199
|
+
type: "function",
|
|
1200
|
+
function: {
|
|
1201
|
+
name: "helperTool",
|
|
1202
|
+
arguments: JSON.stringify({ value: args.input }),
|
|
1203
|
+
},
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
const helperResult = await this.callTool(helperCall);
|
|
1207
|
+
return `Main tool result: ${helperResult.functionResp}`;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
toolsService.addTool(helperTool);
|
|
1211
|
+
toolsService.addTool(mainTool);
|
|
1212
|
+
toolsService.setFunction("helperTool", helperFunction);
|
|
1213
|
+
toolsService.setFunction("mainTool", mainFunction);
|
|
1214
|
+
|
|
1215
|
+
const toolCall: ToolCall = {
|
|
1216
|
+
id: "test-call-2",
|
|
1217
|
+
type: "function",
|
|
1218
|
+
function: {
|
|
1219
|
+
name: "mainTool",
|
|
1220
|
+
arguments: JSON.stringify({ input: "test data" }),
|
|
1221
|
+
},
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
const result = await toolsService.callTool(toolCall);
|
|
1225
|
+
|
|
1226
|
+
expect(result.functionResp).toBe(
|
|
1227
|
+
"Main tool result: Helper processed: test data"
|
|
1228
|
+
);
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
it("should maintain binding through overrides", async () => {
|
|
1232
|
+
const testTool: Tool = {
|
|
1233
|
+
type: "function",
|
|
1234
|
+
function: {
|
|
1235
|
+
name: "bindingTestTool",
|
|
1236
|
+
description: "Test tool for binding with overrides",
|
|
1237
|
+
parameters: {
|
|
1238
|
+
type: "object",
|
|
1239
|
+
properties: {
|
|
1240
|
+
data: { type: "string" },
|
|
1241
|
+
},
|
|
1242
|
+
},
|
|
1243
|
+
},
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
function originalFunction(
|
|
1247
|
+
this: ToolsService,
|
|
1248
|
+
args: { data: string }
|
|
1249
|
+
) {
|
|
1250
|
+
if (!this || typeof this.getContext !== "function") {
|
|
1251
|
+
throw new Error(
|
|
1252
|
+
"'this' is not bound to ToolsService instance in original"
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
return `Original: ${args.data}`;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
const overrideFunction = function (
|
|
1259
|
+
this: ToolsService,
|
|
1260
|
+
originalArgs: any[],
|
|
1261
|
+
originalTool: Tool
|
|
1262
|
+
) {
|
|
1263
|
+
if (!this || typeof this.getContext !== "function") {
|
|
1264
|
+
throw new Error(
|
|
1265
|
+
"'this' is not bound to ToolsService instance in override"
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
return `Override: ${originalArgs[0].data}`;
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
toolsService.addTool(testTool);
|
|
1272
|
+
toolsService.setFunction("bindingTestTool", originalFunction);
|
|
1273
|
+
toolsService.registerOverride("bindingTestTool", overrideFunction);
|
|
1274
|
+
|
|
1275
|
+
const toolCall: ToolCall = {
|
|
1276
|
+
id: "test-call-3",
|
|
1277
|
+
type: "function",
|
|
1278
|
+
function: {
|
|
1279
|
+
name: "bindingTestTool",
|
|
1280
|
+
arguments: JSON.stringify({ data: "test" }),
|
|
1281
|
+
},
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
const result = await toolsService.callTool(toolCall);
|
|
1285
|
+
|
|
1286
|
+
expect(result.functionResp).toBe("Override: test");
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
it("should maintain binding through wrappers", async () => {
|
|
1290
|
+
const testTool: Tool = {
|
|
1291
|
+
type: "function",
|
|
1292
|
+
function: {
|
|
1293
|
+
name: "wrapperBindingTest",
|
|
1294
|
+
description: "Test tool for binding with wrappers",
|
|
1295
|
+
parameters: {
|
|
1296
|
+
type: "object",
|
|
1297
|
+
properties: {
|
|
1298
|
+
value: { type: "string" },
|
|
1299
|
+
},
|
|
1300
|
+
},
|
|
1301
|
+
},
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1304
|
+
function baseFunction(this: ToolsService, args: { value: string }) {
|
|
1305
|
+
if (!this || typeof this.getContext !== "function") {
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
"'this' is not bound to ToolsService instance in base function"
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
return `Base: ${args.value}`;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
const wrapper = (originalFunc: Function, args: any, tool: Tool) => {
|
|
1314
|
+
// The wrapper should preserve the binding
|
|
1315
|
+
const result = originalFunc.call(toolsService, args);
|
|
1316
|
+
return `Wrapped: ${result}`;
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
toolsService.addTool(testTool);
|
|
1320
|
+
toolsService.setFunction("wrapperBindingTest", baseFunction);
|
|
1321
|
+
toolsService.registerWrapper("wrapperBindingTest", wrapper);
|
|
1322
|
+
|
|
1323
|
+
const toolCall: ToolCall = {
|
|
1324
|
+
id: "test-call-4",
|
|
1325
|
+
type: "function",
|
|
1326
|
+
function: {
|
|
1327
|
+
name: "wrapperBindingTest",
|
|
1328
|
+
arguments: JSON.stringify({ value: "test" }),
|
|
1329
|
+
},
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
const result = await toolsService.callTool(toolCall);
|
|
1333
|
+
|
|
1334
|
+
expect(result.functionResp).toBe("Wrapped: Base: test");
|
|
1335
|
+
});
|
|
1336
|
+
});
|
|
1337
|
+
});
|
|
1338
|
+
});
|
|
1339
|
+
});
|