@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,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
|
+
});
|