@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
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import Gemini15ChatPlugin from './gemini15ChatPlugin.js';
|
|
2
|
+
import CortexResponse from '../../lib/cortexResponse.js';
|
|
3
|
+
import { requestState } from '../requestState.js';
|
|
4
|
+
import { addCitationsToResolver } from '../../lib/pathwayTools.js';
|
|
2
5
|
import mime from 'mime-types';
|
|
3
6
|
|
|
4
7
|
class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
@@ -6,6 +9,10 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
6
9
|
constructor(pathway, model) {
|
|
7
10
|
super(pathway, model);
|
|
8
11
|
this.isMultiModal = true;
|
|
12
|
+
this.pathwayToolCallback = pathway.toolCallback;
|
|
13
|
+
this.toolCallsBuffer = [];
|
|
14
|
+
this.contentBuffer = '';
|
|
15
|
+
this.hadToolCalls = false;
|
|
9
16
|
}
|
|
10
17
|
|
|
11
18
|
// Override the convertMessagesToGemini method to handle multimodal vision messages
|
|
@@ -94,6 +101,27 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
94
101
|
if ((role === lastAuthor || author === lastAuthor) && modifiedMessages.length > 0) {
|
|
95
102
|
modifiedMessages[modifiedMessages.length - 1].parts.push(geminiPart);
|
|
96
103
|
}
|
|
104
|
+
// Handle tool result messages
|
|
105
|
+
else if (role === 'tool') {
|
|
106
|
+
// Convert OpenAI tool result format to Gemini format
|
|
107
|
+
// OpenAI: { role: 'tool', tool_call_id: '...', content: '...' }
|
|
108
|
+
// Gemini: { role: 'function', parts: [{ functionResponse: { name: '...', response: { content: '...' } } }] }
|
|
109
|
+
const toolCallId = message.tool_call_id || message.toolCallId;
|
|
110
|
+
const toolName = toolCallId ? toolCallId.split('_')[0] : 'unknown_tool';
|
|
111
|
+
|
|
112
|
+
modifiedMessages.push({
|
|
113
|
+
role: 'function',
|
|
114
|
+
parts: [{
|
|
115
|
+
functionResponse: {
|
|
116
|
+
name: toolName,
|
|
117
|
+
response: {
|
|
118
|
+
content: content
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}]
|
|
122
|
+
});
|
|
123
|
+
lastAuthor = 'function';
|
|
124
|
+
}
|
|
97
125
|
// Gemini only supports user: and model: roles
|
|
98
126
|
else if (role === 'user' || role === 'assistant' || author) {
|
|
99
127
|
modifiedMessages.push({
|
|
@@ -133,6 +161,113 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
133
161
|
};
|
|
134
162
|
}
|
|
135
163
|
|
|
164
|
+
// Convert OpenAI tools to Gemini format
|
|
165
|
+
convertOpenAIToolsToGemini(openAITools) {
|
|
166
|
+
if (!openAITools || !Array.isArray(openAITools)) {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Convert OpenAI tools to Gemini functionDeclarations format
|
|
171
|
+
const functionDeclarations = openAITools.map(tool => {
|
|
172
|
+
if (tool.type === 'function' && tool.function) {
|
|
173
|
+
return {
|
|
174
|
+
name: tool.function.name,
|
|
175
|
+
description: tool.function.description || `Tool for ${tool.function.name}`,
|
|
176
|
+
parameters: tool.function.parameters || {
|
|
177
|
+
type: 'object',
|
|
178
|
+
properties: {},
|
|
179
|
+
required: []
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}).filter(Boolean);
|
|
185
|
+
|
|
186
|
+
// Return in the correct Gemini format: tools array with functionDeclarations
|
|
187
|
+
return [{
|
|
188
|
+
functionDeclarations: functionDeclarations
|
|
189
|
+
}];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Override getRequestParameters to handle tool conversion
|
|
193
|
+
getRequestParameters(text, parameters, prompt, cortexRequest) {
|
|
194
|
+
// Convert OpenAI tools to Gemini format if present
|
|
195
|
+
let convertedTools = [];
|
|
196
|
+
|
|
197
|
+
// Handle tools parameter - could be string (from REST) or array
|
|
198
|
+
let toolsArray = parameters?.tools;
|
|
199
|
+
if (typeof toolsArray === 'string') {
|
|
200
|
+
try {
|
|
201
|
+
toolsArray = JSON.parse(toolsArray);
|
|
202
|
+
} catch (e) {
|
|
203
|
+
toolsArray = [];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (toolsArray && Array.isArray(toolsArray)) {
|
|
208
|
+
convertedTools = this.convertOpenAIToolsToGemini(toolsArray);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (cortexRequest?.tools && Array.isArray(cortexRequest.tools)) {
|
|
212
|
+
const requestTools = this.convertOpenAIToolsToGemini(cortexRequest.tools);
|
|
213
|
+
convertedTools = [...convertedTools, ...requestTools];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (cortexRequest?.pathway?.tools && Array.isArray(cortexRequest.pathway.tools)) {
|
|
217
|
+
const pathwayTools = this.convertOpenAIToolsToGemini(cortexRequest.pathway.tools);
|
|
218
|
+
convertedTools = [...convertedTools, ...pathwayTools];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Temporarily remove geminiTools from pathway to prevent override
|
|
222
|
+
const originalGeminiTools = cortexRequest?.pathway?.geminiTools;
|
|
223
|
+
if (cortexRequest?.pathway) {
|
|
224
|
+
delete cortexRequest.pathway.geminiTools;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const baseParameters = super.getRequestParameters(text, parameters, prompt, cortexRequest);
|
|
228
|
+
|
|
229
|
+
// Restore original geminiTools
|
|
230
|
+
if (cortexRequest?.pathway && originalGeminiTools !== undefined) {
|
|
231
|
+
cortexRequest.pathway.geminiTools = originalGeminiTools;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (convertedTools.length > 0) {
|
|
235
|
+
baseParameters.tools = convertedTools;
|
|
236
|
+
|
|
237
|
+
// Handle tool_choice parameter - convert OpenAI format to Gemini toolConfig
|
|
238
|
+
let toolChoice = parameters.tool_choice;
|
|
239
|
+
if (typeof toolChoice === 'string' && toolChoice !== 'auto' && toolChoice !== 'none' && toolChoice !== 'required' && toolChoice !== 'any') {
|
|
240
|
+
try {
|
|
241
|
+
toolChoice = JSON.parse(toolChoice);
|
|
242
|
+
} catch (e) {
|
|
243
|
+
toolChoice = 'auto';
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (toolChoice) {
|
|
248
|
+
if (typeof toolChoice === 'string') {
|
|
249
|
+
if (toolChoice === 'auto') {
|
|
250
|
+
baseParameters.toolConfig = { functionCallingConfig: { mode: 'AUTO' } };
|
|
251
|
+
} else if (toolChoice === 'required' || toolChoice === 'any') {
|
|
252
|
+
baseParameters.toolConfig = { functionCallingConfig: { mode: 'ANY' } };
|
|
253
|
+
} else if (toolChoice === 'none') {
|
|
254
|
+
baseParameters.toolConfig = { functionCallingConfig: { mode: 'NONE' } };
|
|
255
|
+
}
|
|
256
|
+
} else if (toolChoice.type === 'function') {
|
|
257
|
+
// Force specific function - use ANY mode with allowed function names
|
|
258
|
+
baseParameters.toolConfig = {
|
|
259
|
+
functionCallingConfig: {
|
|
260
|
+
mode: 'ANY',
|
|
261
|
+
allowedFunctionNames: [toolChoice.function.name || toolChoice.function]
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return baseParameters;
|
|
269
|
+
}
|
|
270
|
+
|
|
136
271
|
async execute(text, parameters, prompt, cortexRequest) {
|
|
137
272
|
let result = null;
|
|
138
273
|
try {
|
|
@@ -149,6 +284,231 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
149
284
|
return result;
|
|
150
285
|
}
|
|
151
286
|
|
|
287
|
+
// Override parseResponse to handle tool calls
|
|
288
|
+
parseResponse(data) {
|
|
289
|
+
if (!data) {
|
|
290
|
+
return data;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Handle streaming data (array of chunks)
|
|
294
|
+
if (Array.isArray(data)) {
|
|
295
|
+
// For streaming, we'll handle this in processStreamEvent
|
|
296
|
+
return super.parseResponse(data);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Handle non-streaming response with tool calls
|
|
300
|
+
if (data.candidates && data.candidates[0]) {
|
|
301
|
+
const candidate = data.candidates[0];
|
|
302
|
+
const { content, finishReason, safetyRatings } = candidate;
|
|
303
|
+
|
|
304
|
+
// Check for safety blocks
|
|
305
|
+
if (safetyRatings?.some(rating => rating.blocked)) {
|
|
306
|
+
const cortexResponse = new CortexResponse({
|
|
307
|
+
output_text: "\n\n*** Response blocked due to safety ratings ***",
|
|
308
|
+
finishReason: "content_filter",
|
|
309
|
+
usage: data.usageMetadata || null,
|
|
310
|
+
metadata: { model: this.modelName }
|
|
311
|
+
});
|
|
312
|
+
return cortexResponse;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Check for tool calls
|
|
316
|
+
if (content?.parts) {
|
|
317
|
+
const toolCalls = [];
|
|
318
|
+
let textContent = '';
|
|
319
|
+
|
|
320
|
+
for (const part of content.parts) {
|
|
321
|
+
if (part.functionCall) {
|
|
322
|
+
toolCalls.push({
|
|
323
|
+
id: part.functionCall.name + '_' + Date.now(),
|
|
324
|
+
type: "function",
|
|
325
|
+
function: {
|
|
326
|
+
name: part.functionCall.name,
|
|
327
|
+
arguments: JSON.stringify(part.functionCall.args || {})
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
} else if (part.text) {
|
|
331
|
+
textContent += part.text;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Create CortexResponse object
|
|
336
|
+
const cortexResponse = new CortexResponse({
|
|
337
|
+
output_text: textContent,
|
|
338
|
+
finishReason: toolCalls.length > 0 ? "tool_calls" : (finishReason === "STOP" ? "stop" : "length"),
|
|
339
|
+
usage: data.usageMetadata || null,
|
|
340
|
+
metadata: { model: this.modelName }
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
if (toolCalls.length > 0) {
|
|
344
|
+
cortexResponse.toolCalls = toolCalls;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Add citations to resolver for non-streaming responses
|
|
348
|
+
const pathwayResolver = requestState[this.requestId]?.pathwayResolver;
|
|
349
|
+
if (pathwayResolver && textContent) {
|
|
350
|
+
addCitationsToResolver(pathwayResolver, textContent);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return cortexResponse;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Fallback to parent implementation
|
|
358
|
+
return super.parseResponse(data);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
// Override processStreamEvent to handle tool calls
|
|
363
|
+
processStreamEvent(event, requestProgress) {
|
|
364
|
+
const eventData = JSON.parse(event.data);
|
|
365
|
+
|
|
366
|
+
// Initialize requestProgress if needed
|
|
367
|
+
requestProgress = requestProgress || {};
|
|
368
|
+
requestProgress.data = requestProgress.data || null;
|
|
369
|
+
|
|
370
|
+
// Reset tool calls flag for new stream
|
|
371
|
+
if (!requestProgress.started) {
|
|
372
|
+
this.hadToolCalls = false;
|
|
373
|
+
this.toolCallsBuffer = [];
|
|
374
|
+
// Don't clear contentBuffer here - it should accumulate across all chunks
|
|
375
|
+
// this.contentBuffer = '';
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Create a helper function to generate message chunks
|
|
379
|
+
const createChunk = (delta, finishReason = null) => ({
|
|
380
|
+
id: eventData.responseId || `chatcmpl-${Date.now()}`,
|
|
381
|
+
object: "chat.completion.chunk",
|
|
382
|
+
created: Math.floor(Date.now() / 1000),
|
|
383
|
+
model: this.modelName,
|
|
384
|
+
choices: [{
|
|
385
|
+
index: 0,
|
|
386
|
+
delta,
|
|
387
|
+
finish_reason: finishReason
|
|
388
|
+
}]
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Handle content chunks with tool calls
|
|
392
|
+
if (eventData.candidates?.[0]?.content?.parts) {
|
|
393
|
+
const parts = eventData.candidates[0].content.parts;
|
|
394
|
+
|
|
395
|
+
for (const part of parts) {
|
|
396
|
+
if (part.functionCall) {
|
|
397
|
+
// Mark that we have tool calls
|
|
398
|
+
this.hadToolCalls = true;
|
|
399
|
+
|
|
400
|
+
// Create tool call object
|
|
401
|
+
const toolCall = {
|
|
402
|
+
id: part.functionCall.name + '_' + Date.now(),
|
|
403
|
+
type: "function",
|
|
404
|
+
function: {
|
|
405
|
+
name: part.functionCall.name,
|
|
406
|
+
arguments: JSON.stringify(part.functionCall.args || {})
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
this.toolCallsBuffer.push(toolCall);
|
|
411
|
+
|
|
412
|
+
// Send tool call delta
|
|
413
|
+
requestProgress.data = JSON.stringify(createChunk({
|
|
414
|
+
tool_calls: [{
|
|
415
|
+
index: this.toolCallsBuffer.length - 1,
|
|
416
|
+
id: toolCall.id,
|
|
417
|
+
type: "function",
|
|
418
|
+
function: {
|
|
419
|
+
name: toolCall.function.name,
|
|
420
|
+
arguments: toolCall.function.arguments
|
|
421
|
+
}
|
|
422
|
+
}]
|
|
423
|
+
}));
|
|
424
|
+
|
|
425
|
+
} else if (part.text) {
|
|
426
|
+
// Regular text content
|
|
427
|
+
this.contentBuffer += part.text;
|
|
428
|
+
|
|
429
|
+
if (!requestProgress.started) {
|
|
430
|
+
// First chunk - send role
|
|
431
|
+
requestProgress.data = JSON.stringify(createChunk({ role: "assistant" }));
|
|
432
|
+
requestProgress.started = true;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Send content chunk
|
|
436
|
+
requestProgress.data = JSON.stringify(createChunk({
|
|
437
|
+
content: part.text
|
|
438
|
+
}));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Handle finish reasons
|
|
444
|
+
if (eventData.candidates?.[0]?.finishReason === "STOP") {
|
|
445
|
+
const finishReason = this.hadToolCalls ? "tool_calls" : "stop";
|
|
446
|
+
|
|
447
|
+
// Check if there's any remaining content in the final chunk that needs to be published
|
|
448
|
+
if (eventData.candidates?.[0]?.content?.parts) {
|
|
449
|
+
const parts = eventData.candidates[0].content.parts;
|
|
450
|
+
for (const part of parts) {
|
|
451
|
+
if (part.text && part.text.trim()) {
|
|
452
|
+
// Send the final content chunk with finish reason
|
|
453
|
+
requestProgress.data = JSON.stringify(createChunk({
|
|
454
|
+
content: part.text
|
|
455
|
+
}, finishReason));
|
|
456
|
+
break; // Only process the first text part
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
} else {
|
|
460
|
+
// No content, just send finish chunk
|
|
461
|
+
requestProgress.data = JSON.stringify(createChunk({}, finishReason));
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const pathwayResolver = requestState[this.requestId]?.pathwayResolver;
|
|
465
|
+
|
|
466
|
+
if (finishReason === 'tool_calls' && this.toolCallsBuffer.length > 0 && this.pathwayToolCallback && pathwayResolver) {
|
|
467
|
+
// Execute tool callback and keep stream open
|
|
468
|
+
const toolMessage = {
|
|
469
|
+
role: 'assistant',
|
|
470
|
+
content: this.contentBuffer || '',
|
|
471
|
+
tool_calls: this.toolCallsBuffer,
|
|
472
|
+
};
|
|
473
|
+
this.pathwayToolCallback(pathwayResolver?.args, toolMessage, pathwayResolver);
|
|
474
|
+
// Clear tool buffer after processing; keep content for citations/continuations
|
|
475
|
+
this.toolCallsBuffer = [];
|
|
476
|
+
} else {
|
|
477
|
+
// Either regular stop, or tool_calls without a callback → close the stream
|
|
478
|
+
requestProgress.progress = 1;
|
|
479
|
+
addCitationsToResolver(pathwayResolver, this.contentBuffer);
|
|
480
|
+
this.toolCallsBuffer = [];
|
|
481
|
+
this.contentBuffer = '';
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Handle safety blocks
|
|
486
|
+
if (eventData.candidates?.[0]?.safetyRatings?.some(rating => rating.blocked)) {
|
|
487
|
+
requestProgress.data = JSON.stringify(createChunk({
|
|
488
|
+
content: "\n\n*** Response blocked due to safety ratings ***"
|
|
489
|
+
}, "content_filter"));
|
|
490
|
+
requestProgress.progress = 1;
|
|
491
|
+
// Clear buffers on safety block (same as OpenAI plugin)
|
|
492
|
+
this.toolCallsBuffer = [];
|
|
493
|
+
this.contentBuffer = '';
|
|
494
|
+
return requestProgress;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Handle prompt feedback blocks
|
|
498
|
+
if (eventData.promptFeedback?.blockReason) {
|
|
499
|
+
requestProgress.data = JSON.stringify(createChunk({
|
|
500
|
+
content: `\n\n*** Response blocked: ${eventData.promptFeedback.blockReason} ***`
|
|
501
|
+
}, "content_filter"));
|
|
502
|
+
requestProgress.progress = 1;
|
|
503
|
+
// Clear buffers on prompt feedback block (same as OpenAI plugin)
|
|
504
|
+
this.toolCallsBuffer = [];
|
|
505
|
+
this.contentBuffer = '';
|
|
506
|
+
return requestProgress;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return requestProgress;
|
|
510
|
+
}
|
|
511
|
+
|
|
152
512
|
}
|
|
153
513
|
|
|
154
514
|
export default Gemini15VisionPlugin;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import ModelPlugin from './modelPlugin.js';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import { config } from '../../config.js';
|
|
4
|
+
|
|
5
|
+
class GoogleCsePlugin extends ModelPlugin {
|
|
6
|
+
constructor(pathway, model) {
|
|
7
|
+
super(pathway, model);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getRequestParameters(text, parameters = {}, prompt) {
|
|
11
|
+
const env = config.getEnv();
|
|
12
|
+
const apiKey = env["GOOGLE_CSE_KEY"];
|
|
13
|
+
const cxEnv = env["GOOGLE_CSE_CX"];
|
|
14
|
+
|
|
15
|
+
if (!apiKey) {
|
|
16
|
+
throw new Error("GOOGLE_CSE_KEY is not set in the environment variables!");
|
|
17
|
+
}
|
|
18
|
+
const cxParam = parameters.cx || cxEnv;
|
|
19
|
+
if (!cxParam) {
|
|
20
|
+
throw new Error("GOOGLE_CSE_CX is not set in the environment variables!");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
q, // query string
|
|
25
|
+
num, // number of results (1..10)
|
|
26
|
+
start, // start index for paging
|
|
27
|
+
safe, // 'off' or 'active'
|
|
28
|
+
dateRestrict, // e.g., 'd1', 'w1', 'm1', 'y1'
|
|
29
|
+
siteSearch, // restrict to site/domain
|
|
30
|
+
siteSearchFilter, // 'e' to exclude or 'i' to include
|
|
31
|
+
searchType, // 'image' for image results
|
|
32
|
+
gl, // country code
|
|
33
|
+
hl, // interface language
|
|
34
|
+
lr, // language restrict, e.g., 'lang_en'
|
|
35
|
+
sort, // sorting expression
|
|
36
|
+
exactTerms, // required terms
|
|
37
|
+
excludeTerms, // terms to exclude
|
|
38
|
+
orTerms, // alternative terms
|
|
39
|
+
fileType, // restrict by filetype
|
|
40
|
+
} = parameters;
|
|
41
|
+
|
|
42
|
+
const params = {
|
|
43
|
+
key: apiKey,
|
|
44
|
+
cx: cxParam,
|
|
45
|
+
q: q || text || '',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Add optional parameters if present
|
|
49
|
+
if (num !== undefined) params.num = num;
|
|
50
|
+
if (start !== undefined) params.start = start;
|
|
51
|
+
if (safe) params.safe = safe; // 'off' | 'active'
|
|
52
|
+
if (dateRestrict) params.dateRestrict = dateRestrict;
|
|
53
|
+
if (siteSearch) params.siteSearch = siteSearch;
|
|
54
|
+
if (siteSearchFilter) params.siteSearchFilter = siteSearchFilter; // 'e' | 'i'
|
|
55
|
+
if (searchType) params.searchType = searchType; // 'image'
|
|
56
|
+
if (gl) params.gl = gl;
|
|
57
|
+
if (hl) params.hl = hl;
|
|
58
|
+
if (lr) params.lr = lr;
|
|
59
|
+
if (sort) params.sort = sort;
|
|
60
|
+
if (exactTerms) params.exactTerms = exactTerms;
|
|
61
|
+
if (excludeTerms) params.excludeTerms = excludeTerms;
|
|
62
|
+
if (orTerms) params.orTerms = orTerms;
|
|
63
|
+
if (fileType) params.fileType = fileType;
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
data: [],
|
|
67
|
+
params
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async execute(text, parameters, prompt, cortexRequest) {
|
|
72
|
+
const requestParameters = this.getRequestParameters(text, parameters, prompt);
|
|
73
|
+
|
|
74
|
+
cortexRequest.data = requestParameters.data;
|
|
75
|
+
cortexRequest.params = requestParameters.params;
|
|
76
|
+
cortexRequest.method = 'GET';
|
|
77
|
+
// URL already points to https://www.googleapis.com/customsearch/v1
|
|
78
|
+
|
|
79
|
+
return this.executeRequest(cortexRequest);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
parseResponse(data) {
|
|
83
|
+
// Return raw JSON string for the pathway/tool to parse
|
|
84
|
+
return JSON.stringify(data);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logRequestData(data, responseData, prompt) {
|
|
88
|
+
// Keep verbose logging consistent
|
|
89
|
+
logger.verbose(`${this.parseResponse(responseData)}`);
|
|
90
|
+
prompt && prompt.debugInfo && (prompt.debugInfo += `\n${JSON.stringify(data)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default GoogleCsePlugin;
|