@aj-archipelago/cortex 1.3.62 → 1.3.63
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/.github/workflows/cortex-file-handler-test.yml +61 -0
- package/README.md +31 -7
- package/config/default.example.json +15 -0
- package/config.js +133 -12
- package/helper-apps/cortex-autogen2/DigiCertGlobalRootCA.crt.pem +22 -0
- package/helper-apps/cortex-autogen2/Dockerfile +31 -0
- package/helper-apps/cortex-autogen2/Dockerfile.worker +41 -0
- package/helper-apps/cortex-autogen2/README.md +183 -0
- package/helper-apps/cortex-autogen2/__init__.py +1 -0
- package/helper-apps/cortex-autogen2/agents.py +131 -0
- package/helper-apps/cortex-autogen2/docker-compose.yml +20 -0
- package/helper-apps/cortex-autogen2/function_app.py +55 -0
- package/helper-apps/cortex-autogen2/host.json +15 -0
- package/helper-apps/cortex-autogen2/main.py +126 -0
- package/helper-apps/cortex-autogen2/poetry.lock +3652 -0
- package/helper-apps/cortex-autogen2/pyproject.toml +36 -0
- package/helper-apps/cortex-autogen2/requirements.txt +20 -0
- package/helper-apps/cortex-autogen2/send_task.py +105 -0
- package/helper-apps/cortex-autogen2/services/__init__.py +1 -0
- package/helper-apps/cortex-autogen2/services/azure_queue.py +85 -0
- package/helper-apps/cortex-autogen2/services/redis_publisher.py +153 -0
- package/helper-apps/cortex-autogen2/task_processor.py +488 -0
- package/helper-apps/cortex-autogen2/tools/__init__.py +24 -0
- package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +175 -0
- package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +601 -0
- package/helper-apps/cortex-autogen2/tools/coding_tools.py +72 -0
- package/helper-apps/cortex-autogen2/tools/download_tools.py +48 -0
- package/helper-apps/cortex-autogen2/tools/file_tools.py +545 -0
- package/helper-apps/cortex-autogen2/tools/search_tools.py +646 -0
- package/helper-apps/cortex-azure-cleaner/README.md +36 -0
- package/helper-apps/cortex-file-converter/README.md +93 -0
- package/helper-apps/cortex-file-converter/key_to_pdf.py +104 -0
- package/helper-apps/cortex-file-converter/list_blob_extensions.py +89 -0
- package/helper-apps/cortex-file-converter/process_azure_keynotes.py +181 -0
- package/helper-apps/cortex-file-converter/requirements.txt +1 -0
- package/helper-apps/cortex-file-handler/.env.test.azure.ci +7 -0
- package/helper-apps/cortex-file-handler/.env.test.azure.sample +1 -1
- package/helper-apps/cortex-file-handler/.env.test.gcs.ci +10 -0
- package/helper-apps/cortex-file-handler/.env.test.gcs.sample +2 -2
- package/helper-apps/cortex-file-handler/INTERFACE.md +41 -0
- package/helper-apps/cortex-file-handler/package.json +1 -1
- package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +41 -17
- package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +30 -15
- package/helper-apps/cortex-file-handler/scripts/test-azure.sh +32 -6
- package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +24 -2
- package/helper-apps/cortex-file-handler/scripts/validate-env.js +128 -0
- package/helper-apps/cortex-file-handler/src/blobHandler.js +161 -51
- package/helper-apps/cortex-file-handler/src/constants.js +3 -0
- package/helper-apps/cortex-file-handler/src/fileChunker.js +10 -8
- package/helper-apps/cortex-file-handler/src/index.js +116 -9
- package/helper-apps/cortex-file-handler/src/redis.js +61 -1
- package/helper-apps/cortex-file-handler/src/services/ConversionService.js +11 -8
- package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +2 -2
- package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +88 -6
- package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +58 -0
- package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +25 -5
- package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +9 -0
- package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +120 -16
- package/helper-apps/cortex-file-handler/src/start.js +27 -17
- package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +52 -1
- package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +40 -0
- package/helper-apps/cortex-file-handler/tests/checkHashShortLived.test.js +553 -0
- package/helper-apps/cortex-file-handler/tests/cleanup.test.js +46 -52
- package/helper-apps/cortex-file-handler/tests/containerConversionFlow.test.js +451 -0
- package/helper-apps/cortex-file-handler/tests/containerNameParsing.test.js +229 -0
- package/helper-apps/cortex-file-handler/tests/containerParameterFlow.test.js +392 -0
- package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +7 -2
- package/helper-apps/cortex-file-handler/tests/deleteOperations.test.js +348 -0
- package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +23 -2
- package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +11 -5
- package/helper-apps/cortex-file-handler/tests/getOperations.test.js +58 -24
- package/helper-apps/cortex-file-handler/tests/postOperations.test.js +11 -4
- package/helper-apps/cortex-file-handler/tests/shortLivedUrlConversion.test.js +225 -0
- package/helper-apps/cortex-file-handler/tests/start.test.js +8 -12
- package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +80 -0
- package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +388 -22
- package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -0
- package/lib/cortexResponse.js +153 -0
- package/lib/entityConstants.js +21 -3
- package/lib/logger.js +21 -4
- package/lib/pathwayTools.js +28 -9
- package/lib/util.js +49 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +1 -0
- package/pathways/bing_afagent.js +54 -1
- package/pathways/call_tools.js +2 -3
- package/pathways/chat_jarvis.js +1 -1
- package/pathways/google_cse.js +27 -0
- package/pathways/grok_live_search.js +18 -0
- package/pathways/system/entity/memory/sys_memory_lookup_required.js +1 -0
- package/pathways/system/entity/memory/sys_memory_required.js +1 -0
- package/pathways/system/entity/memory/sys_search_memory.js +1 -0
- package/pathways/system/entity/sys_entity_agent.js +56 -4
- package/pathways/system/entity/sys_generator_quick.js +1 -0
- package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +26 -0
- package/pathways/system/entity/tools/sys_tool_google_search.js +141 -0
- package/pathways/system/entity/tools/sys_tool_grok_x_search.js +237 -0
- package/pathways/system/entity/tools/sys_tool_image.js +1 -1
- package/pathways/system/rest_streaming/sys_claude_37_sonnet.js +21 -0
- package/pathways/system/rest_streaming/sys_claude_41_opus.js +21 -0
- package/pathways/system/rest_streaming/sys_claude_4_sonnet.js +21 -0
- package/pathways/system/rest_streaming/sys_google_gemini_25_flash.js +25 -0
- package/pathways/system/rest_streaming/{sys_google_gemini_chat.js → sys_google_gemini_25_pro.js} +6 -4
- package/pathways/system/rest_streaming/sys_grok_4.js +23 -0
- package/pathways/system/rest_streaming/sys_grok_4_fast_non_reasoning.js +23 -0
- package/pathways/system/rest_streaming/sys_grok_4_fast_reasoning.js +23 -0
- package/pathways/system/rest_streaming/sys_openai_chat.js +3 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41.js +22 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41_mini.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt41_nano.js +21 -0
- package/pathways/system/rest_streaming/{sys_claude_35_sonnet.js → sys_openai_chat_gpt4_omni.js} +6 -4
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_omni_mini.js +21 -0
- package/pathways/system/rest_streaming/{sys_claude_3_haiku.js → sys_openai_chat_gpt5.js} +7 -5
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_chat.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_mini.js +21 -0
- package/pathways/system/rest_streaming/sys_openai_chat_gpt5_nano.js +21 -0
- package/pathways/system/rest_streaming/{sys_openai_chat_o1.js → sys_openai_chat_o3.js} +6 -3
- package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +3 -0
- package/pathways/system/workspaces/run_workspace_prompt.js +99 -0
- package/pathways/vision.js +1 -1
- package/server/graphql.js +1 -1
- package/server/modelExecutor.js +8 -0
- package/server/pathwayResolver.js +166 -16
- package/server/pathwayResponseParser.js +16 -8
- package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
- package/server/plugins/claude3VertexPlugin.js +193 -45
- package/server/plugins/gemini15ChatPlugin.js +21 -0
- package/server/plugins/gemini15VisionPlugin.js +360 -0
- package/server/plugins/googleCsePlugin.js +94 -0
- package/server/plugins/grokVisionPlugin.js +365 -0
- package/server/plugins/modelPlugin.js +3 -1
- package/server/plugins/openAiChatPlugin.js +106 -13
- package/server/plugins/openAiVisionPlugin.js +42 -30
- package/server/resolver.js +28 -4
- package/server/rest.js +270 -53
- package/server/typeDef.js +1 -0
- package/tests/{mocks.js → helpers/mocks.js} +5 -2
- package/tests/{server.js → helpers/server.js} +2 -2
- package/tests/helpers/sseAssert.js +23 -0
- package/tests/helpers/sseClient.js +73 -0
- package/tests/helpers/subscriptionAssert.js +11 -0
- package/tests/helpers/subscriptions.js +113 -0
- package/tests/{sublong.srt → integration/features/translate/sublong.srt} +4543 -4543
- package/tests/integration/features/translate/translate_chunking_stream.test.js +100 -0
- package/tests/{translate_srt.test.js → integration/features/translate/translate_srt.test.js} +2 -2
- package/tests/integration/graphql/async/stream/agentic.test.js +477 -0
- package/tests/integration/graphql/async/stream/subscription_streaming.test.js +62 -0
- package/tests/integration/graphql/async/stream/sys_entity_start_streaming.test.js +71 -0
- package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +56 -0
- package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +66 -0
- package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +56 -0
- package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +72 -0
- package/tests/integration/graphql/features/google/sysToolGoogleSearch.test.js +96 -0
- package/tests/integration/graphql/features/grok/grok.test.js +688 -0
- package/tests/integration/graphql/features/grok/grok_x_search_tool.test.js +354 -0
- package/tests/{main.test.js → integration/graphql/features/main.test.js} +1 -1
- package/tests/{call_tools.test.js → integration/graphql/features/tools/call_tools.test.js} +2 -2
- package/tests/{vision.test.js → integration/graphql/features/vision/vision.test.js} +1 -1
- package/tests/integration/graphql/subscriptions/connection.test.js +26 -0
- package/tests/{openai_api.test.js → integration/rest/oai/openai_api.test.js} +63 -238
- package/tests/integration/rest/oai/tool_calling_api.test.js +343 -0
- package/tests/integration/rest/oai/tool_calling_streaming.test.js +85 -0
- package/tests/integration/rest/vendors/claude_streaming.test.js +47 -0
- package/tests/integration/rest/vendors/claude_tool_calling_streaming.test.js +75 -0
- package/tests/integration/rest/vendors/gemini_streaming.test.js +47 -0
- package/tests/integration/rest/vendors/gemini_tool_calling_streaming.test.js +75 -0
- package/tests/integration/rest/vendors/grok_streaming.test.js +55 -0
- package/tests/integration/rest/vendors/grok_tool_calling_streaming.test.js +75 -0
- package/tests/{azureAuthTokenHelper.test.js → unit/core/azureAuthTokenHelper.test.js} +1 -1
- package/tests/{chunkfunction.test.js → unit/core/chunkfunction.test.js} +2 -2
- package/tests/{config.test.js → unit/core/config.test.js} +3 -3
- package/tests/{encodeCache.test.js → unit/core/encodeCache.test.js} +1 -1
- package/tests/{fastLruCache.test.js → unit/core/fastLruCache.test.js} +1 -1
- package/tests/{handleBars.test.js → unit/core/handleBars.test.js} +1 -1
- package/tests/{memoryfunction.test.js → unit/core/memoryfunction.test.js} +2 -2
- package/tests/unit/core/mergeResolver.test.js +952 -0
- package/tests/{parser.test.js → unit/core/parser.test.js} +3 -3
- package/tests/unit/core/pathwayResolver.test.js +187 -0
- package/tests/{requestMonitor.test.js → unit/core/requestMonitor.test.js} +1 -1
- package/tests/{requestMonitorDurationEstimator.test.js → unit/core/requestMonitorDurationEstimator.test.js} +1 -1
- package/tests/{truncateMessages.test.js → unit/core/truncateMessages.test.js} +3 -3
- package/tests/{util.test.js → unit/core/util.test.js} +1 -1
- package/tests/{apptekTranslatePlugin.test.js → unit/plugins/apptekTranslatePlugin.test.js} +3 -3
- package/tests/{azureFoundryAgents.test.js → unit/plugins/azureFoundryAgents.test.js} +136 -1
- package/tests/{claude3VertexPlugin.test.js → unit/plugins/claude3VertexPlugin.test.js} +32 -10
- package/tests/{claude3VertexToolConversion.test.js → unit/plugins/claude3VertexToolConversion.test.js} +3 -3
- package/tests/unit/plugins/googleCsePlugin.test.js +111 -0
- package/tests/unit/plugins/grokVisionPlugin.test.js +1392 -0
- package/tests/{modelPlugin.test.js → unit/plugins/modelPlugin.test.js} +3 -3
- package/tests/{multimodal_conversion.test.js → unit/plugins/multimodal_conversion.test.js} +4 -4
- package/tests/{openAiChatPlugin.test.js → unit/plugins/openAiChatPlugin.test.js} +13 -4
- package/tests/{openAiToolPlugin.test.js → unit/plugins/openAiToolPlugin.test.js} +35 -27
- package/tests/{tokenHandlingTests.test.js → unit/plugins/tokenHandlingTests.test.js} +5 -5
- package/tests/{translate_apptek.test.js → unit/plugins/translate_apptek.test.js} +3 -3
- package/tests/{streaming.test.js → unit/plugins.streaming/plugin_stream_events.test.js} +19 -58
- package/helper-apps/mogrt-handler/tests/test-files/test.gif +0 -1
- package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +0 -1
- package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +0 -1
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4.js +0 -19
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_32.js +0 -19
- package/pathways/system/rest_streaming/sys_openai_chat_gpt4_turbo.js +0 -19
- package/pathways/system/workspaces/run_claude35_sonnet.js +0 -21
- package/pathways/system/workspaces/run_claude3_haiku.js +0 -20
- package/pathways/system/workspaces/run_gpt35turbo.js +0 -20
- package/pathways/system/workspaces/run_gpt4.js +0 -20
- package/pathways/system/workspaces/run_gpt4_32.js +0 -20
- package/tests/agentic.test.js +0 -256
- package/tests/pathwayResolver.test.js +0 -78
- package/tests/subscription.test.js +0 -387
- /package/tests/{subchunk.srt → integration/features/translate/subchunk.srt} +0 -0
- /package/tests/{subhorizontal.srt → integration/features/translate/subhorizontal.srt} +0 -0
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
// mergeResolver.test.js
|
|
2
|
+
// Comprehensive tests for mergeResolver and mergeResultData methods
|
|
3
|
+
|
|
4
|
+
import test from 'ava';
|
|
5
|
+
import { PathwayResolver } from '../../../server/pathwayResolver.js';
|
|
6
|
+
import CortexResponse from '../../../lib/cortexResponse.js';
|
|
7
|
+
import { mockConfig, mockPathwayString, mockModelEndpoints } from '../../helpers/mocks.js';
|
|
8
|
+
|
|
9
|
+
const mockPathway = mockPathwayString;
|
|
10
|
+
mockPathway.useInputChunking = false;
|
|
11
|
+
mockPathway.prompt = 'What is AI?';
|
|
12
|
+
|
|
13
|
+
const mockArgs = {
|
|
14
|
+
text: 'Artificial intelligence',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
test.beforeEach((t) => {
|
|
18
|
+
t.context.pathwayResolver = new PathwayResolver({
|
|
19
|
+
config: mockConfig,
|
|
20
|
+
pathway: mockPathway,
|
|
21
|
+
args: mockArgs,
|
|
22
|
+
endpoints: mockModelEndpoints,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// mergeResolver Tests
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
test('mergeResolver merges basic resolver properties correctly', (t) => {
|
|
31
|
+
const resolver1 = t.context.pathwayResolver;
|
|
32
|
+
const resolver2 = new PathwayResolver({
|
|
33
|
+
config: mockConfig,
|
|
34
|
+
pathway: mockPathway,
|
|
35
|
+
args: mockArgs,
|
|
36
|
+
endpoints: mockModelEndpoints,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Set up initial data
|
|
40
|
+
resolver1.previousResult = 'initial result';
|
|
41
|
+
resolver1.warnings = ['warning1'];
|
|
42
|
+
resolver1.errors = ['error1'];
|
|
43
|
+
resolver1.pathwayResultData = { citations: ['cite1'], usage: { tokens: 100 } };
|
|
44
|
+
|
|
45
|
+
resolver2.previousResult = 'new result';
|
|
46
|
+
resolver2.warnings = ['warning2'];
|
|
47
|
+
resolver2.errors = ['error2'];
|
|
48
|
+
resolver2.pathwayResultData = { citations: ['cite2'], usage: { tokens: 200 } };
|
|
49
|
+
|
|
50
|
+
resolver1.mergeResolver(resolver2);
|
|
51
|
+
|
|
52
|
+
t.is(resolver1.previousResult, 'new result'); // Should use other resolver's result
|
|
53
|
+
t.deepEqual(resolver1.warnings, ['warning1', 'warning2']);
|
|
54
|
+
t.deepEqual(resolver1.errors, ['error1', 'error2']);
|
|
55
|
+
t.deepEqual(resolver1.pathwayResultData.citations, ['cite1', 'cite2']);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('mergeResolver handles null/undefined otherResolver gracefully', (t) => {
|
|
59
|
+
const resolver = t.context.pathwayResolver;
|
|
60
|
+
const originalWarnings = ['warning1'];
|
|
61
|
+
const originalErrors = ['error1'];
|
|
62
|
+
const originalResultData = { citations: ['cite1'] };
|
|
63
|
+
|
|
64
|
+
resolver.warnings = originalWarnings;
|
|
65
|
+
resolver.errors = originalErrors;
|
|
66
|
+
resolver.pathwayResultData = originalResultData;
|
|
67
|
+
|
|
68
|
+
// Test with null
|
|
69
|
+
resolver.mergeResolver(null);
|
|
70
|
+
t.deepEqual(resolver.warnings, originalWarnings);
|
|
71
|
+
t.deepEqual(resolver.errors, originalErrors);
|
|
72
|
+
t.deepEqual(resolver.pathwayResultData, originalResultData);
|
|
73
|
+
|
|
74
|
+
// Test with undefined
|
|
75
|
+
resolver.mergeResolver(undefined);
|
|
76
|
+
t.deepEqual(resolver.warnings, originalWarnings);
|
|
77
|
+
t.deepEqual(resolver.errors, originalErrors);
|
|
78
|
+
t.deepEqual(resolver.pathwayResultData, originalResultData);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('mergeResolver preserves original previousResult when other has none', (t) => {
|
|
82
|
+
const resolver1 = t.context.pathwayResolver;
|
|
83
|
+
const resolver2 = new PathwayResolver({
|
|
84
|
+
config: mockConfig,
|
|
85
|
+
pathway: mockPathway,
|
|
86
|
+
args: mockArgs,
|
|
87
|
+
endpoints: mockModelEndpoints,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
resolver1.previousResult = 'original result';
|
|
91
|
+
resolver2.previousResult = null;
|
|
92
|
+
|
|
93
|
+
resolver1.mergeResolver(resolver2);
|
|
94
|
+
t.is(resolver1.previousResult, 'original result');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('mergeResolver uses other previousResult when original is null', (t) => {
|
|
98
|
+
const resolver1 = t.context.pathwayResolver;
|
|
99
|
+
const resolver2 = new PathwayResolver({
|
|
100
|
+
config: mockConfig,
|
|
101
|
+
pathway: mockPathway,
|
|
102
|
+
args: mockArgs,
|
|
103
|
+
endpoints: mockModelEndpoints,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
resolver1.previousResult = null;
|
|
107
|
+
resolver2.previousResult = 'new result';
|
|
108
|
+
|
|
109
|
+
resolver1.mergeResolver(resolver2);
|
|
110
|
+
t.is(resolver1.previousResult, 'new result');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('mergeResolver handles empty arrays correctly', (t) => {
|
|
114
|
+
const resolver1 = t.context.pathwayResolver;
|
|
115
|
+
const resolver2 = new PathwayResolver({
|
|
116
|
+
config: mockConfig,
|
|
117
|
+
pathway: mockPathway,
|
|
118
|
+
args: mockArgs,
|
|
119
|
+
endpoints: mockModelEndpoints,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
resolver1.warnings = [];
|
|
123
|
+
resolver1.errors = [];
|
|
124
|
+
resolver2.warnings = [];
|
|
125
|
+
resolver2.errors = [];
|
|
126
|
+
|
|
127
|
+
resolver1.mergeResolver(resolver2);
|
|
128
|
+
t.deepEqual(resolver1.warnings, []);
|
|
129
|
+
t.deepEqual(resolver1.errors, []);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// mergeResultData Tests - Basic Object Merging
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
test('mergeResultData returns current data when newData is null/undefined', (t) => {
|
|
137
|
+
const resolver = t.context.pathwayResolver;
|
|
138
|
+
const originalData = { citations: ['cite1'], usage: { tokens: 100 } };
|
|
139
|
+
resolver.pathwayResultData = originalData;
|
|
140
|
+
|
|
141
|
+
const result1 = resolver.mergeResultData(null);
|
|
142
|
+
const result2 = resolver.mergeResultData(undefined);
|
|
143
|
+
|
|
144
|
+
t.deepEqual(result1, originalData);
|
|
145
|
+
t.deepEqual(result2, originalData);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('mergeResultData merges simple objects correctly', (t) => {
|
|
149
|
+
const resolver = t.context.pathwayResolver;
|
|
150
|
+
resolver.pathwayResultData = {
|
|
151
|
+
citations: ['cite1'],
|
|
152
|
+
usage: { tokens: 100 },
|
|
153
|
+
metadata: { source: 'test1' }
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const newData = {
|
|
157
|
+
citations: ['cite2'],
|
|
158
|
+
usage: { tokens: 200 },
|
|
159
|
+
metadata: { source: 'test2' }
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const result = resolver.mergeResultData(newData);
|
|
163
|
+
|
|
164
|
+
t.deepEqual(result.citations, ['cite1', 'cite2']);
|
|
165
|
+
t.deepEqual(result.usage, [{ tokens: 200 }, { tokens: 100 }]); // Should be converted to array
|
|
166
|
+
t.deepEqual(result.metadata, { source: 'test2' });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('mergeResultData handles empty current data', (t) => {
|
|
170
|
+
const resolver = t.context.pathwayResolver;
|
|
171
|
+
resolver.pathwayResultData = null;
|
|
172
|
+
|
|
173
|
+
const newData = {
|
|
174
|
+
citations: ['cite1'],
|
|
175
|
+
usage: { tokens: 100 },
|
|
176
|
+
finishReason: 'stop'
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const result = resolver.mergeResultData(newData);
|
|
180
|
+
|
|
181
|
+
t.deepEqual(result.citations, newData.citations);
|
|
182
|
+
t.deepEqual(result.finishReason, newData.finishReason);
|
|
183
|
+
t.deepEqual(result.usage, [{ tokens: 100 }]); // Should be converted to array
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// mergeResultData Tests - CortexResponse Handling
|
|
188
|
+
// ============================================================================
|
|
189
|
+
|
|
190
|
+
test('mergeResultData correctly handles CortexResponse objects', (t) => {
|
|
191
|
+
const resolver = t.context.pathwayResolver;
|
|
192
|
+
resolver.pathwayResultData = {
|
|
193
|
+
citations: ['cite1'],
|
|
194
|
+
usage: { tokens: 100 }
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const cortexResponse = new CortexResponse({
|
|
198
|
+
output_text: 'Test response',
|
|
199
|
+
citations: ['cite2'],
|
|
200
|
+
toolCalls: [{ name: 'test_tool', args: {} }],
|
|
201
|
+
usage: { tokens: 200 },
|
|
202
|
+
finishReason: 'tool_calls'
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const result = resolver.mergeResultData(cortexResponse);
|
|
206
|
+
|
|
207
|
+
t.deepEqual(result.citations, ['cite1', 'cite2']);
|
|
208
|
+
t.deepEqual(result.toolCalls, [{ name: 'test_tool', args: {} }]);
|
|
209
|
+
t.deepEqual(result.usage, [{ tokens: 200 }, { tokens: 100 }]); // Should be converted to array
|
|
210
|
+
t.is(result.finishReason, 'tool_calls');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('mergeResultData handles CortexResponse with empty arrays', (t) => {
|
|
214
|
+
const resolver = t.context.pathwayResolver;
|
|
215
|
+
resolver.pathwayResultData = {
|
|
216
|
+
citations: ['cite1'],
|
|
217
|
+
toolCalls: [{ name: 'existing_tool', args: {} }]
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const cortexResponse = new CortexResponse({
|
|
221
|
+
output_text: 'Test response',
|
|
222
|
+
citations: [],
|
|
223
|
+
toolCalls: [],
|
|
224
|
+
usage: { tokens: 100 }
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const result = resolver.mergeResultData(cortexResponse);
|
|
228
|
+
|
|
229
|
+
t.deepEqual(result.citations, ['cite1']); // Should preserve existing
|
|
230
|
+
t.deepEqual(result.toolCalls, [{ name: 'existing_tool', args: {} }]); // Should preserve existing
|
|
231
|
+
t.deepEqual(result.usage, [{ tokens: 100 }]); // Should be converted to array
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('mergeResultData handles CortexResponse with null arrays', (t) => {
|
|
235
|
+
const resolver = t.context.pathwayResolver;
|
|
236
|
+
resolver.pathwayResultData = {
|
|
237
|
+
citations: ['cite1'],
|
|
238
|
+
toolCalls: [{ name: 'existing_tool', args: {} }]
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const cortexResponse = new CortexResponse({
|
|
242
|
+
output_text: 'Test response',
|
|
243
|
+
citations: null,
|
|
244
|
+
toolCalls: null,
|
|
245
|
+
usage: { tokens: 100 }
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const result = resolver.mergeResultData(cortexResponse);
|
|
249
|
+
|
|
250
|
+
t.deepEqual(result.citations, ['cite1']); // Should preserve existing
|
|
251
|
+
t.deepEqual(result.toolCalls, [{ name: 'existing_tool', args: {} }]); // Should preserve existing
|
|
252
|
+
t.deepEqual(result.usage, [{ tokens: 100 }]); // Should be converted to array
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// mergeResultData Tests - Array Field Handling
|
|
257
|
+
// ============================================================================
|
|
258
|
+
|
|
259
|
+
test('mergeResultData concatenates citations arrays correctly', (t) => {
|
|
260
|
+
const resolver = t.context.pathwayResolver;
|
|
261
|
+
resolver.pathwayResultData = {
|
|
262
|
+
citations: ['cite1', 'cite2']
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const newData = {
|
|
266
|
+
citations: ['cite3', 'cite4']
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const result = resolver.mergeResultData(newData);
|
|
270
|
+
|
|
271
|
+
t.deepEqual(result.citations, ['cite1', 'cite2', 'cite3', 'cite4']);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('mergeResultData concatenates toolCalls arrays correctly', (t) => {
|
|
275
|
+
const resolver = t.context.pathwayResolver;
|
|
276
|
+
resolver.pathwayResultData = {
|
|
277
|
+
toolCalls: [{ name: 'tool1', args: {} }]
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const newData = {
|
|
281
|
+
toolCalls: [{ name: 'tool2', args: {} }, { name: 'tool3', args: {} }]
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const result = resolver.mergeResultData(newData);
|
|
285
|
+
|
|
286
|
+
t.is(result.toolCalls.length, 3);
|
|
287
|
+
t.is(result.toolCalls[0].name, 'tool1');
|
|
288
|
+
t.is(result.toolCalls[1].name, 'tool2');
|
|
289
|
+
t.is(result.toolCalls[2].name, 'tool3');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test('mergeResultData handles mixed array and non-array citations', (t) => {
|
|
293
|
+
const resolver = t.context.pathwayResolver;
|
|
294
|
+
resolver.pathwayResultData = {
|
|
295
|
+
citations: ['cite1']
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const newData = {
|
|
299
|
+
citations: ['cite2'] // Keep as array to match expected behavior
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const result = resolver.mergeResultData(newData);
|
|
303
|
+
|
|
304
|
+
t.deepEqual(result.citations, ['cite1', 'cite2']);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// mergeResultData Tests - Usage and ToolUsed Array Creation
|
|
309
|
+
// ============================================================================
|
|
310
|
+
|
|
311
|
+
test('mergeResultData creates usage array with new value first', (t) => {
|
|
312
|
+
const resolver = t.context.pathwayResolver;
|
|
313
|
+
resolver.pathwayResultData = {
|
|
314
|
+
usage: { tokens: 100, prompt_tokens: 50 }
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const newData = {
|
|
318
|
+
usage: { tokens: 200, prompt_tokens: 100 }
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const result = resolver.mergeResultData(newData);
|
|
322
|
+
|
|
323
|
+
t.is(Array.isArray(result.usage), true);
|
|
324
|
+
t.is(result.usage.length, 2);
|
|
325
|
+
t.deepEqual(result.usage[0], { tokens: 200, prompt_tokens: 100 }); // New first
|
|
326
|
+
t.deepEqual(result.usage[1], { tokens: 100, prompt_tokens: 50 }); // Old second
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test('mergeResultData creates toolUsed array with new value first', (t) => {
|
|
330
|
+
const resolver = t.context.pathwayResolver;
|
|
331
|
+
resolver.pathwayResultData = {
|
|
332
|
+
toolUsed: 'tool1'
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const newData = {
|
|
336
|
+
toolUsed: 'tool2'
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const result = resolver.mergeResultData(newData);
|
|
340
|
+
|
|
341
|
+
t.is(Array.isArray(result.toolUsed), true);
|
|
342
|
+
t.is(result.toolUsed.length, 2);
|
|
343
|
+
t.is(result.toolUsed[0], 'tool2'); // New first
|
|
344
|
+
t.is(result.toolUsed[1], 'tool1'); // Old second
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('mergeResultData handles existing usage arrays correctly', (t) => {
|
|
348
|
+
const resolver = t.context.pathwayResolver;
|
|
349
|
+
resolver.pathwayResultData = {
|
|
350
|
+
usage: [{ tokens: 100 }, { tokens: 150 }]
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const newData = {
|
|
354
|
+
usage: [{ tokens: 200 }, { tokens: 250 }]
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const result = resolver.mergeResultData(newData);
|
|
358
|
+
|
|
359
|
+
t.is(Array.isArray(result.usage), true);
|
|
360
|
+
t.is(result.usage.length, 4);
|
|
361
|
+
t.deepEqual(result.usage[0], { tokens: 200 }); // New first
|
|
362
|
+
t.deepEqual(result.usage[1], { tokens: 250 }); // New second
|
|
363
|
+
t.deepEqual(result.usage[2], { tokens: 100 }); // Old first
|
|
364
|
+
t.deepEqual(result.usage[3], { tokens: 150 }); // Old second
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('mergeResultData handles null usage values correctly', (t) => {
|
|
368
|
+
const resolver = t.context.pathwayResolver;
|
|
369
|
+
resolver.pathwayResultData = {
|
|
370
|
+
usage: null
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const newData = {
|
|
374
|
+
usage: { tokens: 200 }
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const result = resolver.mergeResultData(newData);
|
|
378
|
+
|
|
379
|
+
t.is(Array.isArray(result.usage), true);
|
|
380
|
+
t.is(result.usage.length, 1);
|
|
381
|
+
t.deepEqual(result.usage[0], { tokens: 200 });
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('mergeResultData handles both null usage values', (t) => {
|
|
385
|
+
const resolver = t.context.pathwayResolver;
|
|
386
|
+
resolver.pathwayResultData = {
|
|
387
|
+
usage: null
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const newData = {
|
|
391
|
+
usage: null
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const result = resolver.mergeResultData(newData);
|
|
395
|
+
|
|
396
|
+
t.is(result.usage, null);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// ============================================================================
|
|
400
|
+
// Integration Tests - Tool Interface Scenarios
|
|
401
|
+
// ============================================================================
|
|
402
|
+
|
|
403
|
+
test('mergeResolver integrates with tool interface data correctly', (t) => {
|
|
404
|
+
const resolver1 = t.context.pathwayResolver;
|
|
405
|
+
const resolver2 = new PathwayResolver({
|
|
406
|
+
config: mockConfig,
|
|
407
|
+
pathway: mockPathway,
|
|
408
|
+
args: mockArgs,
|
|
409
|
+
endpoints: mockModelEndpoints,
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Simulate tool interface data
|
|
413
|
+
resolver1.pathwayResultData = {
|
|
414
|
+
citations: ['source1'],
|
|
415
|
+
toolCalls: [{ name: 'search_tool', args: { query: 'test' } }],
|
|
416
|
+
usage: { tokens: 100 },
|
|
417
|
+
toolUsed: 'search_tool'
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
resolver2.pathwayResultData = {
|
|
421
|
+
citations: ['source2'],
|
|
422
|
+
toolCalls: [{ name: 'analyze_tool', args: { data: 'test' } }],
|
|
423
|
+
usage: { tokens: 150 },
|
|
424
|
+
toolUsed: 'analyze_tool'
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
resolver1.mergeResolver(resolver2);
|
|
428
|
+
|
|
429
|
+
const result = resolver1.pathwayResultData;
|
|
430
|
+
|
|
431
|
+
t.deepEqual(result.citations, ['source1', 'source2']);
|
|
432
|
+
t.is(result.toolCalls.length, 2);
|
|
433
|
+
t.is(result.toolCalls[0].name, 'search_tool');
|
|
434
|
+
t.is(result.toolCalls[1].name, 'analyze_tool');
|
|
435
|
+
t.is(Array.isArray(result.usage), true);
|
|
436
|
+
t.is(result.usage.length, 2);
|
|
437
|
+
t.is(Array.isArray(result.toolUsed), true);
|
|
438
|
+
t.is(result.toolUsed.length, 2);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test('mergeResultData handles complex tool interface scenarios', (t) => {
|
|
442
|
+
const resolver = t.context.pathwayResolver;
|
|
443
|
+
|
|
444
|
+
// Initial data from first tool call
|
|
445
|
+
resolver.pathwayResultData = {
|
|
446
|
+
citations: ['doc1'],
|
|
447
|
+
toolCalls: [{ name: 'fetch_data', args: { id: 1 } }],
|
|
448
|
+
usage: { tokens: 50, prompt_tokens: 25 },
|
|
449
|
+
toolUsed: 'fetch_data'
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// CortexResponse from second tool call
|
|
453
|
+
const cortexResponse = new CortexResponse({
|
|
454
|
+
output_text: 'Analysis complete',
|
|
455
|
+
citations: ['doc2', 'doc3'],
|
|
456
|
+
toolCalls: [{ name: 'analyze_data', args: { data: 'fetched' } }],
|
|
457
|
+
usage: { tokens: 100, prompt_tokens: 50 },
|
|
458
|
+
finishReason: 'stop'
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const result = resolver.mergeResultData(cortexResponse);
|
|
462
|
+
|
|
463
|
+
t.deepEqual(result.citations, ['doc1', 'doc2', 'doc3']);
|
|
464
|
+
t.is(result.toolCalls.length, 2);
|
|
465
|
+
t.is(result.toolCalls[0].name, 'fetch_data');
|
|
466
|
+
t.is(result.toolCalls[1].name, 'analyze_data');
|
|
467
|
+
t.is(Array.isArray(result.usage), true);
|
|
468
|
+
t.is(result.usage.length, 2);
|
|
469
|
+
t.deepEqual(result.usage[0], { tokens: 100, prompt_tokens: 50 }); // New first
|
|
470
|
+
t.deepEqual(result.usage[1], { tokens: 50, prompt_tokens: 25 }); // Old second
|
|
471
|
+
t.is(result.finishReason, 'stop');
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// ============================================================================
|
|
475
|
+
// Edge Cases and Error Handling
|
|
476
|
+
// ============================================================================
|
|
477
|
+
|
|
478
|
+
test('mergeResultData handles malformed CortexResponse objects', (t) => {
|
|
479
|
+
const resolver = t.context.pathwayResolver;
|
|
480
|
+
resolver.pathwayResultData = { citations: ['cite1'] };
|
|
481
|
+
|
|
482
|
+
// Create a mock object that looks like CortexResponse but isn't
|
|
483
|
+
const mockCortexResponse = {
|
|
484
|
+
constructor: { name: 'CortexResponse' },
|
|
485
|
+
citations: ['cite2'],
|
|
486
|
+
usage: { tokens: 100 }
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const result = resolver.mergeResultData(mockCortexResponse);
|
|
490
|
+
|
|
491
|
+
t.deepEqual(result.citations, ['cite1', 'cite2']);
|
|
492
|
+
t.deepEqual(result.usage, [{ tokens: 100 }]); // Should be converted to array
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test('mergeResultData handles deeply nested objects', (t) => {
|
|
496
|
+
const resolver = t.context.pathwayResolver;
|
|
497
|
+
resolver.pathwayResultData = {
|
|
498
|
+
metadata: {
|
|
499
|
+
nested: {
|
|
500
|
+
deep: {
|
|
501
|
+
value: 'original'
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
const newData = {
|
|
508
|
+
metadata: {
|
|
509
|
+
nested: {
|
|
510
|
+
deep: {
|
|
511
|
+
value: 'updated',
|
|
512
|
+
newField: 'added'
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const result = resolver.mergeResultData(newData);
|
|
519
|
+
|
|
520
|
+
t.deepEqual(result.metadata.nested.deep.value, 'updated');
|
|
521
|
+
t.is(result.metadata.nested.deep.newField, 'added');
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
test('mergeResultData handles circular references gracefully', (t) => {
|
|
525
|
+
const resolver = t.context.pathwayResolver;
|
|
526
|
+
resolver.pathwayResultData = { citations: ['cite1'] };
|
|
527
|
+
|
|
528
|
+
const newData = { citations: ['cite2'] };
|
|
529
|
+
newData.self = newData; // Create circular reference
|
|
530
|
+
|
|
531
|
+
const result = resolver.mergeResultData(newData);
|
|
532
|
+
|
|
533
|
+
t.deepEqual(result.citations, ['cite1', 'cite2']);
|
|
534
|
+
t.is(result.self, newData); // Should handle circular reference
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// ============================================================================
|
|
538
|
+
// Performance and Memory Tests
|
|
539
|
+
// ============================================================================
|
|
540
|
+
|
|
541
|
+
test('mergeResultData handles large arrays efficiently', (t) => {
|
|
542
|
+
const resolver = t.context.pathwayResolver;
|
|
543
|
+
|
|
544
|
+
// Create large arrays
|
|
545
|
+
const largeCitations = Array.from({ length: 1000 }, (_, i) => `cite${i}`);
|
|
546
|
+
const largeToolCalls = Array.from({ length: 100 }, (_, i) => ({
|
|
547
|
+
name: `tool${i}`,
|
|
548
|
+
args: { id: i }
|
|
549
|
+
}));
|
|
550
|
+
|
|
551
|
+
resolver.pathwayResultData = {
|
|
552
|
+
citations: largeCitations.slice(0, 500),
|
|
553
|
+
toolCalls: largeToolCalls.slice(0, 50)
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const newData = {
|
|
557
|
+
citations: largeCitations.slice(500),
|
|
558
|
+
toolCalls: largeToolCalls.slice(50)
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
const result = resolver.mergeResultData(newData);
|
|
562
|
+
|
|
563
|
+
t.is(result.citations.length, 1000);
|
|
564
|
+
t.is(result.toolCalls.length, 100);
|
|
565
|
+
t.is(result.citations[0], 'cite0');
|
|
566
|
+
t.is(result.citations[999], 'cite999');
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test('mergeResolver handles multiple sequential merges correctly', (t) => {
|
|
570
|
+
const resolver = t.context.pathwayResolver;
|
|
571
|
+
|
|
572
|
+
// First merge
|
|
573
|
+
const resolver1 = new PathwayResolver({
|
|
574
|
+
config: mockConfig,
|
|
575
|
+
pathway: mockPathway,
|
|
576
|
+
args: mockArgs,
|
|
577
|
+
endpoints: mockModelEndpoints,
|
|
578
|
+
});
|
|
579
|
+
resolver1.pathwayResultData = { citations: ['cite1'], usage: { tokens: 100 } };
|
|
580
|
+
resolver.mergeResolver(resolver1);
|
|
581
|
+
|
|
582
|
+
// Second merge
|
|
583
|
+
const resolver2 = new PathwayResolver({
|
|
584
|
+
config: mockConfig,
|
|
585
|
+
pathway: mockPathway,
|
|
586
|
+
args: mockArgs,
|
|
587
|
+
endpoints: mockModelEndpoints,
|
|
588
|
+
});
|
|
589
|
+
resolver2.pathwayResultData = { citations: ['cite2'], usage: { tokens: 200 } };
|
|
590
|
+
resolver.mergeResolver(resolver2);
|
|
591
|
+
|
|
592
|
+
// Third merge
|
|
593
|
+
const resolver3 = new PathwayResolver({
|
|
594
|
+
config: mockConfig,
|
|
595
|
+
pathway: mockPathway,
|
|
596
|
+
args: mockArgs,
|
|
597
|
+
endpoints: mockModelEndpoints,
|
|
598
|
+
});
|
|
599
|
+
resolver3.pathwayResultData = { citations: ['cite3'], usage: { tokens: 300 } };
|
|
600
|
+
resolver.mergeResolver(resolver3);
|
|
601
|
+
|
|
602
|
+
const result = resolver.pathwayResultData;
|
|
603
|
+
|
|
604
|
+
t.deepEqual(result.citations, ['cite1', 'cite2', 'cite3']);
|
|
605
|
+
t.is(Array.isArray(result.usage), true);
|
|
606
|
+
t.is(result.usage.length, 3);
|
|
607
|
+
t.deepEqual(result.usage[0], { tokens: 300 }); // Most recent first
|
|
608
|
+
t.deepEqual(result.usage[1], { tokens: 200 });
|
|
609
|
+
t.deepEqual(result.usage[2], { tokens: 100 });
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// ============================================================================
|
|
613
|
+
// Additional Edge Cases
|
|
614
|
+
// ============================================================================
|
|
615
|
+
|
|
616
|
+
test('mergeResultData handles undefined array fields correctly', (t) => {
|
|
617
|
+
const resolver = t.context.pathwayResolver;
|
|
618
|
+
resolver.pathwayResultData = {
|
|
619
|
+
citations: undefined,
|
|
620
|
+
toolCalls: undefined
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
const newData = {
|
|
624
|
+
citations: ['cite1'],
|
|
625
|
+
toolCalls: [{ name: 'tool1', args: {} }]
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
const result = resolver.mergeResultData(newData);
|
|
629
|
+
|
|
630
|
+
t.deepEqual(result.citations, ['cite1']);
|
|
631
|
+
t.deepEqual(result.toolCalls, [{ name: 'tool1', args: {} }]);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
test('mergeResultData handles empty string citations', (t) => {
|
|
635
|
+
const resolver = t.context.pathwayResolver;
|
|
636
|
+
resolver.pathwayResultData = {
|
|
637
|
+
citations: ['']
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
const newData = {
|
|
641
|
+
citations: ['cite1']
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
const result = resolver.mergeResultData(newData);
|
|
645
|
+
|
|
646
|
+
t.deepEqual(result.citations, ['', 'cite1']);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
test('mergeResultData handles complex nested toolCalls', (t) => {
|
|
650
|
+
const resolver = t.context.pathwayResolver;
|
|
651
|
+
resolver.pathwayResultData = {
|
|
652
|
+
toolCalls: [{
|
|
653
|
+
name: 'search_tool',
|
|
654
|
+
args: { query: 'test', filters: { category: 'tech' } },
|
|
655
|
+
id: 'call_1'
|
|
656
|
+
}]
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const newData = {
|
|
660
|
+
toolCalls: [{
|
|
661
|
+
name: 'analyze_tool',
|
|
662
|
+
args: { data: 'search_results', options: { deep: true } },
|
|
663
|
+
id: 'call_2'
|
|
664
|
+
}]
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
const result = resolver.mergeResultData(newData);
|
|
668
|
+
|
|
669
|
+
t.is(result.toolCalls.length, 2);
|
|
670
|
+
t.is(result.toolCalls[0].name, 'search_tool');
|
|
671
|
+
t.is(result.toolCalls[0].args.query, 'test');
|
|
672
|
+
t.is(result.toolCalls[0].args.filters.category, 'tech');
|
|
673
|
+
t.is(result.toolCalls[1].name, 'analyze_tool');
|
|
674
|
+
t.is(result.toolCalls[1].args.data, 'search_results');
|
|
675
|
+
t.is(result.toolCalls[1].args.options.deep, true);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
test('mergeResultData preserves non-standard fields', (t) => {
|
|
679
|
+
const resolver = t.context.pathwayResolver;
|
|
680
|
+
resolver.pathwayResultData = {
|
|
681
|
+
customField: 'original',
|
|
682
|
+
nestedData: { value: 1, items: ['a', 'b'] }
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
const newData = {
|
|
686
|
+
customField: 'updated',
|
|
687
|
+
nestedData: { value: 2, items: ['c', 'd'] },
|
|
688
|
+
newField: 'added'
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
const result = resolver.mergeResultData(newData);
|
|
692
|
+
|
|
693
|
+
t.is(result.customField, 'updated');
|
|
694
|
+
t.is(result.nestedData.value, 2);
|
|
695
|
+
t.deepEqual(result.nestedData.items, ['c', 'd']);
|
|
696
|
+
t.is(result.newField, 'added');
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
test('mergeResolver handles resolver with no pathwayResultData', (t) => {
|
|
700
|
+
const resolver1 = t.context.pathwayResolver;
|
|
701
|
+
const resolver2 = new PathwayResolver({
|
|
702
|
+
config: mockConfig,
|
|
703
|
+
pathway: mockPathway,
|
|
704
|
+
args: mockArgs,
|
|
705
|
+
endpoints: mockModelEndpoints,
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
resolver1.pathwayResultData = { citations: ['cite1'] };
|
|
709
|
+
resolver2.pathwayResultData = null;
|
|
710
|
+
|
|
711
|
+
resolver1.mergeResolver(resolver2);
|
|
712
|
+
|
|
713
|
+
t.deepEqual(resolver1.pathwayResultData, { citations: ['cite1'] });
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
test('mergeResultData handles boolean and numeric values in usage', (t) => {
|
|
717
|
+
const resolver = t.context.pathwayResolver;
|
|
718
|
+
resolver.pathwayResultData = {
|
|
719
|
+
usage: { tokens: 100, cached: true, cost: 0.05 }
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
const newData = {
|
|
723
|
+
usage: { tokens: 200, cached: false, cost: 0.10 }
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
const result = resolver.mergeResultData(newData);
|
|
727
|
+
|
|
728
|
+
t.is(Array.isArray(result.usage), true);
|
|
729
|
+
t.is(result.usage.length, 2);
|
|
730
|
+
t.deepEqual(result.usage[0], { tokens: 200, cached: false, cost: 0.10 });
|
|
731
|
+
t.deepEqual(result.usage[1], { tokens: 100, cached: true, cost: 0.05 });
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
// ============================================================================
|
|
735
|
+
// Tool Property Integration Tests
|
|
736
|
+
// ============================================================================
|
|
737
|
+
|
|
738
|
+
test('tool property setter integrates data correctly via mergeResultData', (t) => {
|
|
739
|
+
const resolver = t.context.pathwayResolver;
|
|
740
|
+
|
|
741
|
+
// Set initial data
|
|
742
|
+
resolver.pathwayResultData = {
|
|
743
|
+
citations: ['cite1'],
|
|
744
|
+
usage: { tokens: 100 }
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
// Set tool property with JSON string
|
|
748
|
+
const toolData = {
|
|
749
|
+
toolUsed: 'search_tool',
|
|
750
|
+
citations: ['cite2'],
|
|
751
|
+
title: 'Test Title',
|
|
752
|
+
search: { query: 'test query' },
|
|
753
|
+
coding: true
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
resolver.tool = JSON.stringify(toolData);
|
|
757
|
+
|
|
758
|
+
// Verify data was merged correctly
|
|
759
|
+
t.deepEqual(resolver.pathwayResultData.citations, ['cite1', 'cite2']);
|
|
760
|
+
t.deepEqual(resolver.pathwayResultData.toolUsed, ['search_tool']); // Should be converted to array
|
|
761
|
+
t.is(resolver.pathwayResultData.title, 'Test Title');
|
|
762
|
+
t.deepEqual(resolver.pathwayResultData.search, { query: 'test query' });
|
|
763
|
+
t.is(resolver.pathwayResultData.coding, true);
|
|
764
|
+
t.deepEqual(resolver.pathwayResultData.usage, [{ tokens: 100 }]); // Should be converted to array
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
test('tool property setter handles object input directly', (t) => {
|
|
768
|
+
const resolver = t.context.pathwayResolver;
|
|
769
|
+
|
|
770
|
+
// Set tool property with object (not JSON string)
|
|
771
|
+
const toolData = {
|
|
772
|
+
toolUsed: 'analyze_tool',
|
|
773
|
+
citations: ['cite1'],
|
|
774
|
+
hideFromModel: true,
|
|
775
|
+
toolCallbackName: 'test_callback'
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
resolver.tool = toolData;
|
|
779
|
+
|
|
780
|
+
// Verify data was merged correctly
|
|
781
|
+
t.deepEqual(resolver.pathwayResultData.citations, ['cite1']);
|
|
782
|
+
t.deepEqual(resolver.pathwayResultData.toolUsed, ['analyze_tool']);
|
|
783
|
+
t.is(resolver.pathwayResultData.hideFromModel, true);
|
|
784
|
+
t.is(resolver.pathwayResultData.toolCallbackName, 'test_callback');
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
test('tool property getter returns correct legacy fields', (t) => {
|
|
788
|
+
const resolver = t.context.pathwayResolver;
|
|
789
|
+
|
|
790
|
+
// Set up pathwayResultData with various fields
|
|
791
|
+
resolver.pathwayResultData = {
|
|
792
|
+
hideFromModel: true,
|
|
793
|
+
toolCallbackName: 'test_callback',
|
|
794
|
+
title: 'Test Title',
|
|
795
|
+
search: { query: 'test' },
|
|
796
|
+
coding: false,
|
|
797
|
+
codeRequestId: 'code_123',
|
|
798
|
+
toolCallbackId: 'callback_456',
|
|
799
|
+
toolUsed: ['tool1', 'tool2'],
|
|
800
|
+
citations: ['cite1', 'cite2'],
|
|
801
|
+
// These should be excluded from tool getter
|
|
802
|
+
usage: { tokens: 100 },
|
|
803
|
+
finishReason: 'stop',
|
|
804
|
+
customField: 'should_not_appear'
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
const toolString = resolver.tool;
|
|
808
|
+
const toolData = JSON.parse(toolString);
|
|
809
|
+
|
|
810
|
+
// Verify only legacy fields are included
|
|
811
|
+
t.is(toolData.hideFromModel, true);
|
|
812
|
+
t.is(toolData.toolCallbackName, 'test_callback');
|
|
813
|
+
t.is(toolData.title, 'Test Title');
|
|
814
|
+
t.deepEqual(toolData.search, { query: 'test' });
|
|
815
|
+
t.is(toolData.coding, false);
|
|
816
|
+
t.is(toolData.codeRequestId, 'code_123');
|
|
817
|
+
t.is(toolData.toolCallbackId, 'callback_456');
|
|
818
|
+
t.deepEqual(toolData.toolUsed, ['tool1', 'tool2']);
|
|
819
|
+
t.deepEqual(toolData.citations, ['cite1', 'cite2']);
|
|
820
|
+
|
|
821
|
+
// Verify excluded fields are not present
|
|
822
|
+
t.is(toolData.usage, undefined);
|
|
823
|
+
t.is(toolData.finishReason, undefined);
|
|
824
|
+
t.is(toolData.customField, undefined);
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
test('tool property getter excludes undefined fields', (t) => {
|
|
828
|
+
const resolver = t.context.pathwayResolver;
|
|
829
|
+
|
|
830
|
+
// Set up pathwayResultData with only some fields defined
|
|
831
|
+
resolver.pathwayResultData = {
|
|
832
|
+
title: 'Test Title',
|
|
833
|
+
citations: ['cite1'],
|
|
834
|
+
// Other legacy fields are undefined
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
const toolString = resolver.tool;
|
|
838
|
+
const toolData = JSON.parse(toolString);
|
|
839
|
+
|
|
840
|
+
// Verify only defined fields are included
|
|
841
|
+
t.is(toolData.title, 'Test Title');
|
|
842
|
+
t.deepEqual(toolData.citations, ['cite1']);
|
|
843
|
+
|
|
844
|
+
// Verify undefined fields are excluded
|
|
845
|
+
t.is(toolData.hideFromModel, undefined);
|
|
846
|
+
t.is(toolData.toolCallbackName, undefined);
|
|
847
|
+
t.is(toolData.search, undefined);
|
|
848
|
+
t.is(toolData.coding, undefined);
|
|
849
|
+
t.is(toolData.codeRequestId, undefined);
|
|
850
|
+
t.is(toolData.toolCallbackId, undefined);
|
|
851
|
+
t.is(toolData.toolUsed, undefined);
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
test('tool property setter handles invalid JSON gracefully', (t) => {
|
|
855
|
+
const resolver = t.context.pathwayResolver;
|
|
856
|
+
|
|
857
|
+
// Set initial data
|
|
858
|
+
resolver.pathwayResultData = {
|
|
859
|
+
citations: ['cite1'],
|
|
860
|
+
title: 'Original Title'
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
// Mock console.warn to capture warning
|
|
864
|
+
const originalWarn = console.warn;
|
|
865
|
+
let warningCalled = false;
|
|
866
|
+
console.warn = (message) => {
|
|
867
|
+
warningCalled = true;
|
|
868
|
+
t.true(message.includes('Invalid tool property assignment'));
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
// Set tool property with invalid JSON
|
|
872
|
+
resolver.tool = '{"invalid": json}';
|
|
873
|
+
|
|
874
|
+
// Verify original data is preserved
|
|
875
|
+
t.deepEqual(resolver.pathwayResultData.citations, ['cite1']);
|
|
876
|
+
t.is(resolver.pathwayResultData.title, 'Original Title');
|
|
877
|
+
t.true(warningCalled);
|
|
878
|
+
|
|
879
|
+
// Restore console.warn
|
|
880
|
+
console.warn = originalWarn;
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
test('tool property integration with mergeResolver', (t) => {
|
|
884
|
+
const resolver1 = t.context.pathwayResolver;
|
|
885
|
+
const resolver2 = new PathwayResolver({
|
|
886
|
+
config: mockConfig,
|
|
887
|
+
pathway: mockPathway,
|
|
888
|
+
args: mockArgs,
|
|
889
|
+
endpoints: mockModelEndpoints,
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
// Set up resolver1 with tool data
|
|
893
|
+
resolver1.pathwayResultData = {
|
|
894
|
+
citations: ['cite1'],
|
|
895
|
+
title: 'Original Title'
|
|
896
|
+
};
|
|
897
|
+
resolver1.tool = JSON.stringify({
|
|
898
|
+
toolUsed: 'search_tool',
|
|
899
|
+
citations: ['cite2'],
|
|
900
|
+
title: 'Updated Title'
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// Set up resolver2 with different tool data
|
|
904
|
+
resolver2.pathwayResultData = {
|
|
905
|
+
citations: ['cite3'],
|
|
906
|
+
coding: true
|
|
907
|
+
};
|
|
908
|
+
resolver2.tool = JSON.stringify({
|
|
909
|
+
toolUsed: 'analyze_tool',
|
|
910
|
+
citations: ['cite4'],
|
|
911
|
+
hideFromModel: true
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
// Merge resolver2 into resolver1
|
|
915
|
+
resolver1.mergeResolver(resolver2);
|
|
916
|
+
|
|
917
|
+
// Verify merged data
|
|
918
|
+
t.deepEqual(resolver1.pathwayResultData.citations, ['cite1', 'cite2', 'cite3', 'cite4']);
|
|
919
|
+
t.is(resolver1.pathwayResultData.title, 'Updated Title'); // From resolver1's tool setter
|
|
920
|
+
t.is(resolver1.pathwayResultData.coding, true); // From resolver2
|
|
921
|
+
t.is(resolver1.pathwayResultData.hideFromModel, true); // From resolver2's tool setter
|
|
922
|
+
t.deepEqual(resolver1.pathwayResultData.toolUsed, ['analyze_tool', 'search_tool']); // Both merged, newest first
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
test('tool property handles complex nested data', (t) => {
|
|
926
|
+
const resolver = t.context.pathwayResolver;
|
|
927
|
+
|
|
928
|
+
const complexToolData = {
|
|
929
|
+
toolUsed: 'complex_tool',
|
|
930
|
+
citations: ['cite1', 'cite2'],
|
|
931
|
+
search: {
|
|
932
|
+
query: 'complex search',
|
|
933
|
+
filters: {
|
|
934
|
+
category: 'tech',
|
|
935
|
+
dateRange: { start: '2024-01-01', end: '2024-12-31' }
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
title: 'Complex Analysis',
|
|
939
|
+
coding: true,
|
|
940
|
+
hideFromModel: false
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
resolver.tool = JSON.stringify(complexToolData);
|
|
944
|
+
|
|
945
|
+
// Verify complex data was merged correctly
|
|
946
|
+
t.deepEqual(resolver.pathwayResultData.toolUsed, ['complex_tool']);
|
|
947
|
+
t.deepEqual(resolver.pathwayResultData.citations, ['cite1', 'cite2']);
|
|
948
|
+
t.deepEqual(resolver.pathwayResultData.search, complexToolData.search);
|
|
949
|
+
t.is(resolver.pathwayResultData.title, 'Complex Analysis');
|
|
950
|
+
t.is(resolver.pathwayResultData.coding, true);
|
|
951
|
+
t.is(resolver.pathwayResultData.hideFromModel, false);
|
|
952
|
+
});
|