@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,1392 @@
|
|
|
1
|
+
// grokVisionPlugin.test.js
|
|
2
|
+
// This file contains direct plugin tests for GrokVisionPlugin (non-connecting tests)
|
|
3
|
+
|
|
4
|
+
import test from 'ava';
|
|
5
|
+
import GrokVisionPlugin from '../../../server/plugins/grokVisionPlugin.js';
|
|
6
|
+
import { safeJsonParse } from '../../../server/plugins/grokVisionPlugin.js';
|
|
7
|
+
|
|
8
|
+
test('should create GrokVisionPlugin instance', t => {
|
|
9
|
+
const mockPathway = {
|
|
10
|
+
name: 'test-pathway',
|
|
11
|
+
temperature: 0.7,
|
|
12
|
+
prompt: 'Test prompt'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const mockModel = {
|
|
16
|
+
name: 'xai-grok-3',
|
|
17
|
+
type: 'GROK-VISION',
|
|
18
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
19
|
+
headers: {
|
|
20
|
+
'Authorization': 'Bearer test-key',
|
|
21
|
+
'Content-Type': 'application/json'
|
|
22
|
+
},
|
|
23
|
+
params: {
|
|
24
|
+
model: 'grok-3-latest'
|
|
25
|
+
},
|
|
26
|
+
maxTokenLength: 131072,
|
|
27
|
+
maxReturnTokens: 4096
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
31
|
+
|
|
32
|
+
t.true(plugin instanceof GrokVisionPlugin);
|
|
33
|
+
t.is(plugin.modelName, 'xai-grok-3');
|
|
34
|
+
t.true(plugin.isMultiModal);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should handle Grok-specific parameters', async t => {
|
|
38
|
+
const mockPathway = {
|
|
39
|
+
name: 'test-pathway',
|
|
40
|
+
temperature: 0.7,
|
|
41
|
+
prompt: 'Test prompt'
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const mockModel = {
|
|
45
|
+
name: 'xai-grok-4',
|
|
46
|
+
type: 'GROK-VISION',
|
|
47
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
48
|
+
headers: {
|
|
49
|
+
'Authorization': 'Bearer test-key',
|
|
50
|
+
'Content-Type': 'application/json'
|
|
51
|
+
},
|
|
52
|
+
params: {
|
|
53
|
+
model: 'grok-4-0709'
|
|
54
|
+
},
|
|
55
|
+
maxTokenLength: 131072,
|
|
56
|
+
maxReturnTokens: 4096
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
60
|
+
|
|
61
|
+
const parameters = {
|
|
62
|
+
search_parameters: '{"mode": "auto"}'
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const requestParams = await plugin.getRequestParameters('test text', parameters, {});
|
|
66
|
+
|
|
67
|
+
t.truthy(requestParams.search_parameters);
|
|
68
|
+
t.is(requestParams.search_parameters.mode, 'auto');
|
|
69
|
+
// Vision parameters are handled in message content, not as top-level parameters
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('should handle all Live Search parameters correctly', async t => {
|
|
73
|
+
const mockPathway = {
|
|
74
|
+
name: 'test-pathway',
|
|
75
|
+
temperature: 0.7,
|
|
76
|
+
prompt: 'Test prompt'
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const mockModel = {
|
|
80
|
+
name: 'xai-grok-3',
|
|
81
|
+
type: 'GROK-VISION',
|
|
82
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
83
|
+
headers: {
|
|
84
|
+
'Authorization': 'Bearer test-key',
|
|
85
|
+
'Content-Type': 'application/json'
|
|
86
|
+
},
|
|
87
|
+
params: {
|
|
88
|
+
model: 'grok-3-latest'
|
|
89
|
+
},
|
|
90
|
+
maxTokenLength: 131072,
|
|
91
|
+
maxReturnTokens: 4096
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
95
|
+
|
|
96
|
+
// Test comprehensive Live Search parameters as JSON string
|
|
97
|
+
const parameters = {
|
|
98
|
+
search_parameters: JSON.stringify({
|
|
99
|
+
mode: 'on',
|
|
100
|
+
return_citations: true,
|
|
101
|
+
from_date: '2024-01-01',
|
|
102
|
+
to_date: '2024-12-31',
|
|
103
|
+
max_search_results: 15,
|
|
104
|
+
sources: [
|
|
105
|
+
{ type: 'web', country: 'US', excluded_websites: ['wikipedia.org'] },
|
|
106
|
+
{ type: 'x', included_x_handles: ['xai'], post_favorite_count: 1000 },
|
|
107
|
+
{ type: 'news', safe_search: false },
|
|
108
|
+
{ type: 'rss', links: ['https://status.x.ai/feed.xml'] }
|
|
109
|
+
]
|
|
110
|
+
})
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const requestParams = await plugin.getRequestParameters('Search for latest tech news', parameters, {});
|
|
114
|
+
|
|
115
|
+
// Verify all search_parameters are set correctly
|
|
116
|
+
t.truthy(requestParams.search_parameters, 'search_parameters should be set');
|
|
117
|
+
t.is(requestParams.search_parameters.mode, 'on', 'search mode should be on');
|
|
118
|
+
t.true(requestParams.search_parameters.return_citations, 'return_citations should be true');
|
|
119
|
+
t.is(requestParams.search_parameters.from_date, '2024-01-01', 'from_date should be set');
|
|
120
|
+
t.is(requestParams.search_parameters.to_date, '2024-12-31', 'to_date should be set');
|
|
121
|
+
t.is(requestParams.search_parameters.max_search_results, 15, 'max_search_results should be 15');
|
|
122
|
+
t.truthy(requestParams.search_parameters.sources, 'sources should be set');
|
|
123
|
+
t.is(requestParams.search_parameters.sources.length, 4, 'should have 4 sources');
|
|
124
|
+
|
|
125
|
+
// Verify web source
|
|
126
|
+
const webSource = requestParams.search_parameters.sources.find(s => s.type === 'web');
|
|
127
|
+
t.truthy(webSource, 'web source should exist');
|
|
128
|
+
t.is(webSource.country, 'US', 'web source country should be US');
|
|
129
|
+
t.deepEqual(webSource.excluded_websites, ['wikipedia.org'], 'web source excluded_websites should be set');
|
|
130
|
+
|
|
131
|
+
// Verify X source
|
|
132
|
+
const xSource = requestParams.search_parameters.sources.find(s => s.type === 'x');
|
|
133
|
+
t.truthy(xSource, 'x source should exist');
|
|
134
|
+
t.deepEqual(xSource.included_x_handles, ['xai'], 'x source included_x_handles should be set');
|
|
135
|
+
t.is(xSource.post_favorite_count, 1000, 'x source post_favorite_count should be 1000');
|
|
136
|
+
|
|
137
|
+
// Verify news source
|
|
138
|
+
const newsSource = requestParams.search_parameters.sources.find(s => s.type === 'news');
|
|
139
|
+
t.truthy(newsSource, 'news source should exist');
|
|
140
|
+
t.false(newsSource.safe_search, 'news source safe_search should be false');
|
|
141
|
+
|
|
142
|
+
// Verify RSS source
|
|
143
|
+
const rssSource = requestParams.search_parameters.sources.find(s => s.type === 'rss');
|
|
144
|
+
t.truthy(rssSource, 'rss source should exist');
|
|
145
|
+
t.deepEqual(rssSource.links, ['https://status.x.ai/feed.xml'], 'rss source links should be set');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('should handle individual source parameters correctly', async t => {
|
|
149
|
+
const mockPathway = {
|
|
150
|
+
name: 'test-pathway',
|
|
151
|
+
temperature: 0.7,
|
|
152
|
+
prompt: 'Test prompt'
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const mockModel = {
|
|
156
|
+
name: 'xai-grok-3',
|
|
157
|
+
type: 'GROK-VISION',
|
|
158
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
159
|
+
headers: {
|
|
160
|
+
'Authorization': 'Bearer test-key',
|
|
161
|
+
'Content-Type': 'application/json'
|
|
162
|
+
},
|
|
163
|
+
params: {
|
|
164
|
+
model: 'grok-3-latest'
|
|
165
|
+
},
|
|
166
|
+
maxTokenLength: 131072,
|
|
167
|
+
maxReturnTokens: 4096
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
171
|
+
|
|
172
|
+
// Test web source with allowed_websites
|
|
173
|
+
const webParameters = {
|
|
174
|
+
search_parameters: JSON.stringify({
|
|
175
|
+
mode: 'auto',
|
|
176
|
+
sources: [{ type: 'web', allowed_websites: ['x.ai', 'github.com'] }]
|
|
177
|
+
})
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const webRequestParams = await plugin.getRequestParameters('Search xAI website', webParameters, {});
|
|
181
|
+
const webSource = webRequestParams.search_parameters.sources[0];
|
|
182
|
+
t.deepEqual(webSource.allowed_websites, ['x.ai', 'github.com'], 'web source allowed_websites should be set');
|
|
183
|
+
|
|
184
|
+
// Test X source with excluded_handles and view count
|
|
185
|
+
const xParameters = {
|
|
186
|
+
search_parameters: JSON.stringify({
|
|
187
|
+
mode: 'auto',
|
|
188
|
+
sources: [{
|
|
189
|
+
type: 'x',
|
|
190
|
+
excluded_x_handles: ['spam_account'],
|
|
191
|
+
post_view_count: 50000
|
|
192
|
+
}]
|
|
193
|
+
})
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const xRequestParams = await plugin.getRequestParameters('Search X posts', xParameters, {});
|
|
197
|
+
const xSource = xRequestParams.search_parameters.sources[0];
|
|
198
|
+
t.deepEqual(xSource.excluded_x_handles, ['spam_account'], 'x source excluded_x_handles should be set');
|
|
199
|
+
t.is(xSource.post_view_count, 50000, 'x source post_view_count should be set');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('should handle date range parameters correctly', async t => {
|
|
203
|
+
const mockPathway = {
|
|
204
|
+
name: 'test-pathway',
|
|
205
|
+
temperature: 0.7,
|
|
206
|
+
prompt: 'Test prompt'
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const mockModel = {
|
|
210
|
+
name: 'xai-grok-3',
|
|
211
|
+
type: 'GROK-VISION',
|
|
212
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
213
|
+
headers: {
|
|
214
|
+
'Authorization': 'Bearer test-key',
|
|
215
|
+
'Content-Type': 'application/json'
|
|
216
|
+
},
|
|
217
|
+
params: {
|
|
218
|
+
model: 'grok-3-latest'
|
|
219
|
+
},
|
|
220
|
+
maxTokenLength: 131072,
|
|
221
|
+
maxReturnTokens: 4096
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
225
|
+
|
|
226
|
+
// Test with only from_date
|
|
227
|
+
const fromDateParameters = {
|
|
228
|
+
search_parameters: JSON.stringify({
|
|
229
|
+
mode: 'auto',
|
|
230
|
+
from_date: '2024-06-01'
|
|
231
|
+
})
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const fromDateRequestParams = await plugin.getRequestParameters('Search recent news', fromDateParameters, {});
|
|
235
|
+
t.is(fromDateRequestParams.search_parameters.from_date, '2024-06-01', 'from_date should be set');
|
|
236
|
+
t.is(fromDateRequestParams.search_parameters.to_date, undefined, 'to_date should not be set');
|
|
237
|
+
|
|
238
|
+
// Test with only to_date
|
|
239
|
+
const toDateParameters = {
|
|
240
|
+
search_parameters: JSON.stringify({
|
|
241
|
+
mode: 'auto',
|
|
242
|
+
to_date: '2024-12-31'
|
|
243
|
+
})
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const toDateRequestParams = await plugin.getRequestParameters('Search historical data', toDateParameters, {});
|
|
247
|
+
t.is(toDateRequestParams.search_parameters.to_date, '2024-12-31', 'to_date should be set');
|
|
248
|
+
t.is(toDateRequestParams.search_parameters.from_date, undefined, 'from_date should not be set');
|
|
249
|
+
|
|
250
|
+
// Test with both dates
|
|
251
|
+
const bothDatesParameters = {
|
|
252
|
+
search_parameters: JSON.stringify({
|
|
253
|
+
mode: 'auto',
|
|
254
|
+
from_date: '2024-01-01',
|
|
255
|
+
to_date: '2024-06-30'
|
|
256
|
+
})
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const bothDatesRequestParams = await plugin.getRequestParameters('Search specific period', bothDatesParameters, {});
|
|
260
|
+
t.is(bothDatesRequestParams.search_parameters.from_date, '2024-01-01', 'from_date should be set');
|
|
261
|
+
t.is(bothDatesRequestParams.search_parameters.to_date, '2024-06-30', 'to_date should be set');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test('should handle empty search_parameters correctly', async t => {
|
|
265
|
+
const mockPathway = {
|
|
266
|
+
name: 'test-pathway',
|
|
267
|
+
temperature: 0.7,
|
|
268
|
+
prompt: 'Test prompt'
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const mockModel = {
|
|
272
|
+
name: 'xai-grok-3',
|
|
273
|
+
type: 'GROK-VISION',
|
|
274
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
275
|
+
headers: {
|
|
276
|
+
'Authorization': 'Bearer test-key',
|
|
277
|
+
'Content-Type': 'application/json'
|
|
278
|
+
},
|
|
279
|
+
params: {
|
|
280
|
+
model: 'grok-3-latest'
|
|
281
|
+
},
|
|
282
|
+
maxTokenLength: 131072,
|
|
283
|
+
maxReturnTokens: 4096
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
287
|
+
|
|
288
|
+
// Test with empty search_parameters (should not be set)
|
|
289
|
+
const emptyParameters = {};
|
|
290
|
+
|
|
291
|
+
const emptyRequestParams = await plugin.getRequestParameters('Search with defaults', emptyParameters, {});
|
|
292
|
+
t.is(emptyRequestParams.search_parameters, undefined, 'search_parameters should not be set when not provided');
|
|
293
|
+
|
|
294
|
+
// Test with empty JSON string
|
|
295
|
+
const emptyJsonParameters = {
|
|
296
|
+
search_parameters: '{}'
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const emptyJsonRequestParams = await plugin.getRequestParameters('Search with empty json', emptyJsonParameters, {});
|
|
300
|
+
t.is(emptyJsonRequestParams.search_parameters, undefined, 'search_parameters should not be set when empty object provided');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Note: grok_live_search pathway test removed as the pathway file does not exist yet
|
|
304
|
+
|
|
305
|
+
test('should handle X.AI vision message structure correctly', async t => {
|
|
306
|
+
const mockPathway = {
|
|
307
|
+
name: 'test-pathway',
|
|
308
|
+
temperature: 0.7,
|
|
309
|
+
prompt: 'Test prompt'
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const mockModel = {
|
|
313
|
+
name: 'xai-grok-3',
|
|
314
|
+
type: 'GROK-VISION',
|
|
315
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
316
|
+
headers: {
|
|
317
|
+
'Authorization': 'Bearer test-key',
|
|
318
|
+
'Content-Type': 'application/json'
|
|
319
|
+
},
|
|
320
|
+
params: {
|
|
321
|
+
model: 'grok-3-latest'
|
|
322
|
+
},
|
|
323
|
+
maxTokenLength: 131072,
|
|
324
|
+
maxReturnTokens: 4096
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
328
|
+
|
|
329
|
+
// Test vision message with URL image
|
|
330
|
+
const visionMessages = [
|
|
331
|
+
{
|
|
332
|
+
role: 'user',
|
|
333
|
+
content: [
|
|
334
|
+
{
|
|
335
|
+
type: 'image_url',
|
|
336
|
+
image_url: {
|
|
337
|
+
url: 'https://example.com/image.jpg',
|
|
338
|
+
detail: 'high'
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
type: 'text',
|
|
343
|
+
text: 'What is in this image?'
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
}
|
|
347
|
+
];
|
|
348
|
+
|
|
349
|
+
const parameters = {};
|
|
350
|
+
|
|
351
|
+
const requestParams = await plugin.getRequestParameters('', parameters, {});
|
|
352
|
+
requestParams.messages = visionMessages;
|
|
353
|
+
|
|
354
|
+
// Verify vision message structure
|
|
355
|
+
t.is(requestParams.messages[0].content.length, 2, 'should have 2 content items');
|
|
356
|
+
t.is(requestParams.messages[0].content[0].type, 'image_url', 'first item should be image_url');
|
|
357
|
+
t.is(requestParams.messages[0].content[1].type, 'text', 'second item should be text');
|
|
358
|
+
t.is(requestParams.messages[0].content[0].image_url.detail, 'high', 'image detail should be high');
|
|
359
|
+
t.is(requestParams.messages[0].content[0].image_url.url, 'https://example.com/image.jpg', 'image URL should be set');
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test('should handle X.AI vision with base64 images', async t => {
|
|
363
|
+
const mockPathway = {
|
|
364
|
+
name: 'test-pathway',
|
|
365
|
+
temperature: 0.7,
|
|
366
|
+
prompt: 'Test prompt'
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const mockModel = {
|
|
370
|
+
name: 'xai-grok-3',
|
|
371
|
+
type: 'GROK-VISION',
|
|
372
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
373
|
+
headers: {
|
|
374
|
+
'Authorization': 'Bearer test-key',
|
|
375
|
+
'Content-Type': 'application/json'
|
|
376
|
+
},
|
|
377
|
+
params: {
|
|
378
|
+
model: 'grok-3-latest'
|
|
379
|
+
},
|
|
380
|
+
maxTokenLength: 131072,
|
|
381
|
+
maxReturnTokens: 4096
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
385
|
+
|
|
386
|
+
// Test vision message with base64 image
|
|
387
|
+
const base64Image = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=';
|
|
388
|
+
|
|
389
|
+
const visionMessages = [
|
|
390
|
+
{
|
|
391
|
+
role: 'user',
|
|
392
|
+
content: [
|
|
393
|
+
{
|
|
394
|
+
type: 'text',
|
|
395
|
+
text: 'What is in this image?'
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
type: 'image_url',
|
|
399
|
+
image_url: {
|
|
400
|
+
url: base64Image,
|
|
401
|
+
detail: 'auto'
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
]
|
|
405
|
+
}
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
const parameters = {};
|
|
409
|
+
|
|
410
|
+
const requestParams = await plugin.getRequestParameters('', parameters, {});
|
|
411
|
+
requestParams.messages = visionMessages;
|
|
412
|
+
|
|
413
|
+
// Verify base64 image message structure
|
|
414
|
+
t.is(requestParams.messages[0].content.length, 2, 'should have 2 content items');
|
|
415
|
+
t.is(requestParams.messages[0].content[0].type, 'text', 'first item should be text');
|
|
416
|
+
t.is(requestParams.messages[0].content[1].type, 'image_url', 'second item should be image_url');
|
|
417
|
+
t.is(requestParams.messages[0].content[1].image_url.detail, 'auto', 'image detail should be auto');
|
|
418
|
+
t.true(requestParams.messages[0].content[1].image_url.url.startsWith('data:image/jpeg;base64,'), 'should be base64 image');
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
test('should handle X.AI vision with multiple images', async t => {
|
|
422
|
+
const mockPathway = {
|
|
423
|
+
name: 'test-pathway',
|
|
424
|
+
temperature: 0.7,
|
|
425
|
+
prompt: 'Test prompt'
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
const mockModel = {
|
|
429
|
+
name: 'xai-grok-3',
|
|
430
|
+
type: 'GROK-VISION',
|
|
431
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
432
|
+
headers: {
|
|
433
|
+
'Authorization': 'Bearer test-key',
|
|
434
|
+
'Content-Type': 'application/json'
|
|
435
|
+
},
|
|
436
|
+
params: {
|
|
437
|
+
model: 'grok-3-latest'
|
|
438
|
+
},
|
|
439
|
+
maxTokenLength: 131072,
|
|
440
|
+
maxReturnTokens: 4096
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
444
|
+
|
|
445
|
+
// Test vision message with multiple images
|
|
446
|
+
const visionMessages = [
|
|
447
|
+
{
|
|
448
|
+
role: 'user',
|
|
449
|
+
content: [
|
|
450
|
+
{
|
|
451
|
+
type: 'text',
|
|
452
|
+
text: 'What are in these images?'
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
type: 'image_url',
|
|
456
|
+
image_url: {
|
|
457
|
+
url: 'https://example.com/image1.jpg',
|
|
458
|
+
detail: 'high'
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
type: 'image_url',
|
|
463
|
+
image_url: {
|
|
464
|
+
url: 'https://example.com/image2.jpg',
|
|
465
|
+
detail: 'low'
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
type: 'text',
|
|
470
|
+
text: 'Compare them.'
|
|
471
|
+
}
|
|
472
|
+
]
|
|
473
|
+
}
|
|
474
|
+
];
|
|
475
|
+
|
|
476
|
+
const parameters = {};
|
|
477
|
+
|
|
478
|
+
const requestParams = await plugin.getRequestParameters('', parameters, {});
|
|
479
|
+
requestParams.messages = visionMessages;
|
|
480
|
+
|
|
481
|
+
// Verify multiple images message structure
|
|
482
|
+
t.is(requestParams.messages[0].content.length, 4, 'should have 4 content items');
|
|
483
|
+
t.is(requestParams.messages[0].content[0].type, 'text', 'first item should be text');
|
|
484
|
+
t.is(requestParams.messages[0].content[1].type, 'image_url', 'second item should be image_url');
|
|
485
|
+
t.is(requestParams.messages[0].content[2].type, 'image_url', 'third item should be image_url');
|
|
486
|
+
t.is(requestParams.messages[0].content[3].type, 'text', 'fourth item should be text');
|
|
487
|
+
t.is(requestParams.messages[0].content[1].image_url.detail, 'high', 'first image detail should be high');
|
|
488
|
+
t.is(requestParams.messages[0].content[2].image_url.detail, 'low', 'second image detail should be low');
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
test('should parse Grok response with citations', t => {
|
|
492
|
+
const mockPathway = {
|
|
493
|
+
name: 'test-pathway',
|
|
494
|
+
temperature: 0.7,
|
|
495
|
+
prompt: 'Test prompt'
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const mockModel = {
|
|
499
|
+
name: 'xai-grok-4',
|
|
500
|
+
type: 'GROK-VISION',
|
|
501
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
502
|
+
headers: {
|
|
503
|
+
'Authorization': 'Bearer test-key',
|
|
504
|
+
'Content-Type': 'application/json'
|
|
505
|
+
},
|
|
506
|
+
params: {
|
|
507
|
+
model: 'grok-4-0709'
|
|
508
|
+
},
|
|
509
|
+
maxTokenLength: 131072,
|
|
510
|
+
maxReturnTokens: 4096
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
514
|
+
|
|
515
|
+
const mockResponse = {
|
|
516
|
+
choices: [{
|
|
517
|
+
message: {
|
|
518
|
+
role: 'assistant',
|
|
519
|
+
content: 'Test response'
|
|
520
|
+
}
|
|
521
|
+
}],
|
|
522
|
+
citations: [
|
|
523
|
+
'https://example.com'
|
|
524
|
+
]
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const result = plugin.parseResponse(mockResponse);
|
|
528
|
+
|
|
529
|
+
// Should return a CortexResponse object
|
|
530
|
+
t.is(result.output_text, 'Test response');
|
|
531
|
+
t.truthy(result.citations);
|
|
532
|
+
// Citations are created from URLs, so title is extracted from URL
|
|
533
|
+
t.is(result.citations[0].url, 'https://example.com');
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
test('should handle tool calls in response', t => {
|
|
537
|
+
const mockPathway = {
|
|
538
|
+
name: 'test-pathway',
|
|
539
|
+
temperature: 0.7,
|
|
540
|
+
prompt: 'Test prompt'
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const mockModel = {
|
|
544
|
+
name: 'xai-grok-4',
|
|
545
|
+
type: 'GROK-VISION',
|
|
546
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
547
|
+
headers: {
|
|
548
|
+
'Authorization': 'Bearer test-key',
|
|
549
|
+
'Content-Type': 'application/json'
|
|
550
|
+
},
|
|
551
|
+
params: {
|
|
552
|
+
model: 'grok-4-0709'
|
|
553
|
+
},
|
|
554
|
+
maxTokenLength: 131072,
|
|
555
|
+
maxReturnTokens: 4096
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
559
|
+
|
|
560
|
+
const mockResponse = {
|
|
561
|
+
choices: [{
|
|
562
|
+
message: {
|
|
563
|
+
role: 'assistant',
|
|
564
|
+
content: 'I will call a tool',
|
|
565
|
+
tool_calls: [
|
|
566
|
+
{
|
|
567
|
+
id: 'call_123',
|
|
568
|
+
type: 'function',
|
|
569
|
+
function: {
|
|
570
|
+
name: 'test_function',
|
|
571
|
+
arguments: '{"param": "value"}'
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
]
|
|
575
|
+
}
|
|
576
|
+
}]
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
const result = plugin.parseResponse(mockResponse);
|
|
580
|
+
|
|
581
|
+
t.is(result.output_text, 'I will call a tool');
|
|
582
|
+
t.truthy(result.toolCalls);
|
|
583
|
+
t.is(result.toolCalls[0].function.name, 'test_function');
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
test('should handle string response from parent', t => {
|
|
587
|
+
const mockPathway = {
|
|
588
|
+
name: 'test-pathway',
|
|
589
|
+
temperature: 0.7,
|
|
590
|
+
prompt: 'Test prompt'
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
const mockModel = {
|
|
594
|
+
name: 'xai-grok-4',
|
|
595
|
+
type: 'GROK-VISION',
|
|
596
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
597
|
+
headers: {
|
|
598
|
+
'Authorization': 'Bearer test-key',
|
|
599
|
+
'Content-Type': 'application/json'
|
|
600
|
+
},
|
|
601
|
+
params: {
|
|
602
|
+
model: 'grok-4-0709'
|
|
603
|
+
},
|
|
604
|
+
maxTokenLength: 131072,
|
|
605
|
+
maxReturnTokens: 4096
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
609
|
+
|
|
610
|
+
const mockResponse = {
|
|
611
|
+
choices: [{
|
|
612
|
+
message: {
|
|
613
|
+
role: 'assistant',
|
|
614
|
+
content: 'Simple text response'
|
|
615
|
+
}
|
|
616
|
+
}]
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
const result = plugin.parseResponse(mockResponse);
|
|
620
|
+
|
|
621
|
+
// Should return a CortexResponse object for simple responses
|
|
622
|
+
t.is(result.output_text, 'Simple text response');
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test('should handle basic Grok API response format', t => {
|
|
626
|
+
const mockPathway = {
|
|
627
|
+
name: 'test-pathway',
|
|
628
|
+
temperature: 0.7,
|
|
629
|
+
prompt: 'Test prompt'
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
const mockModel = {
|
|
633
|
+
name: 'xai-grok-3',
|
|
634
|
+
type: 'GROK-VISION',
|
|
635
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
636
|
+
headers: {
|
|
637
|
+
'Authorization': 'Bearer test-key',
|
|
638
|
+
'Content-Type': 'application/json'
|
|
639
|
+
},
|
|
640
|
+
params: {
|
|
641
|
+
model: 'grok-3-latest'
|
|
642
|
+
},
|
|
643
|
+
maxTokenLength: 131072,
|
|
644
|
+
maxReturnTokens: 4096
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
648
|
+
|
|
649
|
+
// This matches the actual Grok API response format from your curl example
|
|
650
|
+
const mockResponse = {
|
|
651
|
+
"id": "13493401-f153-7de6-ac07-c6f6b2609b06",
|
|
652
|
+
"object": "chat.completion",
|
|
653
|
+
"created": 1753113536,
|
|
654
|
+
"model": "grok-3",
|
|
655
|
+
"choices": [{
|
|
656
|
+
"index": 0,
|
|
657
|
+
"message": {
|
|
658
|
+
"role": "assistant",
|
|
659
|
+
"content": "Hi\nHello World",
|
|
660
|
+
"refusal": null
|
|
661
|
+
},
|
|
662
|
+
"finish_reason": "stop"
|
|
663
|
+
}],
|
|
664
|
+
"usage": {
|
|
665
|
+
"prompt_tokens": 28,
|
|
666
|
+
"completion_tokens": 4,
|
|
667
|
+
"total_tokens": 32,
|
|
668
|
+
"prompt_tokens_details": {
|
|
669
|
+
"text_tokens": 28,
|
|
670
|
+
"audio_tokens": 0,
|
|
671
|
+
"image_tokens": 0,
|
|
672
|
+
"cached_tokens": 5
|
|
673
|
+
},
|
|
674
|
+
"completion_tokens_details": {
|
|
675
|
+
"reasoning_tokens": 0,
|
|
676
|
+
"audio_tokens": 0,
|
|
677
|
+
"accepted_prediction_tokens": 0,
|
|
678
|
+
"rejected_prediction_tokens": 0
|
|
679
|
+
},
|
|
680
|
+
"num_sources_used": 0
|
|
681
|
+
},
|
|
682
|
+
"system_fingerprint": "fp_0d42a4eb3d"
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
const result = plugin.parseResponse(mockResponse);
|
|
686
|
+
|
|
687
|
+
// Should return a CortexResponse object
|
|
688
|
+
t.is(result.output_text, 'Hi\nHello World');
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
test('should parse messages with image content', async t => {
|
|
692
|
+
const mockPathway = {
|
|
693
|
+
name: 'test-pathway',
|
|
694
|
+
temperature: 0.7,
|
|
695
|
+
prompt: 'Test prompt'
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
const mockModel = {
|
|
699
|
+
name: 'xai-grok-4',
|
|
700
|
+
type: 'GROK-VISION',
|
|
701
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
702
|
+
headers: {
|
|
703
|
+
'Authorization': 'Bearer test-key',
|
|
704
|
+
'Content-Type': 'application/json'
|
|
705
|
+
},
|
|
706
|
+
params: {
|
|
707
|
+
model: 'grok-4-0709'
|
|
708
|
+
},
|
|
709
|
+
maxTokenLength: 131072,
|
|
710
|
+
maxReturnTokens: 4096
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
714
|
+
|
|
715
|
+
const messages = [
|
|
716
|
+
{
|
|
717
|
+
role: 'user',
|
|
718
|
+
content: [
|
|
719
|
+
{ type: 'text', text: 'Describe this image' },
|
|
720
|
+
{
|
|
721
|
+
type: 'image_url',
|
|
722
|
+
image_url: {
|
|
723
|
+
url: 'https://example.com/image.jpg',
|
|
724
|
+
detail: 'auto'
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
]
|
|
728
|
+
}
|
|
729
|
+
];
|
|
730
|
+
|
|
731
|
+
// Mock validateImageUrl to return true
|
|
732
|
+
plugin.validateImageUrl = () => Promise.resolve(true);
|
|
733
|
+
|
|
734
|
+
const result = await plugin.tryParseMessages(messages);
|
|
735
|
+
|
|
736
|
+
t.is(result[0].content.length, 2);
|
|
737
|
+
t.is(result[0].content[0].type, 'text');
|
|
738
|
+
t.is(result[0].content[1].type, 'image_url');
|
|
739
|
+
t.is(result[0].content[1].image_url.detail, 'auto');
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
test('should handle Grok vision response with web search results', t => {
|
|
743
|
+
const mockPathway = {
|
|
744
|
+
name: 'test-pathway',
|
|
745
|
+
temperature: 0.7,
|
|
746
|
+
prompt: 'Test prompt'
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
const mockModel = {
|
|
750
|
+
name: 'xai-grok-4',
|
|
751
|
+
type: 'GROK-VISION',
|
|
752
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
753
|
+
headers: {
|
|
754
|
+
'Authorization': 'Bearer test-key',
|
|
755
|
+
'Content-Type': 'application/json'
|
|
756
|
+
},
|
|
757
|
+
params: {
|
|
758
|
+
model: 'grok-4-0709'
|
|
759
|
+
},
|
|
760
|
+
maxTokenLength: 131072,
|
|
761
|
+
maxReturnTokens: 4096
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
765
|
+
|
|
766
|
+
const mockResponse = {
|
|
767
|
+
choices: [{
|
|
768
|
+
message: {
|
|
769
|
+
role: 'assistant',
|
|
770
|
+
content: 'Based on the image and web search...'
|
|
771
|
+
}
|
|
772
|
+
}],
|
|
773
|
+
web_search_results: [
|
|
774
|
+
{
|
|
775
|
+
title: 'Search Result',
|
|
776
|
+
snippet: 'Relevant information',
|
|
777
|
+
url: 'https://example.com'
|
|
778
|
+
}
|
|
779
|
+
]
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
const result = plugin.parseResponse(mockResponse);
|
|
783
|
+
|
|
784
|
+
// Should return a CortexResponse object
|
|
785
|
+
t.is(result.output_text, 'Based on the image and web search...');
|
|
786
|
+
t.truthy(result.searchResults);
|
|
787
|
+
t.is(result.searchResults[0].title, 'Search Result');
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
test('should handle streaming events with Grok-specific fields', t => {
|
|
791
|
+
const mockPathway = {
|
|
792
|
+
name: 'test-pathway',
|
|
793
|
+
temperature: 0.7,
|
|
794
|
+
prompt: 'Test prompt'
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
const mockModel = {
|
|
798
|
+
name: 'xai-grok-4',
|
|
799
|
+
type: 'GROK-VISION',
|
|
800
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
801
|
+
headers: {
|
|
802
|
+
'Authorization': 'Bearer test-key',
|
|
803
|
+
'Content-Type': 'application/json'
|
|
804
|
+
},
|
|
805
|
+
params: {
|
|
806
|
+
model: 'grok-4-0709'
|
|
807
|
+
},
|
|
808
|
+
maxTokenLength: 131072,
|
|
809
|
+
maxReturnTokens: 4096
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
813
|
+
|
|
814
|
+
const event = {
|
|
815
|
+
data: JSON.stringify({
|
|
816
|
+
choices: [{
|
|
817
|
+
delta: {
|
|
818
|
+
content: 'Test content',
|
|
819
|
+
citations: [{ title: 'Citation', url: 'https://example.com' }],
|
|
820
|
+
search_queries: ['test query'],
|
|
821
|
+
web_search_results: [{ title: 'Result', url: 'https://example.com' }],
|
|
822
|
+
real_time_data: { timestamp: '2024-01-01T00:00:00Z', data: 'Real-time info' }
|
|
823
|
+
}
|
|
824
|
+
}]
|
|
825
|
+
})
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
const requestProgress = { data: '', progress: 0 };
|
|
829
|
+
const result = plugin.processStreamEvent(event, requestProgress);
|
|
830
|
+
|
|
831
|
+
// Should return the requestProgress object (calls parent implementation)
|
|
832
|
+
t.is(result, requestProgress);
|
|
833
|
+
t.is(typeof result.data, 'string');
|
|
834
|
+
t.is(typeof result.progress, 'number');
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
test('should handle end of stream event', t => {
|
|
838
|
+
const mockPathway = {
|
|
839
|
+
name: 'test-pathway',
|
|
840
|
+
temperature: 0.7,
|
|
841
|
+
prompt: 'Test prompt'
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
const mockModel = {
|
|
845
|
+
name: 'xai-grok-4',
|
|
846
|
+
type: 'GROK-VISION',
|
|
847
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
848
|
+
headers: {
|
|
849
|
+
'Authorization': 'Bearer test-key',
|
|
850
|
+
'Content-Type': 'application/json'
|
|
851
|
+
},
|
|
852
|
+
params: {
|
|
853
|
+
model: 'grok-4-0709'
|
|
854
|
+
},
|
|
855
|
+
maxTokenLength: 131072,
|
|
856
|
+
maxReturnTokens: 4096
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
860
|
+
|
|
861
|
+
const event = { data: '[DONE]' };
|
|
862
|
+
const requestProgress = { data: '', progress: 0 };
|
|
863
|
+
|
|
864
|
+
const result = plugin.processStreamEvent(event, requestProgress);
|
|
865
|
+
|
|
866
|
+
t.is(result.progress, 1);
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
test('should handle Live Search parameters correctly', async t => {
|
|
870
|
+
const mockPathway = {
|
|
871
|
+
name: 'test-pathway',
|
|
872
|
+
temperature: 0.7,
|
|
873
|
+
prompt: 'Test prompt'
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const mockModel = {
|
|
877
|
+
name: 'xai-grok-3',
|
|
878
|
+
type: 'GROK-VISION',
|
|
879
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
880
|
+
headers: {
|
|
881
|
+
'Authorization': 'Bearer test-key',
|
|
882
|
+
'Content-Type': 'application/json'
|
|
883
|
+
},
|
|
884
|
+
params: {
|
|
885
|
+
model: 'grok-3-latest'
|
|
886
|
+
},
|
|
887
|
+
maxTokenLength: 131072,
|
|
888
|
+
maxReturnTokens: 4096
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
892
|
+
|
|
893
|
+
// Test Live Search specific parameters
|
|
894
|
+
const parameters = {
|
|
895
|
+
search_parameters: '{"mode": "auto"}'
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
const requestParams = await plugin.getRequestParameters('Search for latest tech news', parameters, {});
|
|
899
|
+
|
|
900
|
+
// Verify search_parameters are set correctly
|
|
901
|
+
t.truthy(requestParams.search_parameters, 'search_parameters should be set');
|
|
902
|
+
t.is(requestParams.search_parameters.mode, 'auto', 'search mode should be auto');
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
test('should parse Live Search response with real-time data', t => {
|
|
906
|
+
const mockPathway = {
|
|
907
|
+
name: 'test-pathway',
|
|
908
|
+
temperature: 0.7,
|
|
909
|
+
prompt: 'Test prompt'
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
const mockModel = {
|
|
913
|
+
name: 'xai-grok-3',
|
|
914
|
+
type: 'GROK-VISION',
|
|
915
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
916
|
+
headers: {
|
|
917
|
+
'Authorization': 'Bearer test-key',
|
|
918
|
+
'Content-Type': 'application/json'
|
|
919
|
+
},
|
|
920
|
+
params: {
|
|
921
|
+
model: 'grok-3-latest'
|
|
922
|
+
},
|
|
923
|
+
maxTokenLength: 131072,
|
|
924
|
+
maxReturnTokens: 4096
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
928
|
+
|
|
929
|
+
// Mock response with Live Search data
|
|
930
|
+
const mockResponse = {
|
|
931
|
+
choices: [{
|
|
932
|
+
message: {
|
|
933
|
+
role: 'assistant',
|
|
934
|
+
content: 'Based on real-time data from X...'
|
|
935
|
+
}
|
|
936
|
+
}],
|
|
937
|
+
citations: [
|
|
938
|
+
'https://x.com/user/status/123456'
|
|
939
|
+
],
|
|
940
|
+
search_queries: ['AI trends', 'artificial intelligence 2024'],
|
|
941
|
+
web_search_results: [
|
|
942
|
+
{
|
|
943
|
+
title: 'Latest AI Developments',
|
|
944
|
+
snippet: 'Recent breakthroughs in AI technology...',
|
|
945
|
+
url: 'https://example.com/ai-news'
|
|
946
|
+
}
|
|
947
|
+
],
|
|
948
|
+
real_time_data: {
|
|
949
|
+
timestamp: '2024-01-01T12:00:00Z',
|
|
950
|
+
platform: 'X',
|
|
951
|
+
trending_topics: ['AI', 'Machine Learning'],
|
|
952
|
+
engagement_metrics: {
|
|
953
|
+
likes: 1000,
|
|
954
|
+
retweets: 500,
|
|
955
|
+
replies: 200
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
const result = plugin.parseResponse(mockResponse);
|
|
961
|
+
|
|
962
|
+
// Verify all Live Search response fields are parsed correctly
|
|
963
|
+
t.is(result.output_text, 'Based on real-time data from X...');
|
|
964
|
+
t.truthy(result.citations, 'Should have citations');
|
|
965
|
+
t.is(result.citations[0].url, 'https://x.com/user/status/123456');
|
|
966
|
+
t.truthy(result.searchQueries, 'Should have search queries');
|
|
967
|
+
t.is(result.searchQueries[0], 'AI trends');
|
|
968
|
+
t.truthy(result.searchResults, 'Should have web search results');
|
|
969
|
+
t.is(result.searchResults[0].title, 'Latest AI Developments');
|
|
970
|
+
t.truthy(result.realTimeData, 'Should have real-time data');
|
|
971
|
+
t.is(result.realTimeData.platform, 'X');
|
|
972
|
+
t.truthy(result.realTimeData.trending_topics, 'Should have trending topics');
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
test('should parse Live Search response with usage data', t => {
|
|
976
|
+
const mockPathway = {
|
|
977
|
+
name: 'test-pathway',
|
|
978
|
+
temperature: 0.7,
|
|
979
|
+
prompt: 'Test prompt'
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
const mockModel = {
|
|
983
|
+
name: 'xai-grok-3',
|
|
984
|
+
type: 'GROK-VISION',
|
|
985
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
986
|
+
headers: {
|
|
987
|
+
'Authorization': 'Bearer test-key',
|
|
988
|
+
'Content-Type': 'application/json'
|
|
989
|
+
},
|
|
990
|
+
params: {
|
|
991
|
+
model: 'grok-3-latest'
|
|
992
|
+
},
|
|
993
|
+
maxTokenLength: 131072,
|
|
994
|
+
maxReturnTokens: 4096
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
998
|
+
|
|
999
|
+
// Mock response with Live Search usage data
|
|
1000
|
+
const mockResponse = {
|
|
1001
|
+
choices: [{
|
|
1002
|
+
message: {
|
|
1003
|
+
role: 'assistant',
|
|
1004
|
+
content: 'Based on live search results...'
|
|
1005
|
+
}
|
|
1006
|
+
}],
|
|
1007
|
+
citations: [
|
|
1008
|
+
'https://example.com/result1',
|
|
1009
|
+
'https://example.com/result2'
|
|
1010
|
+
],
|
|
1011
|
+
usage: {
|
|
1012
|
+
prompt_tokens: 150,
|
|
1013
|
+
completion_tokens: 200,
|
|
1014
|
+
total_tokens: 350,
|
|
1015
|
+
num_sources_used: 5
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
|
|
1019
|
+
const result = plugin.parseResponse(mockResponse);
|
|
1020
|
+
|
|
1021
|
+
// Verify response content and citations
|
|
1022
|
+
t.is(result.output_text, 'Based on live search results...');
|
|
1023
|
+
t.truthy(result.citations, 'Should have citations');
|
|
1024
|
+
t.is(result.citations.length, 2, 'Should have 2 citations');
|
|
1025
|
+
t.is(result.citations[0].url, 'https://example.com/result1');
|
|
1026
|
+
t.is(result.citations[1].url, 'https://example.com/result2');
|
|
1027
|
+
|
|
1028
|
+
// Verify usage data is preserved
|
|
1029
|
+
t.truthy(mockResponse.usage, 'Usage data should be preserved in original response');
|
|
1030
|
+
t.is(mockResponse.usage.num_sources_used, 5, 'Should show 5 sources used');
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
test('should validate search parameters - valid parameters', t => {
|
|
1034
|
+
const mockPathway = {
|
|
1035
|
+
name: 'test-pathway',
|
|
1036
|
+
temperature: 0.7,
|
|
1037
|
+
prompt: 'Test prompt'
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
const mockModel = {
|
|
1041
|
+
name: 'xai-grok-3',
|
|
1042
|
+
type: 'GROK-VISION',
|
|
1043
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1044
|
+
headers: {
|
|
1045
|
+
'Authorization': 'Bearer test-key',
|
|
1046
|
+
'Content-Type': 'application/json'
|
|
1047
|
+
},
|
|
1048
|
+
params: {
|
|
1049
|
+
model: 'grok-3-latest'
|
|
1050
|
+
},
|
|
1051
|
+
maxTokenLength: 131072,
|
|
1052
|
+
maxReturnTokens: 4096
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1056
|
+
|
|
1057
|
+
const validParams = {
|
|
1058
|
+
mode: 'auto',
|
|
1059
|
+
return_citations: true,
|
|
1060
|
+
from_date: '2024-01-01',
|
|
1061
|
+
to_date: '2024-12-31',
|
|
1062
|
+
max_search_results: 25,
|
|
1063
|
+
sources: [
|
|
1064
|
+
{ type: 'web', country: 'US' },
|
|
1065
|
+
{ type: 'x', included_x_handles: ['testuser'] },
|
|
1066
|
+
{ type: 'news' },
|
|
1067
|
+
{ type: 'rss', links: ['https://example.com/feed.xml'] }
|
|
1068
|
+
]
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
t.true(plugin.validateSearchParameters(validParams));
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
test('should validate search parameters - invalid mode', t => {
|
|
1075
|
+
const mockPathway = {
|
|
1076
|
+
name: 'test-pathway',
|
|
1077
|
+
temperature: 0.7,
|
|
1078
|
+
prompt: 'Test prompt'
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
const mockModel = {
|
|
1082
|
+
name: 'xai-grok-3',
|
|
1083
|
+
type: 'GROK-VISION',
|
|
1084
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1085
|
+
headers: {
|
|
1086
|
+
'Authorization': 'Bearer test-key',
|
|
1087
|
+
'Content-Type': 'application/json'
|
|
1088
|
+
},
|
|
1089
|
+
params: {
|
|
1090
|
+
model: 'grok-3-latest'
|
|
1091
|
+
},
|
|
1092
|
+
maxTokenLength: 131072,
|
|
1093
|
+
maxReturnTokens: 4096
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1097
|
+
|
|
1098
|
+
const invalidParams = {
|
|
1099
|
+
mode: 'invalid_mode'
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
const error = t.throws(() => plugin.validateSearchParameters(invalidParams));
|
|
1103
|
+
t.true(error.message.includes('Invalid \'mode\' parameter'));
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
test('should validate search parameters - invalid date format', t => {
|
|
1107
|
+
const mockPathway = {
|
|
1108
|
+
name: 'test-pathway',
|
|
1109
|
+
temperature: 0.7,
|
|
1110
|
+
prompt: 'Test prompt'
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
const mockModel = {
|
|
1114
|
+
name: 'xai-grok-3',
|
|
1115
|
+
type: 'GROK-VISION',
|
|
1116
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1117
|
+
headers: {
|
|
1118
|
+
'Authorization': 'Bearer test-key',
|
|
1119
|
+
'Content-Type': 'application/json'
|
|
1120
|
+
},
|
|
1121
|
+
params: {
|
|
1122
|
+
model: 'grok-3-latest'
|
|
1123
|
+
},
|
|
1124
|
+
maxTokenLength: 131072,
|
|
1125
|
+
maxReturnTokens: 4096
|
|
1126
|
+
};
|
|
1127
|
+
|
|
1128
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1129
|
+
|
|
1130
|
+
const invalidParams = {
|
|
1131
|
+
from_date: '2024/01/01' // Invalid format
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
const error = t.throws(() => plugin.validateSearchParameters(invalidParams));
|
|
1135
|
+
t.true(error.message.includes('must be in YYYY-MM-DD format'));
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
test('should validate search parameters - invalid max_search_results', t => {
|
|
1139
|
+
const mockPathway = {
|
|
1140
|
+
name: 'test-pathway',
|
|
1141
|
+
temperature: 0.7,
|
|
1142
|
+
prompt: 'Test prompt'
|
|
1143
|
+
};
|
|
1144
|
+
|
|
1145
|
+
const mockModel = {
|
|
1146
|
+
name: 'xai-grok-3',
|
|
1147
|
+
type: 'GROK-VISION',
|
|
1148
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1149
|
+
headers: {
|
|
1150
|
+
'Authorization': 'Bearer test-key',
|
|
1151
|
+
'Content-Type': 'application/json'
|
|
1152
|
+
},
|
|
1153
|
+
params: {
|
|
1154
|
+
model: 'grok-3-latest'
|
|
1155
|
+
},
|
|
1156
|
+
maxTokenLength: 131072,
|
|
1157
|
+
maxReturnTokens: 4096
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1161
|
+
|
|
1162
|
+
const invalidParams = {
|
|
1163
|
+
max_search_results: 100 // Too high
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
const error = t.throws(() => plugin.validateSearchParameters(invalidParams));
|
|
1167
|
+
t.true(error.message.includes('must be 50 or less'));
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
test('should validate search parameters - invalid X handles count', t => {
|
|
1171
|
+
const mockPathway = {
|
|
1172
|
+
name: 'test-pathway',
|
|
1173
|
+
temperature: 0.7,
|
|
1174
|
+
prompt: 'Test prompt'
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
const mockModel = {
|
|
1178
|
+
name: 'xai-grok-3',
|
|
1179
|
+
type: 'GROK-VISION',
|
|
1180
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1181
|
+
headers: {
|
|
1182
|
+
'Authorization': 'Bearer test-key',
|
|
1183
|
+
'Content-Type': 'application/json'
|
|
1184
|
+
},
|
|
1185
|
+
params: {
|
|
1186
|
+
model: 'grok-3-latest'
|
|
1187
|
+
},
|
|
1188
|
+
maxTokenLength: 131072,
|
|
1189
|
+
maxReturnTokens: 4096
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1193
|
+
|
|
1194
|
+
const invalidParams = {
|
|
1195
|
+
sources: [{
|
|
1196
|
+
type: 'x',
|
|
1197
|
+
included_x_handles: Array(11).fill('user') // Too many handles
|
|
1198
|
+
}]
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
const error = t.throws(() => plugin.validateSearchParameters(invalidParams));
|
|
1202
|
+
t.true(error.message.includes('can have a maximum of 10 items'));
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
test('should validate search parameters - conflicting X handles', t => {
|
|
1206
|
+
const mockPathway = {
|
|
1207
|
+
name: 'test-pathway',
|
|
1208
|
+
temperature: 0.7,
|
|
1209
|
+
prompt: 'Test prompt'
|
|
1210
|
+
};
|
|
1211
|
+
|
|
1212
|
+
const mockModel = {
|
|
1213
|
+
name: 'xai-grok-3',
|
|
1214
|
+
type: 'GROK-VISION',
|
|
1215
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1216
|
+
headers: {
|
|
1217
|
+
'Authorization': 'Bearer test-key',
|
|
1218
|
+
'Content-Type': 'application/json'
|
|
1219
|
+
},
|
|
1220
|
+
params: {
|
|
1221
|
+
model: 'grok-3-latest'
|
|
1222
|
+
},
|
|
1223
|
+
maxTokenLength: 131072,
|
|
1224
|
+
maxReturnTokens: 4096
|
|
1225
|
+
};
|
|
1226
|
+
|
|
1227
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1228
|
+
|
|
1229
|
+
const invalidParams = {
|
|
1230
|
+
sources: [{
|
|
1231
|
+
type: 'x',
|
|
1232
|
+
included_x_handles: ['user1'],
|
|
1233
|
+
excluded_x_handles: ['user2'] // Cannot specify both
|
|
1234
|
+
}]
|
|
1235
|
+
};
|
|
1236
|
+
|
|
1237
|
+
const error = t.throws(() => plugin.validateSearchParameters(invalidParams));
|
|
1238
|
+
t.true(error.message.includes('cannot be specified simultaneously'));
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
test('should validate search parameters - invalid RSS links count', t => {
|
|
1242
|
+
const mockPathway = {
|
|
1243
|
+
name: 'test-pathway',
|
|
1244
|
+
temperature: 0.7,
|
|
1245
|
+
prompt: 'Test prompt'
|
|
1246
|
+
};
|
|
1247
|
+
|
|
1248
|
+
const mockModel = {
|
|
1249
|
+
name: 'xai-grok-3',
|
|
1250
|
+
type: 'GROK-VISION',
|
|
1251
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1252
|
+
headers: {
|
|
1253
|
+
'Authorization': 'Bearer test-key',
|
|
1254
|
+
'Content-Type': 'application/json'
|
|
1255
|
+
},
|
|
1256
|
+
params: {
|
|
1257
|
+
model: 'grok-3-latest'
|
|
1258
|
+
},
|
|
1259
|
+
maxTokenLength: 131072,
|
|
1260
|
+
maxReturnTokens: 4096
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1264
|
+
|
|
1265
|
+
const invalidParams = {
|
|
1266
|
+
sources: [{
|
|
1267
|
+
type: 'rss',
|
|
1268
|
+
links: ['https://feed1.xml', 'https://feed2.xml'] // Too many links
|
|
1269
|
+
}]
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
const error = t.throws(() => plugin.validateSearchParameters(invalidParams));
|
|
1273
|
+
t.true(error.message.includes('can only have one item'));
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
test('safeJsonParse should parse valid JSON', t => {
|
|
1277
|
+
const mockPathway = {
|
|
1278
|
+
name: 'test-pathway',
|
|
1279
|
+
temperature: 0.7,
|
|
1280
|
+
prompt: 'Test prompt'
|
|
1281
|
+
};
|
|
1282
|
+
|
|
1283
|
+
const mockModel = {
|
|
1284
|
+
name: 'xai-grok-3',
|
|
1285
|
+
type: 'GROK-VISION',
|
|
1286
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1287
|
+
headers: {
|
|
1288
|
+
'Authorization': 'Bearer test-key',
|
|
1289
|
+
'Content-Type': 'application/json'
|
|
1290
|
+
},
|
|
1291
|
+
params: {
|
|
1292
|
+
model: 'grok-3-latest'
|
|
1293
|
+
},
|
|
1294
|
+
maxTokenLength: 131072,
|
|
1295
|
+
maxReturnTokens: 4096
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1298
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1299
|
+
|
|
1300
|
+
const validJson = '{"key": "value", "number": 42}';
|
|
1301
|
+
const result = safeJsonParse(validJson);
|
|
1302
|
+
|
|
1303
|
+
t.deepEqual(result, { key: 'value', number: 42 });
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
test('safeJsonParse should return original string for invalid JSON', t => {
|
|
1307
|
+
const mockPathway = {
|
|
1308
|
+
name: 'test-pathway',
|
|
1309
|
+
temperature: 0.7,
|
|
1310
|
+
prompt: 'Test prompt'
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
const mockModel = {
|
|
1314
|
+
name: 'xai-grok-3',
|
|
1315
|
+
type: 'GROK-VISION',
|
|
1316
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1317
|
+
headers: {
|
|
1318
|
+
'Authorization': 'Bearer test-key',
|
|
1319
|
+
'Content-Type': 'application/json'
|
|
1320
|
+
},
|
|
1321
|
+
params: {
|
|
1322
|
+
model: 'grok-3-latest'
|
|
1323
|
+
},
|
|
1324
|
+
maxTokenLength: 131072,
|
|
1325
|
+
maxReturnTokens: 4096
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1329
|
+
|
|
1330
|
+
const invalidJson = '{"key": "value", "invalid": }';
|
|
1331
|
+
const result = safeJsonParse(invalidJson);
|
|
1332
|
+
|
|
1333
|
+
t.is(result, invalidJson);
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
test('safeJsonParse should return non-string input as-is', t => {
|
|
1337
|
+
const mockPathway = {
|
|
1338
|
+
name: 'test-pathway',
|
|
1339
|
+
temperature: 0.7,
|
|
1340
|
+
prompt: 'Test prompt'
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
const mockModel = {
|
|
1344
|
+
name: 'xai-grok-3',
|
|
1345
|
+
type: 'GROK-VISION',
|
|
1346
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1347
|
+
headers: {
|
|
1348
|
+
'Authorization': 'Bearer test-key',
|
|
1349
|
+
'Content-Type': 'application/json'
|
|
1350
|
+
},
|
|
1351
|
+
params: {
|
|
1352
|
+
model: 'grok-3-latest'
|
|
1353
|
+
},
|
|
1354
|
+
maxTokenLength: 131072,
|
|
1355
|
+
maxReturnTokens: 4096
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1359
|
+
|
|
1360
|
+
const objectInput = { key: 'value' };
|
|
1361
|
+
const result = safeJsonParse(objectInput);
|
|
1362
|
+
|
|
1363
|
+
t.is(result, objectInput);
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
test('safeJsonParse should return null/undefined as-is', t => {
|
|
1367
|
+
const mockPathway = {
|
|
1368
|
+
name: 'test-pathway',
|
|
1369
|
+
temperature: 0.7,
|
|
1370
|
+
prompt: 'Test prompt'
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1373
|
+
const mockModel = {
|
|
1374
|
+
name: 'xai-grok-3',
|
|
1375
|
+
type: 'GROK-VISION',
|
|
1376
|
+
url: 'https://api.x.ai/v1/chat/completions',
|
|
1377
|
+
headers: {
|
|
1378
|
+
'Authorization': 'Bearer test-key',
|
|
1379
|
+
'Content-Type': 'application/json'
|
|
1380
|
+
},
|
|
1381
|
+
params: {
|
|
1382
|
+
model: 'grok-3-latest'
|
|
1383
|
+
},
|
|
1384
|
+
maxTokenLength: 131072,
|
|
1385
|
+
maxReturnTokens: 4096
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
const plugin = new GrokVisionPlugin(mockPathway, mockModel);
|
|
1389
|
+
|
|
1390
|
+
t.is(safeJsonParse(null), null);
|
|
1391
|
+
t.is(safeJsonParse(undefined), undefined);
|
|
1392
|
+
});
|