@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.
Files changed (211) hide show
  1. package/.github/workflows/cortex-file-handler-test.yml +61 -0
  2. package/README.md +31 -7
  3. package/config/default.example.json +15 -0
  4. package/config.js +133 -12
  5. package/helper-apps/cortex-autogen2/DigiCertGlobalRootCA.crt.pem +22 -0
  6. package/helper-apps/cortex-autogen2/Dockerfile +31 -0
  7. package/helper-apps/cortex-autogen2/Dockerfile.worker +41 -0
  8. package/helper-apps/cortex-autogen2/README.md +183 -0
  9. package/helper-apps/cortex-autogen2/__init__.py +1 -0
  10. package/helper-apps/cortex-autogen2/agents.py +131 -0
  11. package/helper-apps/cortex-autogen2/docker-compose.yml +20 -0
  12. package/helper-apps/cortex-autogen2/function_app.py +55 -0
  13. package/helper-apps/cortex-autogen2/host.json +15 -0
  14. package/helper-apps/cortex-autogen2/main.py +126 -0
  15. package/helper-apps/cortex-autogen2/poetry.lock +3652 -0
  16. package/helper-apps/cortex-autogen2/pyproject.toml +36 -0
  17. package/helper-apps/cortex-autogen2/requirements.txt +20 -0
  18. package/helper-apps/cortex-autogen2/send_task.py +105 -0
  19. package/helper-apps/cortex-autogen2/services/__init__.py +1 -0
  20. package/helper-apps/cortex-autogen2/services/azure_queue.py +85 -0
  21. package/helper-apps/cortex-autogen2/services/redis_publisher.py +153 -0
  22. package/helper-apps/cortex-autogen2/task_processor.py +488 -0
  23. package/helper-apps/cortex-autogen2/tools/__init__.py +24 -0
  24. package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +175 -0
  25. package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +601 -0
  26. package/helper-apps/cortex-autogen2/tools/coding_tools.py +72 -0
  27. package/helper-apps/cortex-autogen2/tools/download_tools.py +48 -0
  28. package/helper-apps/cortex-autogen2/tools/file_tools.py +545 -0
  29. package/helper-apps/cortex-autogen2/tools/search_tools.py +646 -0
  30. package/helper-apps/cortex-azure-cleaner/README.md +36 -0
  31. package/helper-apps/cortex-file-converter/README.md +93 -0
  32. package/helper-apps/cortex-file-converter/key_to_pdf.py +104 -0
  33. package/helper-apps/cortex-file-converter/list_blob_extensions.py +89 -0
  34. package/helper-apps/cortex-file-converter/process_azure_keynotes.py +181 -0
  35. package/helper-apps/cortex-file-converter/requirements.txt +1 -0
  36. package/helper-apps/cortex-file-handler/.env.test.azure.ci +7 -0
  37. package/helper-apps/cortex-file-handler/.env.test.azure.sample +1 -1
  38. package/helper-apps/cortex-file-handler/.env.test.gcs.ci +10 -0
  39. package/helper-apps/cortex-file-handler/.env.test.gcs.sample +2 -2
  40. package/helper-apps/cortex-file-handler/INTERFACE.md +41 -0
  41. package/helper-apps/cortex-file-handler/package.json +1 -1
  42. package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +41 -17
  43. package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +30 -15
  44. package/helper-apps/cortex-file-handler/scripts/test-azure.sh +32 -6
  45. package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +24 -2
  46. package/helper-apps/cortex-file-handler/scripts/validate-env.js +128 -0
  47. package/helper-apps/cortex-file-handler/src/blobHandler.js +161 -51
  48. package/helper-apps/cortex-file-handler/src/constants.js +3 -0
  49. package/helper-apps/cortex-file-handler/src/fileChunker.js +10 -8
  50. package/helper-apps/cortex-file-handler/src/index.js +116 -9
  51. package/helper-apps/cortex-file-handler/src/redis.js +61 -1
  52. package/helper-apps/cortex-file-handler/src/services/ConversionService.js +11 -8
  53. package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +2 -2
  54. package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +88 -6
  55. package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +58 -0
  56. package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +25 -5
  57. package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +9 -0
  58. package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +120 -16
  59. package/helper-apps/cortex-file-handler/src/start.js +27 -17
  60. package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +52 -1
  61. package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +40 -0
  62. package/helper-apps/cortex-file-handler/tests/checkHashShortLived.test.js +553 -0
  63. package/helper-apps/cortex-file-handler/tests/cleanup.test.js +46 -52
  64. package/helper-apps/cortex-file-handler/tests/containerConversionFlow.test.js +451 -0
  65. package/helper-apps/cortex-file-handler/tests/containerNameParsing.test.js +229 -0
  66. package/helper-apps/cortex-file-handler/tests/containerParameterFlow.test.js +392 -0
  67. package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +7 -2
  68. package/helper-apps/cortex-file-handler/tests/deleteOperations.test.js +348 -0
  69. package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +23 -2
  70. package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +11 -5
  71. package/helper-apps/cortex-file-handler/tests/getOperations.test.js +58 -24
  72. package/helper-apps/cortex-file-handler/tests/postOperations.test.js +11 -4
  73. package/helper-apps/cortex-file-handler/tests/shortLivedUrlConversion.test.js +225 -0
  74. package/helper-apps/cortex-file-handler/tests/start.test.js +8 -12
  75. package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +80 -0
  76. package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +388 -22
  77. package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -0
  78. package/lib/cortexResponse.js +153 -0
  79. package/lib/entityConstants.js +21 -3
  80. package/lib/logger.js +21 -4
  81. package/lib/pathwayTools.js +28 -9
  82. package/lib/util.js +49 -0
  83. package/package.json +1 -1
  84. package/pathways/basePathway.js +1 -0
  85. package/pathways/bing_afagent.js +54 -1
  86. package/pathways/call_tools.js +2 -3
  87. package/pathways/chat_jarvis.js +1 -1
  88. package/pathways/google_cse.js +27 -0
  89. package/pathways/grok_live_search.js +18 -0
  90. package/pathways/system/entity/memory/sys_memory_lookup_required.js +1 -0
  91. package/pathways/system/entity/memory/sys_memory_required.js +1 -0
  92. package/pathways/system/entity/memory/sys_search_memory.js +1 -0
  93. package/pathways/system/entity/sys_entity_agent.js +56 -4
  94. package/pathways/system/entity/sys_generator_quick.js +1 -0
  95. package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +26 -0
  96. package/pathways/system/entity/tools/sys_tool_google_search.js +141 -0
  97. package/pathways/system/entity/tools/sys_tool_grok_x_search.js +237 -0
  98. package/pathways/system/entity/tools/sys_tool_image.js +1 -1
  99. package/pathways/system/rest_streaming/sys_claude_37_sonnet.js +21 -0
  100. package/pathways/system/rest_streaming/sys_claude_41_opus.js +21 -0
  101. package/pathways/system/rest_streaming/sys_claude_4_sonnet.js +21 -0
  102. package/pathways/system/rest_streaming/sys_google_gemini_25_flash.js +25 -0
  103. package/pathways/system/rest_streaming/{sys_google_gemini_chat.js → sys_google_gemini_25_pro.js} +6 -4
  104. package/pathways/system/rest_streaming/sys_grok_4.js +23 -0
  105. package/pathways/system/rest_streaming/sys_grok_4_fast_non_reasoning.js +23 -0
  106. package/pathways/system/rest_streaming/sys_grok_4_fast_reasoning.js +23 -0
  107. package/pathways/system/rest_streaming/sys_openai_chat.js +3 -0
  108. package/pathways/system/rest_streaming/sys_openai_chat_gpt41.js +22 -0
  109. package/pathways/system/rest_streaming/sys_openai_chat_gpt41_mini.js +21 -0
  110. package/pathways/system/rest_streaming/sys_openai_chat_gpt41_nano.js +21 -0
  111. package/pathways/system/rest_streaming/{sys_claude_35_sonnet.js → sys_openai_chat_gpt4_omni.js} +6 -4
  112. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_omni_mini.js +21 -0
  113. package/pathways/system/rest_streaming/{sys_claude_3_haiku.js → sys_openai_chat_gpt5.js} +7 -5
  114. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_chat.js +21 -0
  115. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_mini.js +21 -0
  116. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_nano.js +21 -0
  117. package/pathways/system/rest_streaming/{sys_openai_chat_o1.js → sys_openai_chat_o3.js} +6 -3
  118. package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +3 -0
  119. package/pathways/system/workspaces/run_workspace_prompt.js +99 -0
  120. package/pathways/vision.js +1 -1
  121. package/server/graphql.js +1 -1
  122. package/server/modelExecutor.js +8 -0
  123. package/server/pathwayResolver.js +166 -16
  124. package/server/pathwayResponseParser.js +16 -8
  125. package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
  126. package/server/plugins/claude3VertexPlugin.js +193 -45
  127. package/server/plugins/gemini15ChatPlugin.js +21 -0
  128. package/server/plugins/gemini15VisionPlugin.js +360 -0
  129. package/server/plugins/googleCsePlugin.js +94 -0
  130. package/server/plugins/grokVisionPlugin.js +365 -0
  131. package/server/plugins/modelPlugin.js +3 -1
  132. package/server/plugins/openAiChatPlugin.js +106 -13
  133. package/server/plugins/openAiVisionPlugin.js +42 -30
  134. package/server/resolver.js +28 -4
  135. package/server/rest.js +270 -53
  136. package/server/typeDef.js +1 -0
  137. package/tests/{mocks.js → helpers/mocks.js} +5 -2
  138. package/tests/{server.js → helpers/server.js} +2 -2
  139. package/tests/helpers/sseAssert.js +23 -0
  140. package/tests/helpers/sseClient.js +73 -0
  141. package/tests/helpers/subscriptionAssert.js +11 -0
  142. package/tests/helpers/subscriptions.js +113 -0
  143. package/tests/{sublong.srt → integration/features/translate/sublong.srt} +4543 -4543
  144. package/tests/integration/features/translate/translate_chunking_stream.test.js +100 -0
  145. package/tests/{translate_srt.test.js → integration/features/translate/translate_srt.test.js} +2 -2
  146. package/tests/integration/graphql/async/stream/agentic.test.js +477 -0
  147. package/tests/integration/graphql/async/stream/subscription_streaming.test.js +62 -0
  148. package/tests/integration/graphql/async/stream/sys_entity_start_streaming.test.js +71 -0
  149. package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +56 -0
  150. package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +66 -0
  151. package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +56 -0
  152. package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +72 -0
  153. package/tests/integration/graphql/features/google/sysToolGoogleSearch.test.js +96 -0
  154. package/tests/integration/graphql/features/grok/grok.test.js +688 -0
  155. package/tests/integration/graphql/features/grok/grok_x_search_tool.test.js +354 -0
  156. package/tests/{main.test.js → integration/graphql/features/main.test.js} +1 -1
  157. package/tests/{call_tools.test.js → integration/graphql/features/tools/call_tools.test.js} +2 -2
  158. package/tests/{vision.test.js → integration/graphql/features/vision/vision.test.js} +1 -1
  159. package/tests/integration/graphql/subscriptions/connection.test.js +26 -0
  160. package/tests/{openai_api.test.js → integration/rest/oai/openai_api.test.js} +63 -238
  161. package/tests/integration/rest/oai/tool_calling_api.test.js +343 -0
  162. package/tests/integration/rest/oai/tool_calling_streaming.test.js +85 -0
  163. package/tests/integration/rest/vendors/claude_streaming.test.js +47 -0
  164. package/tests/integration/rest/vendors/claude_tool_calling_streaming.test.js +75 -0
  165. package/tests/integration/rest/vendors/gemini_streaming.test.js +47 -0
  166. package/tests/integration/rest/vendors/gemini_tool_calling_streaming.test.js +75 -0
  167. package/tests/integration/rest/vendors/grok_streaming.test.js +55 -0
  168. package/tests/integration/rest/vendors/grok_tool_calling_streaming.test.js +75 -0
  169. package/tests/{azureAuthTokenHelper.test.js → unit/core/azureAuthTokenHelper.test.js} +1 -1
  170. package/tests/{chunkfunction.test.js → unit/core/chunkfunction.test.js} +2 -2
  171. package/tests/{config.test.js → unit/core/config.test.js} +3 -3
  172. package/tests/{encodeCache.test.js → unit/core/encodeCache.test.js} +1 -1
  173. package/tests/{fastLruCache.test.js → unit/core/fastLruCache.test.js} +1 -1
  174. package/tests/{handleBars.test.js → unit/core/handleBars.test.js} +1 -1
  175. package/tests/{memoryfunction.test.js → unit/core/memoryfunction.test.js} +2 -2
  176. package/tests/unit/core/mergeResolver.test.js +952 -0
  177. package/tests/{parser.test.js → unit/core/parser.test.js} +3 -3
  178. package/tests/unit/core/pathwayResolver.test.js +187 -0
  179. package/tests/{requestMonitor.test.js → unit/core/requestMonitor.test.js} +1 -1
  180. package/tests/{requestMonitorDurationEstimator.test.js → unit/core/requestMonitorDurationEstimator.test.js} +1 -1
  181. package/tests/{truncateMessages.test.js → unit/core/truncateMessages.test.js} +3 -3
  182. package/tests/{util.test.js → unit/core/util.test.js} +1 -1
  183. package/tests/{apptekTranslatePlugin.test.js → unit/plugins/apptekTranslatePlugin.test.js} +3 -3
  184. package/tests/{azureFoundryAgents.test.js → unit/plugins/azureFoundryAgents.test.js} +136 -1
  185. package/tests/{claude3VertexPlugin.test.js → unit/plugins/claude3VertexPlugin.test.js} +32 -10
  186. package/tests/{claude3VertexToolConversion.test.js → unit/plugins/claude3VertexToolConversion.test.js} +3 -3
  187. package/tests/unit/plugins/googleCsePlugin.test.js +111 -0
  188. package/tests/unit/plugins/grokVisionPlugin.test.js +1392 -0
  189. package/tests/{modelPlugin.test.js → unit/plugins/modelPlugin.test.js} +3 -3
  190. package/tests/{multimodal_conversion.test.js → unit/plugins/multimodal_conversion.test.js} +4 -4
  191. package/tests/{openAiChatPlugin.test.js → unit/plugins/openAiChatPlugin.test.js} +13 -4
  192. package/tests/{openAiToolPlugin.test.js → unit/plugins/openAiToolPlugin.test.js} +35 -27
  193. package/tests/{tokenHandlingTests.test.js → unit/plugins/tokenHandlingTests.test.js} +5 -5
  194. package/tests/{translate_apptek.test.js → unit/plugins/translate_apptek.test.js} +3 -3
  195. package/tests/{streaming.test.js → unit/plugins.streaming/plugin_stream_events.test.js} +19 -58
  196. package/helper-apps/mogrt-handler/tests/test-files/test.gif +0 -1
  197. package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +0 -1
  198. package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +0 -1
  199. package/pathways/system/rest_streaming/sys_openai_chat_gpt4.js +0 -19
  200. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_32.js +0 -19
  201. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_turbo.js +0 -19
  202. package/pathways/system/workspaces/run_claude35_sonnet.js +0 -21
  203. package/pathways/system/workspaces/run_claude3_haiku.js +0 -20
  204. package/pathways/system/workspaces/run_gpt35turbo.js +0 -20
  205. package/pathways/system/workspaces/run_gpt4.js +0 -20
  206. package/pathways/system/workspaces/run_gpt4_32.js +0 -20
  207. package/tests/agentic.test.js +0 -256
  208. package/tests/pathwayResolver.test.js +0 -78
  209. package/tests/subscription.test.js +0 -387
  210. /package/tests/{subchunk.srt → integration/features/translate/subchunk.srt} +0 -0
  211. /package/tests/{subhorizontal.srt → integration/features/translate/subhorizontal.srt} +0 -0
@@ -1,7 +1,7 @@
1
1
  import test from 'ava';
2
- import * as parser from '../server/parser.js';
3
- import * as pathwayTools from '../lib/pathwayTools.js';
4
- import serverFactory from '../index.js';
2
+ import * as parser from '../../../server/parser.js';
3
+ import * as pathwayTools from '../../../lib/pathwayTools.js';
4
+ import serverFactory from '../../../index.js';
5
5
 
6
6
  let testServer;
7
7
 
@@ -0,0 +1,187 @@
1
+ import test from 'ava';
2
+ import { PathwayResolver } from '../../../server/pathwayResolver.js';
3
+ import sinon from 'sinon';
4
+ import { mockConfig, mockPathwayString, mockModelEndpoints } from '../../helpers/mocks.js';
5
+
6
+ const mockPathway = mockPathwayString;
7
+ mockPathway.useInputChunking = false;
8
+ mockPathway.prompt = 'What is AI?';
9
+
10
+ const mockArgs = {
11
+ text: 'Artificial intelligence',
12
+ };
13
+
14
+ test.beforeEach((t) => {
15
+ t.context.pathwayResolver = new PathwayResolver({
16
+ config: mockConfig,
17
+ pathway: mockPathway,
18
+ args: mockArgs,
19
+ endpoints: mockModelEndpoints,
20
+ });
21
+ });
22
+
23
+ test('constructor initializes properties correctly', (t) => {
24
+ const resolver = t.context.pathwayResolver;
25
+ t.deepEqual(resolver.config, mockConfig);
26
+ t.deepEqual(resolver.pathway, mockPathway);
27
+ t.deepEqual(resolver.args, mockArgs);
28
+ t.is(resolver.useInputChunking, mockPathway.useInputChunking);
29
+ t.is(typeof resolver.requestId, 'string');
30
+ });
31
+
32
+ test('resolve returns request id when async is true', async (t) => {
33
+ const resolver = t.context.pathwayResolver;
34
+ const requestId = await resolver.resolve({ ...mockArgs, async: true });
35
+ t.is(typeof requestId, 'string');
36
+ t.is(requestId, resolver.requestId);
37
+ });
38
+
39
+ test('resolve calls promptAndParse when async is false', async (t) => {
40
+ const resolver = t.context.pathwayResolver;
41
+ const promptAndParseStub = sinon.stub(resolver, 'promptAndParse').returns(Promise.resolve('test-result'));
42
+
43
+ const result = await resolver.resolve(mockArgs);
44
+ t.true(promptAndParseStub.calledOnce);
45
+ t.is(result, 'test-result');
46
+ });
47
+
48
+ test('processInputText returns input text if no chunking', (t) => {
49
+ const resolver = t.context.pathwayResolver;
50
+ const text = 'This is a test input text';
51
+ const result = resolver.processInputText(text);
52
+ t.deepEqual(result, [text]);
53
+ });
54
+
55
+ test('applyPromptsSerially returns result of last prompt', async (t) => {
56
+ const resolver = t.context.pathwayResolver;
57
+ const text = 'This is a test input text';
58
+ const applyPromptStub = sinon.stub(resolver, 'applyPrompt');
59
+ applyPromptStub.onCall(0).returns(Promise.resolve('result1'));
60
+ applyPromptStub.onCall(1).returns(Promise.resolve('result2'));
61
+
62
+ resolver.pathwayPrompt = ['prompt1', 'prompt2'];
63
+ const result = await resolver.applyPromptsSerially(text, mockArgs);
64
+
65
+ t.is(result, 'result2');
66
+ });
67
+
68
+ test('processRequest returns empty result when input text is empty', async (t) => {
69
+ const resolver = t.context.pathwayResolver;
70
+ const text = '';
71
+ const processRequestStub = sinon.stub(resolver, 'processRequest').returns(Promise.resolve(''));
72
+
73
+ await resolver.resolve({ ...mockArgs, text });
74
+
75
+ t.true(processRequestStub.calledOnce);
76
+ const returnValue = await processRequestStub.firstCall.returnValue;
77
+ t.is(returnValue, text);
78
+ });
79
+
80
+ test('swapModel successfully changes model', (t) => {
81
+ const resolver = t.context.pathwayResolver;
82
+ const originalModelName = resolver.modelName;
83
+ const originalModel = resolver.model;
84
+ const originalModelExecutor = resolver.modelExecutor;
85
+
86
+ // Mock the getChunkMaxTokenLength method to avoid complex calculations
87
+ const getChunkMaxTokenLengthStub = sinon.stub(resolver, 'getChunkMaxTokenLength').returns(1000);
88
+
89
+ // Find a different model name from the mock endpoints
90
+ const availableModels = Object.keys(mockModelEndpoints);
91
+ const newModelName = availableModels.find(name => name !== originalModelName) || availableModels[0];
92
+
93
+ resolver.swapModel(newModelName);
94
+
95
+ t.not(resolver.modelName, originalModelName);
96
+ t.is(resolver.modelName, newModelName);
97
+ t.not(resolver.model, originalModel);
98
+ t.is(resolver.model, mockModelEndpoints[newModelName]);
99
+ t.not(resolver.modelExecutor, originalModelExecutor);
100
+ t.true(getChunkMaxTokenLengthStub.calledOnce);
101
+
102
+ getChunkMaxTokenLengthStub.restore();
103
+ });
104
+
105
+ test('swapModel throws error for non-existent model', (t) => {
106
+ const resolver = t.context.pathwayResolver;
107
+
108
+ t.throws(() => {
109
+ resolver.swapModel('non-existent-model');
110
+ }, { message: 'Model non-existent-model not found in config' });
111
+ });
112
+
113
+ test('swapModel logs warning about model change', (t) => {
114
+ const resolver = t.context.pathwayResolver;
115
+ const logWarningStub = sinon.stub(resolver, 'logWarning');
116
+
117
+ // Find a different model name from the mock endpoints
118
+ const availableModels = Object.keys(mockModelEndpoints);
119
+ const newModelName = availableModels.find(name => name !== resolver.modelName) || availableModels[0];
120
+
121
+ resolver.swapModel(newModelName);
122
+
123
+ t.true(logWarningStub.calledWith(`Model swapped to ${newModelName}`));
124
+
125
+ logWarningStub.restore();
126
+ });
127
+
128
+ test('promptAndParse swaps model when model is specified in args', async (t) => {
129
+ const resolver = t.context.pathwayResolver;
130
+ const swapModelStub = sinon.stub(resolver, 'swapModel');
131
+ const processRequestStub = sinon.stub(resolver, 'processRequest').returns(Promise.resolve('test result'));
132
+
133
+ // Mock the response parser to return the result directly
134
+ const parseStub = sinon.stub(resolver.responseParser, 'parse').returns(Promise.resolve('test result'));
135
+
136
+ const argsWithModel = { ...mockArgs, modelOverride: 'anotherModel' };
137
+
138
+ await resolver.promptAndParse(argsWithModel);
139
+
140
+ t.true(swapModelStub.calledWith('anotherModel'));
141
+
142
+ swapModelStub.restore();
143
+ processRequestStub.restore();
144
+ parseStub.restore();
145
+ });
146
+
147
+ test('promptAndParse does not swap model when model is same as current', async (t) => {
148
+ const resolver = t.context.pathwayResolver;
149
+ const swapModelStub = sinon.stub(resolver, 'swapModel');
150
+ const processRequestStub = sinon.stub(resolver, 'processRequest').returns(Promise.resolve('test result'));
151
+
152
+ // Mock the response parser to return the result directly
153
+ const parseStub = sinon.stub(resolver.responseParser, 'parse').returns(Promise.resolve('test result'));
154
+
155
+ const argsWithSameModel = { ...mockArgs, model: resolver.modelName };
156
+
157
+ await resolver.promptAndParse(argsWithSameModel);
158
+
159
+ t.false(swapModelStub.called);
160
+
161
+ swapModelStub.restore();
162
+ processRequestStub.restore();
163
+ parseStub.restore();
164
+ });
165
+
166
+ test('promptAndParse handles model swap errors gracefully', async (t) => {
167
+ const resolver = t.context.pathwayResolver;
168
+ const logErrorStub = sinon.stub(resolver, 'logError');
169
+ const processRequestStub = sinon.stub(resolver, 'processRequest').returns(Promise.resolve('test result'));
170
+
171
+ // Mock the response parser to return the result directly
172
+ const parseStub = sinon.stub(resolver.responseParser, 'parse').returns(Promise.resolve('test result'));
173
+
174
+ // Mock swapModel to throw an error
175
+ const swapModelStub = sinon.stub(resolver, 'swapModel').throws(new Error('Model not found'));
176
+
177
+ const argsWithInvalidModel = { ...mockArgs, modelOverride: 'invalidModel' };
178
+
179
+ await resolver.promptAndParse(argsWithInvalidModel);
180
+
181
+ t.true(logErrorStub.calledWith('Failed to swap model to invalidModel: Model not found'));
182
+
183
+ swapModelStub.restore();
184
+ logErrorStub.restore();
185
+ processRequestStub.restore();
186
+ parseStub.restore();
187
+ });
@@ -1,5 +1,5 @@
1
1
  import test from 'ava';
2
- import RequestMonitor from '../lib/requestMonitor.js'; // replace with actual path
2
+ import RequestMonitor from '../../../lib/requestMonitor.js'; // replace with actual path
3
3
 
4
4
  test('RequestMonitor: startCall', t => {
5
5
  const rm = new RequestMonitor();
@@ -1,5 +1,5 @@
1
1
  import test from 'ava';
2
- import RequestMonitor from '../lib/requestMonitor.js';
2
+ import RequestMonitor from '../../../lib/requestMonitor.js';
3
3
 
4
4
  test('add and get average request duration', async (t) => {
5
5
  const estimator = new RequestMonitor(5);
@@ -1,8 +1,8 @@
1
1
  // ModelPlugin.test.js
2
2
  import test from 'ava';
3
- import ModelPlugin from '../server/plugins/modelPlugin.js';
4
- import { encode } from '../lib/encodeCache.js';
5
- import { mockPathwayResolverString } from './mocks.js';
3
+ import ModelPlugin from '../../../server/plugins/modelPlugin.js';
4
+ import { encode } from '../../../lib/encodeCache.js';
5
+ import { mockPathwayResolverString } from '../../helpers/mocks.js';
6
6
 
7
7
  const { config, pathway, modelName, model } = mockPathwayResolverString;
8
8
 
@@ -2,7 +2,7 @@
2
2
  // Tests for utility functions in cortex/lib/util.js
3
3
 
4
4
  import test from 'ava';
5
- import { removeOldImageAndFileContent } from '../lib/util.js';
5
+ import { removeOldImageAndFileContent } from '../../../lib/util.js';
6
6
 
7
7
  // Test removeOldImageAndFileContent function
8
8
 
@@ -1,8 +1,8 @@
1
1
  import test from 'ava';
2
2
  import sinon from 'sinon';
3
- import ApptekTranslatePlugin from '../server/plugins/apptekTranslatePlugin.js';
4
- import { config } from '../config.js';
5
- import * as pathwayTools from '../lib/pathwayTools.js';
3
+ import ApptekTranslatePlugin from '../../../server/plugins/apptekTranslatePlugin.js';
4
+ import { config } from '../../../config.js';
5
+ import * as pathwayTools from '../../../lib/pathwayTools.js';
6
6
 
7
7
  // Mock pathway and model
8
8
  const mockPathway = {
@@ -1,6 +1,6 @@
1
1
  // azureFoundryAgents.test.js
2
2
  import test from 'ava';
3
- import AzureFoundryAgentsPlugin from '../server/plugins/azureFoundryAgentsPlugin.js';
3
+ import AzureFoundryAgentsPlugin from '../../../server/plugins/azureFoundryAgentsPlugin.js';
4
4
 
5
5
  test.beforeEach(t => {
6
6
  const mockPathway = {
@@ -87,6 +87,141 @@ test('should create correct request parameters', t => {
87
87
  t.is(result.stream, false);
88
88
  });
89
89
 
90
+ test('should use custom instructions from parameters', t => {
91
+ const { plugin } = t.context;
92
+ const text = 'Hello, can you help me?';
93
+ const customInstructions = 'You are a specialized search agent.';
94
+ const parameters = {
95
+ stream: false,
96
+ instructions: customInstructions
97
+ };
98
+ const prompt = {
99
+ context: 'You are helpful.',
100
+ examples: [],
101
+ messages: [{ role: 'user', content: text }]
102
+ };
103
+
104
+ plugin.baseUrl = 'https://archipelago-foundry-resource.services.ai.azure.com/api/projects/archipelago-foundry';
105
+ plugin.assistantId = 'asst_testid';
106
+ const result = plugin.getRequestParameters(text, parameters, prompt);
107
+
108
+ t.is(result.instructions, customInstructions);
109
+ });
110
+
111
+ test('should use custom tools from parameters', t => {
112
+ const { plugin } = t.context;
113
+ const text = 'Hello, can you help me?';
114
+ const customTools = [
115
+ {
116
+ type: "bing_grounding",
117
+ bing_grounding: {
118
+ search_configurations: [
119
+ {
120
+ connection_id: "test-connection-id",
121
+ count: 10,
122
+ freshness: "day",
123
+ market: "en-us",
124
+ set_lang: "en"
125
+ }
126
+ ]
127
+ }
128
+ }
129
+ ];
130
+ const parameters = {
131
+ stream: false,
132
+ tools: customTools
133
+ };
134
+ const prompt = {
135
+ context: 'You are helpful.',
136
+ examples: [],
137
+ messages: [{ role: 'user', content: text }]
138
+ };
139
+
140
+ plugin.baseUrl = 'https://archipelago-foundry-resource.services.ai.azure.com/api/projects/archipelago-foundry';
141
+ plugin.assistantId = 'asst_testid';
142
+ const result = plugin.getRequestParameters(text, parameters, prompt);
143
+
144
+ t.deepEqual(result.tools, customTools);
145
+ });
146
+
147
+ test('should use custom parallel_tool_calls from parameters', t => {
148
+ const { plugin } = t.context;
149
+ const text = 'Hello, can you help me?';
150
+ const parameters = {
151
+ stream: false,
152
+ parallel_tool_calls: false
153
+ };
154
+ const prompt = {
155
+ context: 'You are helpful.',
156
+ examples: [],
157
+ messages: [{ role: 'user', content: text }]
158
+ };
159
+
160
+ plugin.baseUrl = 'https://archipelago-foundry-resource.services.ai.azure.com/api/projects/archipelago-foundry';
161
+ plugin.assistantId = 'asst_testid';
162
+ const result = plugin.getRequestParameters(text, parameters, prompt);
163
+
164
+ t.is(result.parallel_tool_calls, false);
165
+ });
166
+
167
+ test('should not include tools or parallel_tool_calls when not provided', t => {
168
+ const { plugin } = t.context;
169
+ const text = 'Hello, can you help me?';
170
+ const parameters = { stream: false };
171
+ const prompt = {
172
+ context: 'You are helpful.',
173
+ examples: [],
174
+ messages: [{ role: 'user', content: text }]
175
+ };
176
+
177
+ plugin.baseUrl = 'https://archipelago-foundry-resource.services.ai.azure.com/api/projects/archipelago-foundry';
178
+ plugin.assistantId = 'asst_testid';
179
+ const result = plugin.getRequestParameters(text, parameters, prompt);
180
+
181
+ t.falsy(result.tools);
182
+ t.falsy(result.parallel_tool_calls);
183
+ });
184
+
185
+ test('should handle Bing grounding tools with custom search parameters', t => {
186
+ const { plugin } = t.context;
187
+ const text = 'Hello, can you help me?';
188
+ const customTools = [
189
+ {
190
+ type: "bing_grounding",
191
+ bing_grounding: {
192
+ search_configurations: [
193
+ {
194
+ connection_id: "test-connection-id",
195
+ count: 10,
196
+ freshness: "day",
197
+ market: "en-gb",
198
+ set_lang: "en"
199
+ }
200
+ ]
201
+ }
202
+ }
203
+ ];
204
+ const parameters = {
205
+ stream: false,
206
+ tools: customTools
207
+ };
208
+ const prompt = {
209
+ context: 'You are helpful.',
210
+ examples: [],
211
+ messages: [{ role: 'user', content: text }]
212
+ };
213
+
214
+ plugin.baseUrl = 'https://archipelago-foundry-resource.services.ai.azure.com/api/projects/archipelago-foundry';
215
+ plugin.assistantId = 'asst_testid';
216
+ const result = plugin.getRequestParameters(text, parameters, prompt);
217
+
218
+ t.deepEqual(result.tools, customTools);
219
+ t.is(result.tools[0].bing_grounding.search_configurations[0].count, 10);
220
+ t.is(result.tools[0].bing_grounding.search_configurations[0].freshness, "day");
221
+ t.is(result.tools[0].bing_grounding.search_configurations[0].market, "en-gb");
222
+ t.is(result.tools[0].bing_grounding.search_configurations[0].set_lang, "en");
223
+ });
224
+
90
225
  test('should parse completed run response', t => {
91
226
  const { plugin } = t.context;
92
227
  const mockResponse = {
@@ -1,7 +1,7 @@
1
1
  import test from 'ava';
2
- import Claude3VertexPlugin from '../server/plugins/claude3VertexPlugin.js';
3
- import { mockPathwayResolverMessages } from './mocks.js';
4
- import { config } from '../config.js';
2
+ import Claude3VertexPlugin from '../../../server/plugins/claude3VertexPlugin.js';
3
+ import { mockPathwayResolverMessages } from '../../helpers/mocks.js';
4
+ import { config } from '../../../config.js';
5
5
  import fs from 'fs';
6
6
  import path from 'path';
7
7
 
@@ -137,26 +137,48 @@ test('getRequestParameters with long message in chatHistory', async (t) => {
137
137
  test('parseResponse', (t) => {
138
138
  const plugin = new Claude3VertexPlugin(pathway, model);
139
139
 
140
+ // Test text content response
140
141
  const dataWithTextContent = {
141
142
  content: [
142
143
  { type: 'text', text: 'Hello, World!' }
143
- ]
144
+ ],
145
+ usage: { input_tokens: 10, output_tokens: 5 },
146
+ stop_reason: 'end_turn'
144
147
  };
145
148
  const resultWithTextContent = plugin.parseResponse(dataWithTextContent);
146
- t.is(resultWithTextContent, 'Hello, World!');
149
+ t.truthy(resultWithTextContent.output_text === 'Hello, World!');
150
+ t.truthy(resultWithTextContent.finishReason === 'stop');
151
+ t.truthy(resultWithTextContent.usage);
152
+ t.truthy(resultWithTextContent.metadata.model === plugin.modelName);
147
153
 
148
- const dataWithoutTextContent = {
154
+ // Test tool calls response
155
+ const dataWithToolCalls = {
149
156
  content: [
150
- { type: 'image', url: 'http://example.com/image.jpg' }
151
- ]
157
+ {
158
+ type: 'tool_use',
159
+ id: 'tool_1',
160
+ name: 'search_web',
161
+ input: { query: 'test search' }
162
+ }
163
+ ],
164
+ usage: { input_tokens: 15, output_tokens: 8 },
165
+ stop_reason: 'tool_use'
152
166
  };
153
- const resultWithoutTextContent = plugin.parseResponse(dataWithoutTextContent);
154
- t.deepEqual(resultWithoutTextContent, dataWithoutTextContent);
167
+ const resultWithToolCalls = plugin.parseResponse(dataWithToolCalls);
168
+ t.truthy(resultWithToolCalls.output_text === '');
169
+ t.truthy(resultWithToolCalls.finishReason === 'tool_calls');
170
+ t.truthy(resultWithToolCalls.toolCalls);
171
+ t.truthy(resultWithToolCalls.toolCalls.length === 1);
172
+ t.truthy(resultWithToolCalls.toolCalls[0].id === 'tool_1');
173
+ t.truthy(resultWithToolCalls.toolCalls[0].function.name === 'search_web');
174
+ t.truthy(resultWithToolCalls.toolCalls[0].function.arguments === '{"query":"test search"}');
155
175
 
176
+ // Test data without content (should return original data)
156
177
  const dataWithoutContent = {};
157
178
  const resultWithoutContent = plugin.parseResponse(dataWithoutContent);
158
179
  t.deepEqual(resultWithoutContent, dataWithoutContent);
159
180
 
181
+ // Test null data (should return null)
160
182
  const dataNull = null;
161
183
  const resultNull = plugin.parseResponse(dataNull);
162
184
  t.is(resultNull, dataNull);
@@ -1,7 +1,7 @@
1
1
  import test from 'ava';
2
- import Claude3VertexPlugin from '../server/plugins/claude3VertexPlugin.js';
3
- import { mockPathwayResolverMessages } from './mocks.js';
4
- import { config } from '../config.js';
2
+ import Claude3VertexPlugin from '../../../server/plugins/claude3VertexPlugin.js';
3
+ import { mockPathwayResolverMessages } from '../../helpers/mocks.js';
4
+ import { config } from '../../../config.js';
5
5
 
6
6
  const { pathway, modelName, model } = mockPathwayResolverMessages;
7
7
 
@@ -0,0 +1,111 @@
1
+ import test from 'ava';
2
+ import sinon from 'sinon';
3
+
4
+ const mockPathway = {
5
+ name: 'google_cse',
6
+ temperature: 0.0,
7
+ prompt: '',
8
+ };
9
+
10
+ const mockModel = {
11
+ name: 'google-cse',
12
+ type: 'GOOGLE-CSE',
13
+ url: 'https://www.googleapis.com/customsearch/v1',
14
+ headers: { 'Content-Type': 'application/json' },
15
+ requestsPerSecond: 10,
16
+ maxTokenLength: 200000,
17
+ };
18
+
19
+ test.beforeEach(async t => {
20
+ t.context.sandbox = sinon.createSandbox();
21
+ t.context.originalEnv = { ...process.env };
22
+ process.env.OPENAI_API_KEY = 'test-openai-key';
23
+ process.env.GOOGLE_CSE_KEY = 'test-google-key';
24
+ process.env.GOOGLE_CSE_CX = 'test-google-cx';
25
+ const module = await import('../../../server/plugins/googleCsePlugin.js');
26
+ const GoogleCsePlugin = module.default;
27
+ t.context.plugin = new GoogleCsePlugin(mockPathway, mockModel);
28
+ });
29
+
30
+ test.afterEach.always(t => {
31
+ t.context.sandbox.restore();
32
+ process.env = t.context.originalEnv;
33
+ });
34
+
35
+ test('getRequestParameters builds query params correctly', t => {
36
+ const { plugin } = t.context;
37
+ const text = 'pokemon';
38
+ const parameters = {
39
+ q: 'pokemon cards',
40
+ num: 5,
41
+ start: 2,
42
+ safe: 'active',
43
+ dateRestrict: 'w1',
44
+ siteSearch: 'example.com',
45
+ siteSearchFilter: 'i',
46
+ searchType: 'image',
47
+ gl: 'us',
48
+ hl: 'en',
49
+ lr: 'lang_en',
50
+ sort: 'date',
51
+ exactTerms: 'pikachu',
52
+ excludeTerms: 'fake',
53
+ orTerms: 'deck,booster',
54
+ fileType: 'pdf',
55
+ cx: 'override-cx',
56
+ };
57
+
58
+ const result = plugin.getRequestParameters(text, parameters, {});
59
+
60
+ t.deepEqual(result, {
61
+ data: [],
62
+ params: {
63
+ key: 'test-google-key',
64
+ cx: 'override-cx',
65
+ q: 'pokemon cards',
66
+ num: 5,
67
+ start: 2,
68
+ safe: 'active',
69
+ dateRestrict: 'w1',
70
+ siteSearch: 'example.com',
71
+ siteSearchFilter: 'i',
72
+ searchType: 'image',
73
+ gl: 'us',
74
+ hl: 'en',
75
+ lr: 'lang_en',
76
+ sort: 'date',
77
+ exactTerms: 'pikachu',
78
+ excludeTerms: 'fake',
79
+ orTerms: 'deck,booster',
80
+ fileType: 'pdf',
81
+ }
82
+ });
83
+ });
84
+
85
+ test('execute sets method GET and calls executeRequest', async t => {
86
+ const { plugin } = t.context;
87
+ const spy = t.context.sandbox.stub(plugin, 'executeRequest').resolves('{"items": []}');
88
+
89
+ const cortexRequest = {
90
+ data: null,
91
+ params: null,
92
+ method: null,
93
+ url: mockModel.url,
94
+ };
95
+
96
+ const res = await plugin.execute('pokemon', { q: 'pokemon' }, {}, cortexRequest);
97
+
98
+ t.is(res, '{"items": []}');
99
+ t.true(spy.calledOnce);
100
+ const calledWith = spy.firstCall.args[0];
101
+ t.is(calledWith.method, 'GET');
102
+ t.is(calledWith.url, 'https://www.googleapis.com/customsearch/v1');
103
+ t.deepEqual(calledWith.params.q, 'pokemon');
104
+ });
105
+
106
+ test('parseResponse returns JSON string', t => {
107
+ const { plugin } = t.context;
108
+ const data = { items: [{ link: 'https://example.com' }] };
109
+ const res = plugin.parseResponse(data);
110
+ t.is(res, JSON.stringify(data));
111
+ });