@aj-archipelago/cortex 1.3.67 → 1.4.0

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 (64) hide show
  1. package/config.js +27 -0
  2. package/helper-apps/cortex-doc-to-pdf/DocToPdfFunction/__init__.py +3 -0
  3. package/helper-apps/cortex-doc-to-pdf/DocToPdfFunction/function.json +20 -0
  4. package/helper-apps/cortex-doc-to-pdf/Dockerfile +46 -0
  5. package/helper-apps/cortex-doc-to-pdf/README.md +408 -0
  6. package/helper-apps/cortex-doc-to-pdf/converter.py +157 -0
  7. package/helper-apps/cortex-doc-to-pdf/docker-compose.yml +23 -0
  8. package/helper-apps/cortex-doc-to-pdf/document_converter.py +181 -0
  9. package/helper-apps/cortex-doc-to-pdf/examples/README.md +252 -0
  10. package/helper-apps/cortex-doc-to-pdf/examples/nodejs-client.js +266 -0
  11. package/helper-apps/cortex-doc-to-pdf/examples/package-lock.json +297 -0
  12. package/helper-apps/cortex-doc-to-pdf/examples/package.json +23 -0
  13. package/helper-apps/cortex-doc-to-pdf/function_app.py +85 -0
  14. package/helper-apps/cortex-doc-to-pdf/host.json +16 -0
  15. package/helper-apps/cortex-doc-to-pdf/request_handlers.py +193 -0
  16. package/helper-apps/cortex-doc-to-pdf/requirements.txt +3 -0
  17. package/helper-apps/cortex-doc-to-pdf/tests/run_tests.sh +26 -0
  18. package/helper-apps/cortex-doc-to-pdf/tests/test_conversion.py +320 -0
  19. package/helper-apps/cortex-doc-to-pdf/tests/test_streaming.py +419 -0
  20. package/helper-apps/cortex-file-handler/package-lock.json +1 -0
  21. package/helper-apps/cortex-file-handler/package.json +1 -0
  22. package/helper-apps/cortex-file-handler/src/services/ConversionService.js +81 -8
  23. package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +54 -7
  24. package/helper-apps/cortex-file-handler/tests/getOperations.test.js +19 -7
  25. package/lib/encodeCache.js +5 -0
  26. package/lib/keyValueStorageClient.js +5 -0
  27. package/lib/logger.js +1 -1
  28. package/lib/pathwayTools.js +8 -1
  29. package/lib/redisSubscription.js +6 -0
  30. package/lib/requestExecutor.js +4 -0
  31. package/lib/util.js +88 -0
  32. package/package.json +1 -1
  33. package/pathways/basePathway.js +3 -3
  34. package/pathways/bing_afagent.js +1 -0
  35. package/pathways/gemini_15_vision.js +1 -1
  36. package/pathways/google_cse.js +2 -2
  37. package/pathways/image_gemini_25.js +85 -0
  38. package/pathways/image_prompt_optimizer_gemini_25.js +149 -0
  39. package/pathways/image_qwen.js +28 -0
  40. package/pathways/image_seedream4.js +26 -0
  41. package/pathways/rag.js +1 -1
  42. package/pathways/rag_jarvis.js +1 -1
  43. package/pathways/system/entity/sys_entity_continue.js +1 -1
  44. package/pathways/system/entity/sys_generator_results.js +1 -1
  45. package/pathways/system/entity/tools/sys_tool_google_search.js +15 -2
  46. package/pathways/system/entity/tools/sys_tool_grok_x_search.js +3 -3
  47. package/pathways/system/entity/tools/sys_tool_image.js +28 -23
  48. package/pathways/system/entity/tools/sys_tool_image_gemini.js +135 -0
  49. package/server/graphql.js +9 -2
  50. package/server/modelExecutor.js +4 -0
  51. package/server/pathwayResolver.js +19 -18
  52. package/server/plugins/claude3VertexPlugin.js +13 -8
  53. package/server/plugins/gemini15ChatPlugin.js +15 -10
  54. package/server/plugins/gemini15VisionPlugin.js +2 -23
  55. package/server/plugins/gemini25ImagePlugin.js +155 -0
  56. package/server/plugins/modelPlugin.js +3 -2
  57. package/server/plugins/openAiChatPlugin.js +6 -6
  58. package/server/plugins/replicateApiPlugin.js +268 -12
  59. package/server/plugins/veoVideoPlugin.js +15 -1
  60. package/server/rest.js +2 -0
  61. package/server/typeDef.js +96 -10
  62. package/tests/integration/apptekTranslatePlugin.integration.test.js +1 -1
  63. package/tests/unit/core/pathwayManager.test.js +2 -4
  64. package/tests/unit/plugins/gemini25ImagePlugin.test.js +294 -0
@@ -107,7 +107,8 @@ export default {
107
107
  try {
108
108
  // Pass-through: call Google CSE with provided args
109
109
  const response = await callPathway('google_cse', {
110
- ...args
110
+ ...args,
111
+ text: args.q
111
112
  }, resolver);
112
113
 
113
114
  if (resolver.errors && resolver.errors.length > 0) {
@@ -117,7 +118,19 @@ export default {
117
118
  return JSON.stringify({ _type: "SearchError", value: errorMessages, recoveryMessage: "This tool failed. You should try the backup tool for this function." });
118
119
  }
119
120
 
120
- const parsedResponse = JSON.parse(response);
121
+ // Check if response is null or empty
122
+ if (!response) {
123
+ logger.error('Google CSE search returned null response');
124
+ return JSON.stringify({ _type: "SearchError", value: ["No response received from Google CSE"], recoveryMessage: "This tool failed. You should try the backup tool for this function." });
125
+ }
126
+
127
+ let parsedResponse;
128
+ try {
129
+ parsedResponse = JSON.parse(response);
130
+ } catch (parseError) {
131
+ logger.error(`Failed to parse Google CSE response: ${parseError.message}`);
132
+ return JSON.stringify({ _type: "SearchError", value: ["Invalid response format from Google CSE"], recoveryMessage: "This tool failed. You should try the backup tool for this function." });
133
+ }
121
134
 
122
135
  const results = [];
123
136
  const items = parsedResponse.items || [];
@@ -10,8 +10,8 @@ export default {
10
10
  inputParameters: {
11
11
  text: '',
12
12
  userMessage: '',
13
- includedHandles: [''],
14
- excludedHandles: [''],
13
+ includedHandles: { type: 'array', items: { type: 'string' }, default: [] },
14
+ excludedHandles: { type: 'array', items: { type: 'string' }, default: [] },
15
15
  minFavorites: 0,
16
16
  minViews: 0,
17
17
  maxResults: 10
@@ -244,4 +244,4 @@ export default {
244
244
  throw e;
245
245
  }
246
246
  }
247
- };
247
+ };
@@ -15,7 +15,7 @@ export default {
15
15
  icon: "🎨",
16
16
  function: {
17
17
  name: "GenerateImage",
18
- description: "Use when asked to create, generate, or generate revisions of visual content. Any time the user asks you for a picture, a selfie, artwork, a drawing or if you want to illustrate something for the user, you can use this tool to generate any sort of image from cartoon to photo realistic. After you have generated the image, you must include the image in your response to show it to the user.",
18
+ description: "Use when asked to create, generate, or generate revisions of visual content. Any time the user asks you for a picture, a selfie, artwork, a drawing or if you want to illustrate something for the user, you can use this tool to generate any sort of image from cartoon to photo realistic. This tool does not display the image to the user - you need to do that with markdown in your response.",
19
19
  parameters: {
20
20
  type: "object",
21
21
  properties: {
@@ -23,16 +23,12 @@ export default {
23
23
  type: "string",
24
24
  description: "A very detailed prompt describing the image you want to create. You should be very specific - explaining subject matter, style, and details about the image including things like camera angle, lens types, lighting, photographic techniques, etc. Any details you can provide to the image creation engine will help it create the most accurate and useful images. The more detailed and descriptive the prompt, the better the result."
25
25
  },
26
- renderText: {
27
- type: "boolean",
28
- description: "Set to true if the image should be optimized to show correct text. This is useful when the user asks for a picture of something that includes specific text as it invokes a different image generation model that is optimized for including text."
29
- },
30
26
  userMessage: {
31
27
  type: "string",
32
28
  description: "A user-friendly message that describes what you're doing with this tool"
33
29
  }
34
30
  },
35
- required: ["detailedInstructions", "renderText", "userMessage"]
31
+ required: ["detailedInstructions", "userMessage"]
36
32
  }
37
33
  }
38
34
  },
@@ -41,7 +37,7 @@ export default {
41
37
  icon: "🔄",
42
38
  function: {
43
39
  name: "ModifyImage",
44
- description: "Use when asked to modify, transform, or edit an existing image. This tool can apply various transformations like style changes, artistic effects, or specific modifications to an image that has been previously uploaded or generated. It takes up to two input images as a reference and outputs a new image based on the instructions.",
40
+ description: "Use when asked to modify, transform, or edit an existing image. This tool can apply various transformations like style changes, artistic effects, or specific modifications to an image that has been previously uploaded or generated. It takes up to two input images as a reference and outputs a new image based on the instructions. This tool does not display the image to the user - you need to do that with markdown in your response.",
45
41
  parameters: {
46
42
  type: "object",
47
43
  properties: {
@@ -53,6 +49,10 @@ export default {
53
49
  type: "string",
54
50
  description: "The second input image URL copied exactly from an image_url field in your chat context if there is one."
55
51
  },
52
+ inputImage3: {
53
+ type: "string",
54
+ description: "The third input image URL copied exactly from an image_url field in your chat context if there is one."
55
+ },
56
56
  detailedInstructions: {
57
57
  type: "string",
58
58
  description: "A very detailed prompt describing how you want to modify the image. Be specific about the changes you want to make, including style changes, artistic effects, or specific modifications. The more detailed and descriptive the prompt, the better the result."
@@ -71,32 +71,37 @@ export default {
71
71
  const pathwayResolver = resolver;
72
72
 
73
73
  try {
74
- let model = "replicate-flux-11-pro";
74
+ let model = "replicate-seedream-4";
75
75
  let prompt = args.detailedInstructions || "";
76
- let numberResults = args.numberResults || 1;
77
- let negativePrompt = args.negativePrompt || "";
78
76
 
79
77
  // If we have an input image, use the flux-kontext-max model
80
- if (args.inputImage) {
81
- model = "replicate-flux-kontext-max";
82
- }
83
-
84
- // If we have two input images, use the multi-image-kontext-max model
85
- if (args.inputImage2) {
86
- model = "replicate-multi-image-kontext-max";
78
+ if (args.inputImage || args.inputImage2 || args.inputImage3) {
79
+ model = "replicate-qwen-image-edit-plus";
87
80
  }
88
81
 
89
82
  pathwayResolver.tool = JSON.stringify({ toolUsed: "image" });
90
- return await callPathway('image_flux', {
83
+
84
+ // Build parameters object, only including image parameters if they have non-empty values
85
+ const params = {
91
86
  ...args,
92
87
  text: prompt,
93
- negativePrompt,
94
- numberResults,
95
88
  model,
96
89
  stream: false,
97
- input_image: args.inputImage,
98
- input_image_2: args.inputImage2,
99
- });
90
+ };
91
+
92
+ if (args.inputImage && args.inputImage.trim()) {
93
+ params.input_image = args.inputImage;
94
+ }
95
+ if (args.inputImage2 && args.inputImage2.trim()) {
96
+ params.input_image_2 = args.inputImage2;
97
+ }
98
+ if (args.inputImage3 && args.inputImage3.trim()) {
99
+ params.input_image_3 = args.inputImage3;
100
+ }
101
+
102
+ // Call appropriate pathway based on model
103
+ const pathwayName = model.includes('seedream') ? 'image_seedream4' : 'image_qwen';
104
+ return await callPathway(pathwayName, params);
100
105
 
101
106
  } catch (e) {
102
107
  pathwayResolver.logError(e.message ?? e);
@@ -0,0 +1,135 @@
1
+ // sys_tool_image_gemini.js
2
+ // Entity tool that creates and modifies images for the entity to show to the user
3
+ import { callPathway } from '../../../../lib/pathwayTools.js';
4
+ import { uploadImageToCloud } from '../../../../lib/util.js';
5
+
6
+ export default {
7
+ prompt: [],
8
+ useInputChunking: false,
9
+ enableDuplicateRequests: false,
10
+ inputParameters: {
11
+ model: 'oai-gpt4o',
12
+ },
13
+ timeout: 300,
14
+ /*
15
+ toolDefinition: [{
16
+ type: "function",
17
+ icon: "🎨",
18
+ function: {
19
+ name: "GenerateImage",
20
+ description: "Use when asked to create, generate, or generate revisions of visual content. Any time the user asks you for a picture, a selfie, artwork, a drawing or if you want to illustrate something for the user, you can use this tool to generate any sort of image from cartoon to photo realistic. After you have generated the image, you must include the image in your response to show it to the user.",
21
+ parameters: {
22
+ type: "object",
23
+ properties: {
24
+ detailedInstructions: {
25
+ type: "string",
26
+ description: "A very detailed prompt describing the image you want to create. You should be very specific - explaining subject matter, style, and details about the image including things like camera angle, lens types, lighting, photographic techniques, etc. Any details you can provide to the image creation engine will help it create the most accurate and useful images. The more detailed and descriptive the prompt, the better the result."
27
+ },
28
+ userMessage: {
29
+ type: "string",
30
+ description: "A user-friendly message that describes what you're doing with this tool"
31
+ }
32
+ },
33
+ required: ["detailedInstructions", "renderText", "userMessage"]
34
+ }
35
+ }
36
+ },
37
+ {
38
+ type: "function",
39
+ icon: "🔄",
40
+ function: {
41
+ name: "ModifyImage",
42
+ description: "Use when asked to modify, transform, or edit an existing image. This tool can apply various transformations like style changes, artistic effects, or specific modifications to an image that has been previously uploaded or generated. It takes up to two input images as a reference and outputs a new image based on the instructions.",
43
+ parameters: {
44
+ type: "object",
45
+ properties: {
46
+ inputImage: {
47
+ type: "string",
48
+ description: "The first image URL copied exactly from an image_url field in your chat context."
49
+ },
50
+ inputImage2: {
51
+ type: "string",
52
+ description: "The second input image URL copied exactly from an image_url field in your chat context if there is one."
53
+ },
54
+ inputImage3: {
55
+ type: "string",
56
+ description: "The third input image URL copied exactly from an image_url field in your chat context if there is one."
57
+ },
58
+ detailedInstructions: {
59
+ type: "string",
60
+ description: "A very detailed prompt describing how you want to modify the image. Be specific about the changes you want to make, including style changes, artistic effects, or specific modifications. The more detailed and descriptive the prompt, the better the result."
61
+ },
62
+ userMessage: {
63
+ type: "string",
64
+ description: "A user-friendly message that describes what you're doing with this tool"
65
+ }
66
+ },
67
+ required: ["inputImage", "detailedInstructions", "userMessage"]
68
+ }
69
+ }
70
+ }],
71
+ */
72
+ executePathway: async ({args, runAllPrompts, resolver}) => {
73
+ const pathwayResolver = resolver;
74
+
75
+ try {
76
+ let model = "gemini-25-flash-image";
77
+ let prompt = args.detailedInstructions || "";
78
+
79
+ // Call the image generation pathway
80
+ let result = await callPathway('image_gemini_25', {
81
+ ...args,
82
+ text: prompt,
83
+ model,
84
+ stream: false,
85
+ input_image: args.inputImage,
86
+ input_image_2: args.inputImage2,
87
+ input_image_3: args.inputImage3,
88
+ optimizePrompt: true,
89
+ }, pathwayResolver);
90
+
91
+ pathwayResolver.tool = JSON.stringify({ toolUsed: "image" });
92
+
93
+ if (pathwayResolver.pathwayResultData) {
94
+ if (pathwayResolver.pathwayResultData.artifacts && Array.isArray(pathwayResolver.pathwayResultData.artifacts)) {
95
+ const uploadedImages = [];
96
+
97
+ // Process each image artifact
98
+ for (const artifact of pathwayResolver.pathwayResultData.artifacts) {
99
+ if (artifact.type === 'image' && artifact.data && artifact.mimeType) {
100
+ try {
101
+ // Upload image to cloud storage
102
+ const imageUrl = await uploadImageToCloud(artifact.data, artifact.mimeType, pathwayResolver);
103
+ uploadedImages.push({
104
+ type: 'image',
105
+ url: imageUrl,
106
+ mimeType: artifact.mimeType
107
+ });
108
+ } catch (uploadError) {
109
+ pathwayResolver.logError(`Failed to upload artifact: ${uploadError.message}`);
110
+ // Keep original artifact as fallback
111
+ uploadedImages.push(artifact);
112
+ }
113
+ } else {
114
+ // Keep non-image artifacts as-is
115
+ uploadedImages.push(artifact);
116
+ }
117
+ }
118
+
119
+ // Return the urls of the uploaded images as text in the result
120
+ result = result + '\n' + uploadedImages.map(image => image.url).join('\n');
121
+ }
122
+ } else {
123
+ // If result is not a CortexResponse, log a warning but return as-is
124
+ pathwayResolver.logWarning('No artifacts to upload');
125
+ result = result + '\n' + 'No images generated';
126
+ }
127
+
128
+ return result;
129
+
130
+ } catch (e) {
131
+ pathwayResolver.logError(e.message ?? e);
132
+ return await callPathway('sys_generator_error', { ...args, text: e.message }, pathwayResolver);
133
+ }
134
+ }
135
+ };
package/server/graphql.js CHANGED
@@ -36,10 +36,17 @@ const getPlugins = (config) => {
36
36
  //if cache is enabled and Redis is available, use it
37
37
  let cache;
38
38
  if (config.get('enableGraphqlCache') && config.get('storageConnectionString')) {
39
- cache = new KeyvAdapter(new Keyv(config.get('storageConnectionString'), {
39
+ const keyvCache = new Keyv(config.get('storageConnectionString'), {
40
40
  ssl: true,
41
41
  abortConnect: false,
42
- }));
42
+ });
43
+
44
+ // Handle Redis connection errors to prevent crashes
45
+ keyvCache.on('error', (error) => {
46
+ logger.error(`GraphQL Keyv Redis connection error: ${error}`);
47
+ });
48
+
49
+ cache = new KeyvAdapter(keyvCache);
43
50
  //caching similar strings, embedding hashing, ... #delta similarity
44
51
  // TODO: custom cache key:
45
52
  // https://www.apollographql.com/docs/apollo-server/performance/cache-backends#implementing-your-own-cache-backend
@@ -21,6 +21,7 @@ import GeminiChatPlugin from './plugins/geminiChatPlugin.js';
21
21
  import GeminiVisionPlugin from './plugins/geminiVisionPlugin.js';
22
22
  import Gemini15ChatPlugin from './plugins/gemini15ChatPlugin.js';
23
23
  import Gemini15VisionPlugin from './plugins/gemini15VisionPlugin.js';
24
+ import Gemini25ImagePlugin from './plugins/gemini25ImagePlugin.js';
24
25
  import AzureBingPlugin from './plugins/azureBingPlugin.js';
25
26
  import Claude3VertexPlugin from './plugins/claude3VertexPlugin.js';
26
27
  import NeuralSpacePlugin from './plugins/neuralSpacePlugin.js';
@@ -103,6 +104,9 @@ class ModelExecutor {
103
104
  case 'GEMINI-1.5-VISION':
104
105
  plugin = new Gemini15VisionPlugin(pathway, model);
105
106
  break;
107
+ case 'GEMINI-2.5-IMAGE':
108
+ plugin = new Gemini25ImagePlugin(pathway, model);
109
+ break;
106
110
  case 'AZURE-BING':
107
111
  plugin = new AzureBingPlugin(pathway, model);
108
112
  break;
@@ -8,8 +8,8 @@ import { Prompt } from './prompt.js';
8
8
  import { getv, setv } from '../lib/keyValueStorageClient.js';
9
9
  import { requestState } from './requestState.js';
10
10
  import { callPathway, addCitationsToResolver } from '../lib/pathwayTools.js';
11
- import { publishRequestProgress } from '../lib/redisSubscription.js';
12
11
  import logger from '../lib/logger.js';
12
+ import { publishRequestProgress } from '../lib/redisSubscription.js';
13
13
  // eslint-disable-next-line import/no-extraneous-dependencies
14
14
  import { createParser } from 'eventsource-parser';
15
15
  import CortexResponse from '../lib/cortexResponse.js';
@@ -158,6 +158,11 @@ class PathwayResolver {
158
158
  return;
159
159
  }
160
160
 
161
+ // Handle CortexResponse objects - merge them into pathwayResultData
162
+ if (responseData && typeof responseData === 'object' && responseData.constructor && responseData.constructor.name === 'CortexResponse') {
163
+ this.pathwayResultData = this.mergeResultData(responseData);
164
+ }
165
+
161
166
  // If the response is a stream, handle it as streaming response
162
167
  if (responseData && typeof responseData.on === 'function') {
163
168
  await this.handleStream(responseData);
@@ -205,7 +210,8 @@ class PathwayResolver {
205
210
  toolCalls: cortexResponse.toolCalls,
206
211
  functionCall: cortexResponse.functionCall,
207
212
  usage: cortexResponse.usage,
208
- finishReason: cortexResponse.finishReason
213
+ finishReason: cortexResponse.finishReason,
214
+ artifacts: cortexResponse.artifacts
209
215
  };
210
216
  newData = cortexData;
211
217
  }
@@ -214,7 +220,7 @@ class PathwayResolver {
214
220
  const merged = { ...currentData, ...newData };
215
221
 
216
222
  // Handle array fields that should be concatenated
217
- const arrayFields = ['citations', 'toolCalls'];
223
+ const arrayFields = ['citations', 'toolCalls', 'artifacts'];
218
224
  for (const field of arrayFields) {
219
225
  const currentArray = currentData[field] || [];
220
226
  const newArray = newData[field] || [];
@@ -593,7 +599,7 @@ class PathwayResolver {
593
599
  if (previousResult) {
594
600
  previousResult = this.truncate(previousResult, 2 * this.chunkMaxTokenLength);
595
601
  }
596
- result = await this.applyPrompt(this.prompts[i], null, currentParameters);
602
+ result = await this.applyPrompt(this.prompts[i], text, currentParameters);
597
603
  } else {
598
604
  // Limit context to N characters
599
605
  if (previousResult) {
@@ -664,20 +670,15 @@ class PathwayResolver {
664
670
  }
665
671
  let result = '';
666
672
 
667
- // If this text is empty, skip applying the prompt as it will likely be a nonsensical result
668
- if (!/^\s*$/.test(text) || parameters?.file || parameters?.inputVector || this?.modelName.includes('cognitive')) {
669
- result = await this.modelExecutor.execute(text, {
670
- ...parameters,
671
- ...this.savedContext,
672
- memorySelf: this.memorySelf,
673
- memoryDirectives: this.memoryDirectives,
674
- memoryTopics: this.memoryTopics,
675
- memoryUser: this.memoryUser,
676
- memoryContext: this.memoryContext
677
- }, prompt, this);
678
- } else {
679
- result = text;
680
- }
673
+ result = await this.modelExecutor.execute(text, {
674
+ ...parameters,
675
+ ...this.savedContext,
676
+ memorySelf: this.memorySelf,
677
+ memoryDirectives: this.memoryDirectives,
678
+ memoryTopics: this.memoryTopics,
679
+ memoryUser: this.memoryUser,
680
+ memoryContext: this.memoryContext
681
+ }, prompt, this);
681
682
 
682
683
  requestState[this.requestId].completedCount++;
683
684
 
@@ -304,12 +304,17 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
304
304
  requestParameters.messages = modifiedMessages;
305
305
 
306
306
  // Convert OpenAI tools format to Claude format if present
307
- if (typeof parameters.tools === 'string') {
308
- parameters.tools = JSON.parse(parameters.tools);
307
+ let toolsArray = parameters.tools;
308
+ if (typeof toolsArray === 'string') {
309
+ try {
310
+ toolsArray = JSON.parse(toolsArray);
311
+ } catch (e) {
312
+ toolsArray = [];
313
+ }
309
314
  }
310
315
 
311
- if (parameters.tools) {
312
- requestParameters.tools = parameters.tools.map(tool => {
316
+ if (toolsArray && Array.isArray(toolsArray) && toolsArray.length > 0) {
317
+ requestParameters.tools = toolsArray.map(tool => {
313
318
  if (tool.type === 'function') {
314
319
  return {
315
320
  name: tool.function.name,
@@ -325,14 +330,13 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
325
330
  });
326
331
  }
327
332
 
333
+ // Handle tool_choice parameter conversion from OpenAI format to Claude format
328
334
  if (parameters.tool_choice) {
329
- // Convert OpenAI tool_choice format to Claude format
330
335
  let toolChoice = parameters.tool_choice;
331
336
 
332
- // Handle JSON string from REST endpoint
337
+ // Parse JSON string if needed
333
338
  if (typeof toolChoice === 'string') {
334
339
  try {
335
- // Try to parse as JSON first
336
340
  toolChoice = JSON.parse(toolChoice);
337
341
  } catch (e) {
338
342
  // If not JSON, handle as simple string values: auto, required, none
@@ -343,11 +347,12 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
343
347
  } else if (toolChoice === 'none') {
344
348
  requestParameters.tool_choice = { type: 'none' };
345
349
  }
350
+ toolChoice = null; // Prevent further processing
346
351
  }
347
352
  }
348
353
 
349
354
  // Handle parsed object
350
- if (toolChoice.type === "function") {
355
+ if (toolChoice && toolChoice.type === "function") {
351
356
  // Handle function-specific tool choice
352
357
  requestParameters.tool_choice = {
353
358
  type: "tool",
@@ -138,16 +138,16 @@ class Gemini15ChatPlugin extends ModelPlugin {
138
138
  }
139
139
 
140
140
  const requestParameters = {
141
- contents: requestMessages,
142
- generationConfig: {
143
- temperature: this.temperature || 0.7,
144
- maxOutputTokens: max_tokens || this.getModelMaxReturnTokens(),
145
- topP: parameters.topP || 0.95,
146
- topK: parameters.topK || 40,
147
- },
148
- ...(geminiSafetySettings ? {safety_settings: geminiSafetySettings} : {}),
149
- ...(system ? {systemInstruction: system} : {}),
150
- ...(geminiTools ? {tools: geminiTools} : {})
141
+ contents: requestMessages,
142
+ generationConfig: {
143
+ temperature: this.temperature || 0.7,
144
+ maxOutputTokens: max_tokens || this.getModelMaxReturnTokens(),
145
+ topP: parameters.topP || 0.95,
146
+ topK: parameters.topK || 40,
147
+ },
148
+ ...(geminiSafetySettings ? {safety_settings: geminiSafetySettings} : {}),
149
+ ...(system ? {systemInstruction: system} : {}),
150
+ ...(geminiTools ? {tools: geminiTools} : {})
151
151
  };
152
152
 
153
153
  return requestParameters;
@@ -155,6 +155,11 @@ class Gemini15ChatPlugin extends ModelPlugin {
155
155
 
156
156
  // Parse the response from the new Chat API
157
157
  parseResponse(data) {
158
+ // Handle CortexResponse objects by returning them as-is
159
+ if (data && typeof data === 'object' && data.constructor && data.constructor.name === 'CortexResponse') {
160
+ return data;
161
+ }
162
+
158
163
  // If data is not an array, return it directly
159
164
  let dataToMerge = [];
160
165
  if (data && data.contents && Array.isArray(data.contents)) {
@@ -204,34 +204,13 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
204
204
  }
205
205
  }
206
206
 
207
- if (toolsArray && Array.isArray(toolsArray)) {
207
+ if (toolsArray && Array.isArray(toolsArray) && toolsArray.length > 0) {
208
208
  convertedTools = this.convertOpenAIToolsToGemini(toolsArray);
209
209
  }
210
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
211
  const baseParameters = super.getRequestParameters(text, parameters, prompt, cortexRequest);
228
212
 
229
- // Restore original geminiTools
230
- if (cortexRequest?.pathway && originalGeminiTools !== undefined) {
231
- cortexRequest.pathway.geminiTools = originalGeminiTools;
232
- }
233
-
234
- if (convertedTools.length > 0) {
213
+ if (convertedTools[0]?.functionDeclarations?.length > 0) {
235
214
  baseParameters.tools = convertedTools;
236
215
 
237
216
  // Handle tool_choice parameter - convert OpenAI format to Gemini toolConfig