@aj-archipelago/cortex 1.3.62 → 1.3.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/.github/workflows/cortex-file-handler-test.yml +61 -0
  2. package/README.md +31 -7
  3. package/config/default.example.json +15 -0
  4. package/config.js +133 -12
  5. package/helper-apps/cortex-autogen2/DigiCertGlobalRootCA.crt.pem +22 -0
  6. package/helper-apps/cortex-autogen2/Dockerfile +31 -0
  7. package/helper-apps/cortex-autogen2/Dockerfile.worker +41 -0
  8. package/helper-apps/cortex-autogen2/README.md +183 -0
  9. package/helper-apps/cortex-autogen2/__init__.py +1 -0
  10. package/helper-apps/cortex-autogen2/agents.py +131 -0
  11. package/helper-apps/cortex-autogen2/docker-compose.yml +20 -0
  12. package/helper-apps/cortex-autogen2/function_app.py +55 -0
  13. package/helper-apps/cortex-autogen2/host.json +15 -0
  14. package/helper-apps/cortex-autogen2/main.py +126 -0
  15. package/helper-apps/cortex-autogen2/poetry.lock +3652 -0
  16. package/helper-apps/cortex-autogen2/pyproject.toml +36 -0
  17. package/helper-apps/cortex-autogen2/requirements.txt +20 -0
  18. package/helper-apps/cortex-autogen2/send_task.py +105 -0
  19. package/helper-apps/cortex-autogen2/services/__init__.py +1 -0
  20. package/helper-apps/cortex-autogen2/services/azure_queue.py +85 -0
  21. package/helper-apps/cortex-autogen2/services/redis_publisher.py +153 -0
  22. package/helper-apps/cortex-autogen2/task_processor.py +488 -0
  23. package/helper-apps/cortex-autogen2/tools/__init__.py +24 -0
  24. package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +175 -0
  25. package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +601 -0
  26. package/helper-apps/cortex-autogen2/tools/coding_tools.py +72 -0
  27. package/helper-apps/cortex-autogen2/tools/download_tools.py +48 -0
  28. package/helper-apps/cortex-autogen2/tools/file_tools.py +545 -0
  29. package/helper-apps/cortex-autogen2/tools/search_tools.py +646 -0
  30. package/helper-apps/cortex-azure-cleaner/README.md +36 -0
  31. package/helper-apps/cortex-file-converter/README.md +93 -0
  32. package/helper-apps/cortex-file-converter/key_to_pdf.py +104 -0
  33. package/helper-apps/cortex-file-converter/list_blob_extensions.py +89 -0
  34. package/helper-apps/cortex-file-converter/process_azure_keynotes.py +181 -0
  35. package/helper-apps/cortex-file-converter/requirements.txt +1 -0
  36. package/helper-apps/cortex-file-handler/.env.test.azure.ci +7 -0
  37. package/helper-apps/cortex-file-handler/.env.test.azure.sample +1 -1
  38. package/helper-apps/cortex-file-handler/.env.test.gcs.ci +10 -0
  39. package/helper-apps/cortex-file-handler/.env.test.gcs.sample +2 -2
  40. package/helper-apps/cortex-file-handler/INTERFACE.md +41 -0
  41. package/helper-apps/cortex-file-handler/package.json +1 -1
  42. package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +41 -17
  43. package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +30 -15
  44. package/helper-apps/cortex-file-handler/scripts/test-azure.sh +32 -6
  45. package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +24 -2
  46. package/helper-apps/cortex-file-handler/scripts/validate-env.js +128 -0
  47. package/helper-apps/cortex-file-handler/src/blobHandler.js +161 -51
  48. package/helper-apps/cortex-file-handler/src/constants.js +3 -0
  49. package/helper-apps/cortex-file-handler/src/fileChunker.js +10 -8
  50. package/helper-apps/cortex-file-handler/src/index.js +116 -9
  51. package/helper-apps/cortex-file-handler/src/redis.js +61 -1
  52. package/helper-apps/cortex-file-handler/src/services/ConversionService.js +11 -8
  53. package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +2 -2
  54. package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +88 -6
  55. package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +58 -0
  56. package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +25 -5
  57. package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +9 -0
  58. package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +120 -16
  59. package/helper-apps/cortex-file-handler/src/start.js +27 -17
  60. package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +52 -1
  61. package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +40 -0
  62. package/helper-apps/cortex-file-handler/tests/checkHashShortLived.test.js +553 -0
  63. package/helper-apps/cortex-file-handler/tests/cleanup.test.js +46 -52
  64. package/helper-apps/cortex-file-handler/tests/containerConversionFlow.test.js +451 -0
  65. package/helper-apps/cortex-file-handler/tests/containerNameParsing.test.js +229 -0
  66. package/helper-apps/cortex-file-handler/tests/containerParameterFlow.test.js +392 -0
  67. package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +7 -2
  68. package/helper-apps/cortex-file-handler/tests/deleteOperations.test.js +348 -0
  69. package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +23 -2
  70. package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +11 -5
  71. package/helper-apps/cortex-file-handler/tests/getOperations.test.js +58 -24
  72. package/helper-apps/cortex-file-handler/tests/postOperations.test.js +11 -4
  73. package/helper-apps/cortex-file-handler/tests/shortLivedUrlConversion.test.js +225 -0
  74. package/helper-apps/cortex-file-handler/tests/start.test.js +8 -12
  75. package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +80 -0
  76. package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +388 -22
  77. package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -0
  78. package/lib/cortexResponse.js +153 -0
  79. package/lib/entityConstants.js +21 -3
  80. package/lib/logger.js +21 -4
  81. package/lib/pathwayTools.js +28 -9
  82. package/lib/util.js +49 -0
  83. package/package.json +1 -1
  84. package/pathways/basePathway.js +1 -0
  85. package/pathways/bing_afagent.js +54 -1
  86. package/pathways/call_tools.js +2 -3
  87. package/pathways/chat_jarvis.js +1 -1
  88. package/pathways/google_cse.js +27 -0
  89. package/pathways/grok_live_search.js +18 -0
  90. package/pathways/system/entity/memory/sys_memory_lookup_required.js +1 -0
  91. package/pathways/system/entity/memory/sys_memory_required.js +1 -0
  92. package/pathways/system/entity/memory/sys_search_memory.js +1 -0
  93. package/pathways/system/entity/sys_entity_agent.js +56 -4
  94. package/pathways/system/entity/sys_generator_quick.js +1 -0
  95. package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +26 -0
  96. package/pathways/system/entity/tools/sys_tool_google_search.js +141 -0
  97. package/pathways/system/entity/tools/sys_tool_grok_x_search.js +237 -0
  98. package/pathways/system/entity/tools/sys_tool_image.js +1 -1
  99. package/pathways/system/rest_streaming/sys_claude_37_sonnet.js +21 -0
  100. package/pathways/system/rest_streaming/sys_claude_41_opus.js +21 -0
  101. package/pathways/system/rest_streaming/sys_claude_4_sonnet.js +21 -0
  102. package/pathways/system/rest_streaming/sys_google_gemini_25_flash.js +25 -0
  103. package/pathways/system/rest_streaming/{sys_google_gemini_chat.js → sys_google_gemini_25_pro.js} +6 -4
  104. package/pathways/system/rest_streaming/sys_grok_4.js +23 -0
  105. package/pathways/system/rest_streaming/sys_grok_4_fast_non_reasoning.js +23 -0
  106. package/pathways/system/rest_streaming/sys_grok_4_fast_reasoning.js +23 -0
  107. package/pathways/system/rest_streaming/sys_openai_chat.js +3 -0
  108. package/pathways/system/rest_streaming/sys_openai_chat_gpt41.js +22 -0
  109. package/pathways/system/rest_streaming/sys_openai_chat_gpt41_mini.js +21 -0
  110. package/pathways/system/rest_streaming/sys_openai_chat_gpt41_nano.js +21 -0
  111. package/pathways/system/rest_streaming/{sys_claude_35_sonnet.js → sys_openai_chat_gpt4_omni.js} +6 -4
  112. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_omni_mini.js +21 -0
  113. package/pathways/system/rest_streaming/{sys_claude_3_haiku.js → sys_openai_chat_gpt5.js} +7 -5
  114. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_chat.js +21 -0
  115. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_mini.js +21 -0
  116. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_nano.js +21 -0
  117. package/pathways/system/rest_streaming/{sys_openai_chat_o1.js → sys_openai_chat_o3.js} +6 -3
  118. package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +3 -0
  119. package/pathways/system/workspaces/run_workspace_prompt.js +99 -0
  120. package/pathways/vision.js +1 -1
  121. package/server/graphql.js +1 -1
  122. package/server/modelExecutor.js +8 -0
  123. package/server/pathwayResolver.js +166 -16
  124. package/server/pathwayResponseParser.js +16 -8
  125. package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
  126. package/server/plugins/claude3VertexPlugin.js +193 -45
  127. package/server/plugins/gemini15ChatPlugin.js +21 -0
  128. package/server/plugins/gemini15VisionPlugin.js +360 -0
  129. package/server/plugins/googleCsePlugin.js +94 -0
  130. package/server/plugins/grokVisionPlugin.js +365 -0
  131. package/server/plugins/modelPlugin.js +3 -1
  132. package/server/plugins/openAiChatPlugin.js +106 -13
  133. package/server/plugins/openAiVisionPlugin.js +42 -30
  134. package/server/resolver.js +28 -4
  135. package/server/rest.js +270 -53
  136. package/server/typeDef.js +1 -0
  137. package/tests/{mocks.js → helpers/mocks.js} +5 -2
  138. package/tests/{server.js → helpers/server.js} +2 -2
  139. package/tests/helpers/sseAssert.js +23 -0
  140. package/tests/helpers/sseClient.js +73 -0
  141. package/tests/helpers/subscriptionAssert.js +11 -0
  142. package/tests/helpers/subscriptions.js +113 -0
  143. package/tests/{sublong.srt → integration/features/translate/sublong.srt} +4543 -4543
  144. package/tests/integration/features/translate/translate_chunking_stream.test.js +100 -0
  145. package/tests/{translate_srt.test.js → integration/features/translate/translate_srt.test.js} +2 -2
  146. package/tests/integration/graphql/async/stream/agentic.test.js +477 -0
  147. package/tests/integration/graphql/async/stream/subscription_streaming.test.js +62 -0
  148. package/tests/integration/graphql/async/stream/sys_entity_start_streaming.test.js +71 -0
  149. package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +56 -0
  150. package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +66 -0
  151. package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +56 -0
  152. package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +72 -0
  153. package/tests/integration/graphql/features/google/sysToolGoogleSearch.test.js +96 -0
  154. package/tests/integration/graphql/features/grok/grok.test.js +688 -0
  155. package/tests/integration/graphql/features/grok/grok_x_search_tool.test.js +354 -0
  156. package/tests/{main.test.js → integration/graphql/features/main.test.js} +1 -1
  157. package/tests/{call_tools.test.js → integration/graphql/features/tools/call_tools.test.js} +2 -2
  158. package/tests/{vision.test.js → integration/graphql/features/vision/vision.test.js} +1 -1
  159. package/tests/integration/graphql/subscriptions/connection.test.js +26 -0
  160. package/tests/{openai_api.test.js → integration/rest/oai/openai_api.test.js} +63 -238
  161. package/tests/integration/rest/oai/tool_calling_api.test.js +343 -0
  162. package/tests/integration/rest/oai/tool_calling_streaming.test.js +85 -0
  163. package/tests/integration/rest/vendors/claude_streaming.test.js +47 -0
  164. package/tests/integration/rest/vendors/claude_tool_calling_streaming.test.js +75 -0
  165. package/tests/integration/rest/vendors/gemini_streaming.test.js +47 -0
  166. package/tests/integration/rest/vendors/gemini_tool_calling_streaming.test.js +75 -0
  167. package/tests/integration/rest/vendors/grok_streaming.test.js +55 -0
  168. package/tests/integration/rest/vendors/grok_tool_calling_streaming.test.js +75 -0
  169. package/tests/{azureAuthTokenHelper.test.js → unit/core/azureAuthTokenHelper.test.js} +1 -1
  170. package/tests/{chunkfunction.test.js → unit/core/chunkfunction.test.js} +2 -2
  171. package/tests/{config.test.js → unit/core/config.test.js} +3 -3
  172. package/tests/{encodeCache.test.js → unit/core/encodeCache.test.js} +1 -1
  173. package/tests/{fastLruCache.test.js → unit/core/fastLruCache.test.js} +1 -1
  174. package/tests/{handleBars.test.js → unit/core/handleBars.test.js} +1 -1
  175. package/tests/{memoryfunction.test.js → unit/core/memoryfunction.test.js} +2 -2
  176. package/tests/unit/core/mergeResolver.test.js +952 -0
  177. package/tests/{parser.test.js → unit/core/parser.test.js} +3 -3
  178. package/tests/unit/core/pathwayResolver.test.js +187 -0
  179. package/tests/{requestMonitor.test.js → unit/core/requestMonitor.test.js} +1 -1
  180. package/tests/{requestMonitorDurationEstimator.test.js → unit/core/requestMonitorDurationEstimator.test.js} +1 -1
  181. package/tests/{truncateMessages.test.js → unit/core/truncateMessages.test.js} +3 -3
  182. package/tests/{util.test.js → unit/core/util.test.js} +1 -1
  183. package/tests/{apptekTranslatePlugin.test.js → unit/plugins/apptekTranslatePlugin.test.js} +3 -3
  184. package/tests/{azureFoundryAgents.test.js → unit/plugins/azureFoundryAgents.test.js} +136 -1
  185. package/tests/{claude3VertexPlugin.test.js → unit/plugins/claude3VertexPlugin.test.js} +32 -10
  186. package/tests/{claude3VertexToolConversion.test.js → unit/plugins/claude3VertexToolConversion.test.js} +3 -3
  187. package/tests/unit/plugins/googleCsePlugin.test.js +111 -0
  188. package/tests/unit/plugins/grokVisionPlugin.test.js +1392 -0
  189. package/tests/{modelPlugin.test.js → unit/plugins/modelPlugin.test.js} +3 -3
  190. package/tests/{multimodal_conversion.test.js → unit/plugins/multimodal_conversion.test.js} +4 -4
  191. package/tests/{openAiChatPlugin.test.js → unit/plugins/openAiChatPlugin.test.js} +13 -4
  192. package/tests/{openAiToolPlugin.test.js → unit/plugins/openAiToolPlugin.test.js} +35 -27
  193. package/tests/{tokenHandlingTests.test.js → unit/plugins/tokenHandlingTests.test.js} +5 -5
  194. package/tests/{translate_apptek.test.js → unit/plugins/translate_apptek.test.js} +3 -3
  195. package/tests/{streaming.test.js → unit/plugins.streaming/plugin_stream_events.test.js} +19 -58
  196. package/helper-apps/mogrt-handler/tests/test-files/test.gif +0 -1
  197. package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +0 -1
  198. package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +0 -1
  199. package/pathways/system/rest_streaming/sys_openai_chat_gpt4.js +0 -19
  200. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_32.js +0 -19
  201. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_turbo.js +0 -19
  202. package/pathways/system/workspaces/run_claude35_sonnet.js +0 -21
  203. package/pathways/system/workspaces/run_claude3_haiku.js +0 -20
  204. package/pathways/system/workspaces/run_gpt35turbo.js +0 -20
  205. package/pathways/system/workspaces/run_gpt4.js +0 -20
  206. package/pathways/system/workspaces/run_gpt4_32.js +0 -20
  207. package/tests/agentic.test.js +0 -256
  208. package/tests/pathwayResolver.test.js +0 -78
  209. package/tests/subscription.test.js +0 -387
  210. /package/tests/{subchunk.srt → integration/features/translate/subchunk.srt} +0 -0
  211. /package/tests/{subhorizontal.srt → integration/features/translate/subhorizontal.srt} +0 -0
@@ -1,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;