@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
@@ -0,0 +1,688 @@
1
+ // grok.test.js
2
+ // This file contains e2e connection tests for Grok models (actual API calls)
3
+
4
+ import test from 'ava';
5
+ import serverFactory from '../../../../../index.js';
6
+
7
+ let testServer;
8
+
9
+ // Set default timeout for all tests in this file (60 seconds)
10
+ test.beforeEach(async t => {
11
+ t.timeout(60000);
12
+ });
13
+
14
+ test.before(async () => {
15
+ const { server, startServer } = await serverFactory();
16
+ startServer && await startServer();
17
+ testServer = server;
18
+ });
19
+
20
+ test.after.always('cleanup', async () => {
21
+ if (testServer) {
22
+ await testServer.stop();
23
+ }
24
+ });
25
+
26
+ // Real API tests - these will call the actual Grok model
27
+ test('make a chat_jarvis API call to Grok 3', async t => {
28
+
29
+ const response = await testServer.executeOperation({
30
+ query: `
31
+ query TestGrokDirect($chatHistory: [MultiMessage], $model: String) {
32
+ chat_jarvis(chatHistory: $chatHistory, model: $model) {
33
+ result
34
+ errors
35
+ }
36
+ }
37
+ `,
38
+ variables: {
39
+ chatHistory: [
40
+ {
41
+ role: 'user',
42
+ content: 'Hi there! To whom am I speaking? Can you tell me what model you are running on right now?'
43
+ }
44
+ ],
45
+ model: 'xai-grok-3'
46
+ }
47
+ });
48
+
49
+ t.is(response.body?.singleResult?.errors, undefined);
50
+ const result = response.body?.singleResult?.data?.chat_jarvis?.result;
51
+ t.truthy(result, 'Should have a result');
52
+ t.true(result.length > 0, 'Should have a non-empty result');
53
+ });
54
+
55
+ test('make a chat_jarvis API call to Grok 4', async t => {
56
+
57
+ const response = await testServer.executeOperation({
58
+ query: `
59
+ query TestGrokDirect($chatHistory: [MultiMessage], $model: String) {
60
+ chat_jarvis(chatHistory: $chatHistory, model: $model) {
61
+ result
62
+ errors
63
+ }
64
+ }
65
+ `,
66
+ variables: {
67
+ chatHistory: [
68
+ {
69
+ role: 'user',
70
+ content: 'Hi there! To whom am I speaking? Can you tell me what model you are running on right now?'
71
+ }
72
+ ],
73
+ model: 'xai-grok-4-fast-reasoning'
74
+ }
75
+ });
76
+
77
+ t.is(response.body?.singleResult?.errors, undefined);
78
+ const result = response.body?.singleResult?.data?.chat_jarvis?.result;
79
+ t.truthy(result, 'Should have a result');
80
+ t.true(result.length > 0, 'Should have a non-empty result');
81
+ });
82
+
83
+ test('test grok live search pathway - simple', async t => {
84
+
85
+ const response = await testServer.executeOperation({
86
+ query: `
87
+ query TestGrokWebSearch($text: String) {
88
+ grok_live_search(text: $text) {
89
+ result
90
+ errors
91
+ resultData
92
+ }
93
+ }
94
+ `,
95
+ variables: {
96
+ text: 'Provide me a digest of world news for last week.'
97
+ }
98
+ });
99
+
100
+ t.is(response.body?.singleResult?.errors, undefined);
101
+ const data = response.body?.singleResult?.data?.grok_live_search;
102
+ const result = data?.result;
103
+ const resultData = data?.resultData;
104
+
105
+ t.truthy(result, 'Should have a result');
106
+ t.true(result.length > 0, 'Should have a non-empty result');
107
+
108
+ // Check for Live Search data in resultData
109
+ if (resultData) {
110
+ try {
111
+ const resultDataObject = JSON.parse(resultData);
112
+
113
+ if (resultDataObject.citations) {
114
+ t.true(Array.isArray(resultDataObject.citations), 'Citations should be an array');
115
+ }
116
+ } catch (error) {
117
+ t.fail('Failed to parse resultData');
118
+ }
119
+ }
120
+ });
121
+
122
+ // Test Grok 4 through the vision pathway for multimodal capabilities
123
+ test('should execute Grok 4 through vision pathway', async t => {
124
+
125
+ const response = await testServer.executeOperation({
126
+ query: `
127
+ query TestGrokVision($text: String, $chatHistory: [MultiMessage], $model: String) {
128
+ vision(text: $text, chatHistory: $chatHistory, model: $model) {
129
+ result
130
+ errors
131
+ }
132
+ }
133
+ `,
134
+ variables: {
135
+ text: 'Describe this image briefly:',
136
+ chatHistory: [
137
+ {
138
+ role: 'user',
139
+ content: [
140
+ '{"type": "text", "text": "Describe this image briefly:"}',
141
+ '{"type":"image_url","image_url":{"url":"https://static.toiimg.com/thumb/msid-102827471,width-1280,height-720,resizemode-4/102827471.jpg"}}'
142
+ ]
143
+ }
144
+ ],
145
+ model: 'xai-grok-4'
146
+ }
147
+ });
148
+
149
+ t.is(response.body?.singleResult?.errors, undefined);
150
+ const result = response.body?.singleResult?.data?.vision?.result;
151
+
152
+ if (result) {
153
+ t.true(result.length > 0, 'Should have a non-empty result');
154
+ } else {
155
+ // If no result, it might be due to API issues (400 error, auth issues, etc.)
156
+ const errors = response.body?.singleResult?.data?.vision?.errors;
157
+ if (errors) {
158
+ t.fail('Should have no errors');
159
+ }
160
+ }
161
+ });
162
+
163
+ // Test Grok Live Search functionality
164
+ test('should execute Live Search with X platform search', async t => {
165
+
166
+ const search_parameters = JSON.stringify({
167
+ mode: 'auto',
168
+ return_citations: true,
169
+ max_search_results: 10,
170
+ sources: [{type: 'web'}, {type: 'x'}]
171
+ });
172
+
173
+ const response = await testServer.executeOperation({
174
+ query: `
175
+ query TestGrokLiveSearch($text: String, $search_parameters: String) {
176
+ grok_live_search(text: $text, search_parameters: $search_parameters) {
177
+ result
178
+ errors
179
+ resultData
180
+ }
181
+ }
182
+ `,
183
+ variables: {
184
+ text: `What are the latest AI model releases from the last couple weeks?`,
185
+ search_parameters: search_parameters
186
+ }
187
+ });
188
+
189
+ t.is(response.body?.singleResult?.errors, undefined);
190
+ const data = response.body?.singleResult?.data?.grok_live_search;
191
+ const result = data?.result;
192
+ const resultData = data?.resultData;
193
+
194
+ t.truthy(result, 'Should have a result');
195
+ t.true(result.length > 0, 'Should have a non-empty result');
196
+
197
+ // Parse resultData for Live Search information
198
+ t.truthy(resultData, 'Should have resultData');
199
+ try {
200
+ const resultDataObject = JSON.parse(resultData);
201
+
202
+ // Parse and display citations
203
+ t.truthy(resultDataObject.citations, 'Should have citations array since return_citations is true');
204
+ t.true(Array.isArray(resultDataObject.citations), 'Citations should be an array');
205
+ t.true(resultDataObject.citations.length > 0, 'Should have at least one citation for successful search');
206
+
207
+ // Validate citation structure
208
+ resultDataObject.citations.forEach((citation, index) => {
209
+ t.truthy(citation.url, `Citation ${index} should have a URL`);
210
+ t.true(typeof citation.url === 'string', `Citation ${index} URL should be a string`);
211
+
212
+ // Validate URL format
213
+ try {
214
+ new URL(citation.url);
215
+ t.pass(`Citation ${index} has valid URL format: ${citation.url}`);
216
+ } catch (e) {
217
+ t.fail(`Citation ${index} has invalid URL format: ${citation.url}`);
218
+ }
219
+ });
220
+
221
+ // Parse and display usage data
222
+ t.truthy(resultDataObject.usage, 'Should have usage data');
223
+ if (resultDataObject.usage) {
224
+ // Usage should now be an array of objects
225
+ t.true(Array.isArray(resultDataObject.usage), 'Usage should be an array');
226
+ if (resultDataObject.usage.length > 0) {
227
+ const latestUsage = resultDataObject.usage[0]; // Most recent usage first
228
+ if (latestUsage.num_sources_used) {
229
+ t.true(typeof latestUsage.num_sources_used === 'number', 'num_sources_used should be a number');
230
+ }
231
+ }
232
+ }
233
+
234
+ } catch (error) {
235
+ t.fail('Failed to parse resultData');
236
+ }
237
+ });
238
+
239
+ // Test Live Search with web source parameters
240
+ test('should execute Live Search with web source parameters', async t => {
241
+
242
+ const search_parameters = JSON.stringify({
243
+ mode: 'auto',
244
+ return_citations: true,
245
+ max_search_results: 10,
246
+ sources: [{
247
+ type: 'web',
248
+ country: 'US',
249
+ excluded_websites: [],
250
+ allowed_websites: ['techcrunch.com', 'wired.com'],
251
+ safe_search: true
252
+ }]
253
+ });
254
+
255
+ const response = await testServer.executeOperation({
256
+ query: `
257
+ query TestGrokWebSearchParams($text: String, $search_parameters: String) {
258
+ grok_live_search(text: $text, search_parameters: $search_parameters) {
259
+ result
260
+ errors
261
+ resultData
262
+ }
263
+ }
264
+ `,
265
+ variables: {
266
+ text: 'What\'s going on with Elon Musk?',
267
+ search_parameters: search_parameters
268
+ }
269
+ });
270
+
271
+ t.is(response.body?.singleResult?.errors, undefined);
272
+ const data = response.body?.singleResult?.data?.grok_live_search;
273
+ const result = data?.result;
274
+ const resultData = data?.resultData;
275
+
276
+ t.truthy(result, 'Should have a result');
277
+ t.true(result.length > 0, 'Should have a non-empty result');
278
+
279
+ // Check for Live Search data in resultData and validate citations
280
+ if (resultData) {
281
+ try {
282
+ const resultDataObject = JSON.parse(resultData);
283
+
284
+ // Citations should exist since return_citations is true
285
+ t.truthy(resultDataObject.citations, 'Should have citations array since return_citations is true');
286
+ t.true(Array.isArray(resultDataObject.citations), 'Citations should be an array');
287
+
288
+ // Validate that citations exist and are from valid sources
289
+ t.true(resultDataObject.citations.length > 0, 'Should have at least one citation for successful search');
290
+
291
+ // Validate citation structure and URLs
292
+ resultDataObject.citations.forEach((citation, index) => {
293
+ t.truthy(citation.url, `Citation ${index} should have a URL`);
294
+ t.true(typeof citation.url === 'string', `Citation ${index} URL should be a string`);
295
+
296
+ // Validate URL format
297
+ try {
298
+ new URL(citation.url);
299
+ t.pass(`Citation ${index} has valid URL format: ${citation.url}`);
300
+ } catch (e) {
301
+ t.fail(`Citation ${index} has invalid URL format: ${citation.url}`);
302
+ }
303
+
304
+ // Additional validation for web sources - should be from allowed websites
305
+ if (citation.url.includes('techcrunch.com') || citation.url.includes('wired.com')) {
306
+ t.pass(`Citation ${index} is from allowed website: ${citation.url}`);
307
+ }
308
+ });
309
+ } catch (error) {
310
+ t.fail('Failed to parse resultData');
311
+ }
312
+ }
313
+ });
314
+
315
+ // Test Live Search with X source parameters
316
+ test('should execute Live Search with X source parameters', async t => {
317
+
318
+ const search_parameters = JSON.stringify({
319
+ mode: 'auto',
320
+ return_citations: true,
321
+ max_search_results: 10,
322
+ sources: [{
323
+ type: 'x',
324
+ included_x_handles: ['OpenAI', 'AnthropicAI', 'xai'],
325
+ post_favorite_count: 100,
326
+ post_view_count: 1000
327
+ }]
328
+ });
329
+
330
+ const response = await testServer.executeOperation({
331
+ query: `
332
+ query TestGrokXSearchParams($text: String, $search_parameters: String) {
333
+ grok_live_search(text: $text, search_parameters: $search_parameters) {
334
+ result
335
+ errors
336
+ resultData
337
+ }
338
+ }
339
+ `,
340
+ variables: {
341
+ text: 'What are the latest AI developments and announcements?',
342
+ search_parameters: search_parameters
343
+ }
344
+ });
345
+
346
+ t.is(response.body?.singleResult?.errors, undefined);
347
+ const data = response.body?.singleResult?.data?.grok_live_search;
348
+ const result = data?.result;
349
+ const resultData = data?.resultData;
350
+
351
+ t.truthy(result, 'Should have a result');
352
+ t.true(result.length > 0, 'Should have a non-empty result');
353
+
354
+ // Check for Live Search data in resultData
355
+ if (resultData) {
356
+ try {
357
+ const resultDataObject = JSON.parse(resultData);
358
+
359
+ // Citations should exist since return_citations is true
360
+ t.truthy(resultDataObject.citations, 'Should have citations array since return_citations is true');
361
+ t.true(Array.isArray(resultDataObject.citations), 'Citations should be an array');
362
+ t.true(resultDataObject.citations.length > 0, 'Should have at least one citation for successful search');
363
+
364
+ // Validate citation structure for X platform sources
365
+ resultDataObject.citations.forEach((citation, index) => {
366
+ t.truthy(citation.url, `Citation ${index} should have a URL`);
367
+ t.true(typeof citation.url === 'string', `Citation ${index} URL should be a string`);
368
+
369
+ // Validate URL format
370
+ try {
371
+ new URL(citation.url);
372
+ t.pass(`Citation ${index} has valid URL format: ${citation.url}`);
373
+ } catch (e) {
374
+ t.fail(`Citation ${index} has invalid URL format: ${citation.url}`);
375
+ }
376
+
377
+ // For X platform sources, URLs should typically be from x.com or twitter.com
378
+ if (citation.url.includes('x.com') || citation.url.includes('twitter.com')) {
379
+ t.pass(`Citation ${index} is from X/Twitter platform: ${citation.url}`);
380
+ }
381
+ });
382
+ } catch (error) {
383
+ t.fail('Failed to parse resultData');
384
+ }
385
+ }
386
+ });
387
+
388
+ // Test Live Search with news source parameters
389
+ test('should execute Live Search with news source parameters', async t => {
390
+
391
+ const search_parameters = JSON.stringify({
392
+ mode: 'auto',
393
+ return_citations: true,
394
+ max_search_results: 10,
395
+ sources: [{
396
+ type: 'news',
397
+ country: 'US',
398
+ excluded_websites: ['tabloid.com'],
399
+ safe_search: true
400
+ }]
401
+ });
402
+
403
+ const response = await testServer.executeOperation({
404
+ query: `
405
+ query TestGrokNewsSearchParams($text: String, $search_parameters: String) {
406
+ grok_live_search(text: $text, search_parameters: $search_parameters) {
407
+ result
408
+ errors
409
+ resultData
410
+ }
411
+ }
412
+ `,
413
+ variables: {
414
+ text: 'Breaking news about climate change',
415
+ search_parameters: search_parameters
416
+ }
417
+ });
418
+
419
+ t.is(response.body?.singleResult?.errors, undefined);
420
+ const data = response.body?.singleResult?.data?.grok_live_search;
421
+ const result = data?.result;
422
+ const resultData = data?.resultData;
423
+
424
+ t.truthy(result, 'Should have a result');
425
+ t.true(result.length > 0, 'Should have a non-empty result');
426
+
427
+ // Check for Live Search data in resultData
428
+ if (resultData) {
429
+ try {
430
+ const resultDataObject = JSON.parse(resultData);
431
+
432
+ // Citations should exist since return_citations is true
433
+ t.truthy(resultDataObject.citations, 'Should have citations array since return_citations is true');
434
+ t.true(Array.isArray(resultDataObject.citations), 'Citations should be an array');
435
+ t.true(resultDataObject.citations.length > 0, 'Should have at least one citation for successful search');
436
+
437
+ // Validate citation structure for news sources
438
+ resultDataObject.citations.forEach((citation, index) => {
439
+ t.truthy(citation.url, `Citation ${index} should have a URL`);
440
+ t.true(typeof citation.url === 'string', `Citation ${index} URL should be a string`);
441
+
442
+ // Validate URL format
443
+ try {
444
+ new URL(citation.url);
445
+ t.pass(`Citation ${index} has valid URL format: ${citation.url}`);
446
+ } catch (e) {
447
+ t.fail(`Citation ${index} has invalid URL format: ${citation.url}`);
448
+ }
449
+
450
+ // For news sources, URLs should not be from excluded websites
451
+ if (!citation.url.includes('tabloid.com')) {
452
+ t.pass(`Citation ${index} is not from excluded website: ${citation.url}`);
453
+ } else {
454
+ t.fail(`Citation ${index} should not be from excluded website tabloid.com: ${citation.url}`);
455
+ }
456
+ });
457
+ } catch (error) {
458
+ t.fail('Failed to parse resultData');
459
+ }
460
+ }
461
+ });
462
+
463
+ // Test Live Search with RSS source parameters
464
+ test('should execute Live Search with RSS source parameters', async t => {
465
+
466
+ const search_parameters = JSON.stringify({
467
+ mode: 'on',
468
+ return_citations: true,
469
+ max_search_results: 10,
470
+ sources: [{
471
+ type: 'rss',
472
+ links: ['https://www.aljazeera.com/xml/rss/all.xml']
473
+ }]
474
+ });
475
+
476
+ const response = await testServer.executeOperation({
477
+ query: `
478
+ query TestGrokRSSSearchParams($text: String, $search_parameters: String) {
479
+ grok_live_search(text: $text, search_parameters: $search_parameters) {
480
+ result
481
+ errors
482
+ resultData
483
+ }
484
+ }
485
+ `,
486
+ variables: {
487
+ text: 'What is the latest news in this feed?',
488
+ search_parameters: search_parameters
489
+ }
490
+ });
491
+
492
+ t.is(response.body?.singleResult?.errors, undefined);
493
+ const data = response.body?.singleResult?.data?.grok_live_search;
494
+ const result = data?.result;
495
+ const resultData = data?.resultData;
496
+
497
+ t.truthy(result, 'Should have a result');
498
+ t.true(result.length > 0, 'Should have a non-empty result');
499
+
500
+ // Check for Live Search data in resultData
501
+ if (resultData) {
502
+ try {
503
+ const resultDataObject = JSON.parse(resultData);
504
+
505
+ // Citations should exist since return_citations is true
506
+ t.truthy(resultDataObject.citations, 'Should have citations array since return_citations is true');
507
+ t.true(Array.isArray(resultDataObject.citations), 'Citations should be an array');
508
+ t.true(resultDataObject.citations.length > 0, 'Should have at least one citation for successful search');
509
+
510
+ // Validate citation structure for RSS sources
511
+ resultDataObject.citations.forEach((citation, index) => {
512
+ t.truthy(citation.url, `Citation ${index} should have a URL`);
513
+ t.true(typeof citation.url === 'string', `Citation ${index} URL should be a string`);
514
+
515
+ // Validate URL format
516
+ try {
517
+ new URL(citation.url);
518
+ t.pass(`Citation ${index} has valid URL format: ${citation.url}`);
519
+ } catch (e) {
520
+ t.fail(`Citation ${index} has invalid URL format: ${citation.url}`);
521
+ }
522
+
523
+ // For RSS sources, URLs should typically be from the RSS feed source
524
+ if (citation.url.includes('aljazeera.com')) {
525
+ t.pass(`Citation ${index} is from RSS feed source: ${citation.url}`);
526
+ }
527
+ });
528
+ } catch (error) {
529
+ t.fail('Failed to parse resultData');
530
+ }
531
+ }
532
+ });
533
+
534
+ // Test Live Search with date range parameters
535
+ test('should execute Live Search with date range parameters', async t => {
536
+
537
+ const fromDate = new Date();
538
+ fromDate.setDate(fromDate.getDate() - 7); // 7 days ago
539
+ const toDate = new Date();
540
+
541
+ const search_parameters = JSON.stringify({
542
+ mode: 'auto',
543
+ return_citations: true,
544
+ max_search_results: 10,
545
+ from_date: fromDate.toISOString().split('T')[0], // YYYY-MM-DD format
546
+ to_date: toDate.toISOString().split('T')[0],
547
+ sources: [{
548
+ type: 'web'
549
+ }]
550
+ });
551
+
552
+ const response = await testServer.executeOperation({
553
+ query: `
554
+ query TestGrokDateRangeSearch($text: String, $search_parameters: String) {
555
+ grok_live_search(text: $text, search_parameters: $search_parameters) {
556
+ result
557
+ errors
558
+ resultData
559
+ }
560
+ }
561
+ `,
562
+ variables: {
563
+ text: 'Recent AI announcements',
564
+ search_parameters: search_parameters
565
+ }
566
+ });
567
+
568
+ t.is(response.body?.singleResult?.errors, undefined);
569
+ const data = response.body?.singleResult?.data?.grok_live_search;
570
+ const result = data?.result;
571
+ const resultData = data?.resultData;
572
+
573
+ t.truthy(result, 'Should have a result');
574
+ t.true(result.length > 0, 'Should have a non-empty result');
575
+
576
+ // Check for Live Search data in resultData
577
+ if (resultData) {
578
+ try {
579
+ const resultDataObject = JSON.parse(resultData);
580
+
581
+ // Citations should exist since return_citations is true
582
+ t.truthy(resultDataObject.citations, 'Should have citations array since return_citations is true');
583
+ t.true(Array.isArray(resultDataObject.citations), 'Citations should be an array');
584
+ t.true(resultDataObject.citations.length > 0, 'Should have at least one citation for successful search');
585
+
586
+ // Validate citation structure for date range searches
587
+ resultDataObject.citations.forEach((citation, index) => {
588
+ t.truthy(citation.url, `Citation ${index} should have a URL`);
589
+ t.true(typeof citation.url === 'string', `Citation ${index} URL should be a string`);
590
+
591
+ // Validate URL format
592
+ try {
593
+ new URL(citation.url);
594
+ t.pass(`Citation ${index} has valid URL format: ${citation.url}`);
595
+ } catch (e) {
596
+ t.fail(`Citation ${index} has invalid URL format: ${citation.url}`);
597
+ }
598
+
599
+ // For date range searches, we can't validate specific date constraints
600
+ // but we can ensure the URLs are properly formed
601
+ t.pass(`Citation ${index} has properly formatted citation data`);
602
+ });
603
+ } catch (error) {
604
+ t.fail('Failed to parse resultData');
605
+ }
606
+ }
607
+ });
608
+
609
+ // Test Live Search with custom sources configuration
610
+ test('should execute Live Search with custom sources configuration', async t => {
611
+
612
+ const search_parameters = JSON.stringify({
613
+ mode: 'auto',
614
+ return_citations: true,
615
+ max_search_results: 10,
616
+ sources: [
617
+ {
618
+ type: 'web'
619
+ },
620
+ {
621
+ type: 'x'
622
+ }
623
+ ]
624
+ });
625
+
626
+ const response = await testServer.executeOperation({
627
+ query: `
628
+ query TestGrokCustomSources($text: String, $search_parameters: String) {
629
+ grok_live_search(text: $text, search_parameters: $search_parameters) {
630
+ result
631
+ errors
632
+ resultData
633
+ }
634
+ }
635
+ `,
636
+ variables: {
637
+ text: 'Latest developments in machine learning',
638
+ search_parameters: search_parameters
639
+ }
640
+ });
641
+
642
+ t.is(response.body?.singleResult?.errors, undefined);
643
+ const data = response.body?.singleResult?.data?.grok_live_search;
644
+ const result = data?.result;
645
+ const resultData = data?.resultData;
646
+
647
+ t.truthy(result, 'Should have a result');
648
+ t.true(result.length > 0, 'Should have a non-empty result');
649
+
650
+ // Check for Live Search data in resultData
651
+ if (resultData) {
652
+ try {
653
+ const resultDataObject = JSON.parse(resultData);
654
+
655
+ // Citations should exist since return_citations is true
656
+ t.truthy(resultDataObject.citations, 'Should have citations array since return_citations is true');
657
+ t.true(Array.isArray(resultDataObject.citations), 'Citations should be an array');
658
+ t.true(resultDataObject.citations.length > 0, 'Should have at least one citation for successful search');
659
+ t.true(resultDataObject.citations.length <= 20, 'Should respect max_search_results limit');
660
+
661
+ // Validate citation structure for custom sources (web + x)
662
+ resultDataObject.citations.forEach((citation, index) => {
663
+ t.truthy(citation.url, `Citation ${index} should have a URL`);
664
+ t.true(typeof citation.url === 'string', `Citation ${index} URL should be a string`);
665
+
666
+ // Validate URL format
667
+ try {
668
+ new URL(citation.url);
669
+ t.pass(`Citation ${index} has valid URL format: ${citation.url}`);
670
+ } catch (e) {
671
+ t.fail(`Citation ${index} has invalid URL format: ${citation.url}`);
672
+ }
673
+
674
+ // For custom sources (web + x), citations can be from various sources
675
+ t.pass(`Citation ${index} has properly formatted citation data`);
676
+ });
677
+
678
+ if (resultDataObject.usage && Array.isArray(resultDataObject.usage) && resultDataObject.usage.length > 0) {
679
+ const latestUsage = resultDataObject.usage[0]; // Most recent usage first
680
+ if (latestUsage.num_sources_used) {
681
+ t.true(typeof latestUsage.num_sources_used === 'number', 'num_sources_used should be a number');
682
+ }
683
+ }
684
+ } catch (error) {
685
+ t.fail('Failed to parse resultData');
686
+ }
687
+ }
688
+ });