@aj-archipelago/cortex 1.2.1 → 1.3.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 (79) hide show
  1. package/config.js +38 -11
  2. package/helper-apps/cortex-autogen/OAI_CONFIG_LIST +2 -1
  3. package/helper-apps/cortex-autogen/agents.py +387 -0
  4. package/helper-apps/cortex-autogen/agents_extra.py +14 -0
  5. package/helper-apps/cortex-autogen/config.py +18 -0
  6. package/helper-apps/cortex-autogen/data_operations.py +29 -0
  7. package/helper-apps/cortex-autogen/function_app.py +6 -3
  8. package/helper-apps/cortex-autogen/main.py +4 -4
  9. package/helper-apps/cortex-autogen/prompts.py +196 -0
  10. package/helper-apps/cortex-autogen/prompts_extra.py +5 -0
  11. package/helper-apps/cortex-autogen/requirements.txt +2 -1
  12. package/helper-apps/cortex-autogen/search.py +83 -0
  13. package/helper-apps/cortex-autogen/test.sh +40 -0
  14. package/helper-apps/cortex-autogen/utils.py +78 -0
  15. package/lib/handleBars.js +25 -0
  16. package/lib/logger.js +2 -0
  17. package/lib/util.js +3 -1
  18. package/package.json +1 -1
  19. package/pathways/chat_code.js +1 -1
  20. package/pathways/chat_context.js +1 -1
  21. package/pathways/chat_jarvis.js +1 -1
  22. package/pathways/chat_persist.js +1 -1
  23. package/pathways/chat_title.js +25 -0
  24. package/pathways/image_recraft.js +1 -1
  25. package/pathways/rag.js +1 -1
  26. package/pathways/rag_jarvis.js +1 -1
  27. package/pathways/rag_search_helper.js +1 -1
  28. package/pathways/system/entity/memory/sys_memory_manager.js +71 -0
  29. package/pathways/system/entity/memory/sys_memory_required.js +21 -0
  30. package/pathways/system/entity/memory/sys_memory_update.js +190 -0
  31. package/pathways/system/entity/memory/sys_read_memory.js +37 -0
  32. package/pathways/system/entity/memory/sys_save_memory.js +60 -0
  33. package/pathways/system/entity/shared/sys_entity_constants.js +24 -0
  34. package/pathways/system/entity/sys_entity_continue.js +57 -0
  35. package/pathways/system/entity/sys_entity_start.js +218 -0
  36. package/pathways/system/entity/sys_generator_error.js +20 -0
  37. package/pathways/system/entity/sys_generator_expert.js +26 -0
  38. package/pathways/system/entity/sys_generator_image.js +127 -0
  39. package/pathways/system/entity/sys_generator_quick.js +19 -0
  40. package/pathways/system/entity/sys_generator_reasoning.js +27 -0
  41. package/pathways/system/entity/sys_generator_results.js +304 -0
  42. package/pathways/system/entity/sys_generator_video_vision.js +27 -0
  43. package/pathways/system/entity/sys_image_prompt_builder.js +35 -0
  44. package/pathways/system/entity/sys_query_builder.js +101 -0
  45. package/pathways/system/entity/sys_router_code.js +37 -0
  46. package/pathways/system/entity/sys_router_tool.js +64 -0
  47. package/pathways/{sys_claude_35_sonnet.js → system/rest_streaming/sys_claude_35_sonnet.js} +1 -1
  48. package/pathways/{sys_claude_3_haiku.js → system/rest_streaming/sys_claude_3_haiku.js} +1 -1
  49. package/pathways/{sys_google_chat.js → system/rest_streaming/sys_google_chat.js} +1 -1
  50. package/pathways/{sys_google_code_chat.js → system/rest_streaming/sys_google_code_chat.js} +1 -1
  51. package/pathways/{sys_google_gemini_chat.js → system/rest_streaming/sys_google_gemini_chat.js} +1 -1
  52. package/pathways/{sys_openai_chat.js → system/rest_streaming/sys_openai_chat.js} +1 -1
  53. package/pathways/{sys_openai_chat_16.js → system/rest_streaming/sys_openai_chat_16.js} +1 -1
  54. package/pathways/{sys_openai_chat_gpt4.js → system/rest_streaming/sys_openai_chat_gpt4.js} +1 -1
  55. package/pathways/{sys_openai_chat_gpt4_32.js → system/rest_streaming/sys_openai_chat_gpt4_32.js} +1 -1
  56. package/pathways/{sys_openai_chat_gpt4_turbo.js → system/rest_streaming/sys_openai_chat_gpt4_turbo.js} +1 -1
  57. package/pathways/{sys_parse_numbered_object_list.js → system/sys_parse_numbered_object_list.js} +2 -2
  58. package/pathways/{sys_repair_json.js → system/sys_repair_json.js} +1 -1
  59. package/pathways/{run_claude35_sonnet.js → system/workspaces/run_claude35_sonnet.js} +1 -1
  60. package/pathways/{run_claude3_haiku.js → system/workspaces/run_claude3_haiku.js} +1 -1
  61. package/pathways/{run_gpt35turbo.js → system/workspaces/run_gpt35turbo.js} +1 -1
  62. package/pathways/{run_gpt4.js → system/workspaces/run_gpt4.js} +1 -1
  63. package/pathways/{run_gpt4_32.js → system/workspaces/run_gpt4_32.js} +1 -1
  64. package/server/pathwayResolver.js +62 -10
  65. package/server/plugins/azureCognitivePlugin.js +14 -1
  66. package/server/plugins/claude3VertexPlugin.js +25 -15
  67. package/server/plugins/gemini15ChatPlugin.js +1 -1
  68. package/server/plugins/geminiChatPlugin.js +1 -1
  69. package/server/plugins/modelPlugin.js +10 -1
  70. package/server/plugins/openAiChatPlugin.js +4 -3
  71. package/server/plugins/openAiDallE3Plugin.js +12 -4
  72. package/server/plugins/openAiVisionPlugin.js +1 -2
  73. package/server/plugins/replicateApiPlugin.js +46 -16
  74. package/tests/multimodal_conversion.test.js +6 -8
  75. package/helper-apps/cortex-autogen/myautogen.py +0 -317
  76. package/helper-apps/cortex-autogen/prompt.txt +0 -0
  77. package/helper-apps/cortex-autogen/prompt_summary.txt +0 -37
  78. package/pathways/index.js +0 -154
  79. /package/pathways/{sys_openai_completion.js → system/rest_streaming/sys_openai_completion.js} +0 -0
@@ -0,0 +1,37 @@
1
+ import { Prompt } from '../../../server/prompt.js';
2
+
3
+ export default {
4
+ inputParameters: {
5
+ chatHistory: [{role: '', content: []}],
6
+ },
7
+ prompt:
8
+ [
9
+ new Prompt({ messages: [
10
+ {
11
+ "role": "system",
12
+ "content": `{{renderTemplate AI_CONVERSATION_HISTORY}}
13
+
14
+ Instructions: You are part of an AI entity named {{{aiName}}}. Your task is to analyze the conversation history to determine whether a coding task has been requested or if the user's needs can be addressed only by executing the code. Output a JSON object with three fields:
15
+
16
+ 1. "codingRequired": Boolean. Set to true if the user asks for or needs code execution. Otherwise, set to false.
17
+
18
+ 2. "codingMessage": String. If codingRequired is true, provide a message to notify the user that a coding task is being handled. Otherwise, leave this as an empty string.
19
+
20
+ 3. "codingTask": String. If codingRequired is true, provide a task description for the coding agent. Make sure to pass all all the information needed as this is the only message that coding agent receives and is aware of. Just provide the task and let the agent decide how to solve or what do to. Never make any assumptions about the agent's knowledge or capabilities. Never say assume this or that. Never give example by yourself, let coding agent decide on that. Provide the task do not ask questions or say anything will further be provided by the user. If codingRequired is false, leave this as an empty string.
21
+
22
+ 4. "codingTaskKeywords": If codingRequired is true, provide a keywords for Azure Cognitive Search to help the coding agent find the relevant code snippets. It will use these keywords as is to search for the code snippets. If codingRequired is false, leave this as an empty string.
23
+
24
+ General guidelines:
25
+ - AJ is for AL Jazeera, AJA is for AJ Arabic, AJE is for AJ English
26
+ - If agent needs to search in task it can use Bing Search
27
+
28
+ Always output just the valid JSON object with all these fields.`,
29
+ },
30
+ {"role": "user", "content": "Analyze the provided conversation history and determine if you should use code executionto respond to the user. Generate a JSON object to indicate if it is needed."},
31
+ ]}),
32
+ ],
33
+ model: 'oai-gpt4o',
34
+ useInputChunking: false,
35
+ enableDuplicateRequests: false,
36
+ json: true,
37
+ }
@@ -0,0 +1,64 @@
1
+ import { Prompt } from '../../../server/prompt.js';
2
+
3
+ export default {
4
+ inputParameters: {
5
+ chatHistory: [{role: '', content: []}],
6
+ model: "oai-gpt4o",
7
+ aiName: "Jarvis",
8
+ },
9
+ prompt:
10
+ [
11
+ new Prompt({ messages: [
12
+ {
13
+ "role": "system",
14
+ "content": `{{renderTemplate AI_CONVERSATION_HISTORY}}
15
+
16
+ Instructions: You are part of an AI entity named {{{aiName}}}. Your task is to determine whether to use a tool based on the conversation history and user's request. Prioritize the latest message from the user in the conversation history when making your decision.
17
+
18
+ Available tools and their specific use cases:
19
+
20
+ 1. Search: Use for current events, news, fact-checking, and information requiring citation. This tool can search the internet, all Al Jazeera news articles and the latest news wires from multiple sources. Only search when necessary for current events, user documents, latest news, or complex topics needing grounding. Don't search for remembered information or general knowledge within your capabilities.
21
+
22
+ 2. Document: Access user's personal document index. Use for user-specific uploaded information. If user refers vaguely to "this document/file/article" without context, search the personal index.
23
+
24
+ 3. Write: Engage for any task related to composing, editing, or refining written content. This includes articles, essays, scripts, or any form of textual creation or modification. If you need to search for information or look at a document first, use the Search or Document tools. This tool is just to create or modify content.
25
+
26
+ 4. Image: Use when asked to create, generate, or manipulate visual content. This covers photographs, illustrations, diagrams, or any other type of image. Always use this tool for image requests unless explicitly directed to use CodeExecution.
27
+
28
+ 5. Code: Engage for any programming-related tasks, including creating, modifying, reviewing, or explaining code. Use for general coding discussions or when specific programming expertise is needed.
29
+
30
+ 6. CodeExecution: Use when explicitly asked to run or execute code, or when a coding agent is needed to perform specific tasks that require code execution like data analysis, data processing, or business intelligence tasks.
31
+
32
+ 7. Reason: Employ for reasoning, scientific analysis, evaluating evidence, strategic planning, problem-solving, logic puzzles, mathematical calculations, or any questions that require careful thought or complex choices. Also use when deep, step-by-step reasoning is required.
33
+
34
+ 8. PDF: Use specifically for processing and answering questions about PDF file content.
35
+
36
+ 9. Vision: Engage for analyzing and responding to queries about image files (jpg, gif, bmp, png, etc).
37
+
38
+ 10. Video: Use for processing and answering questions about video or audio file content.
39
+
40
+ 11. Clarify: Use when you must have more information from the user to determine which tool to use. In this case your tool message should be one or more questions to the user to clarify their request.
41
+
42
+ Tool Selection Guidelines:
43
+ - Prioritize the most specific tool for the task at hand.
44
+ - If multiple tools seem applicable, choose the one most central to the user's request.
45
+ - For ambiguous requests, consider using the Reason tool to plan a multi-step approach.
46
+ - Always use the Image tool for image generation unless explicitly directed to use CodeExecution.
47
+ - If the user explicitly asks you to use a tool, you must use it.
48
+
49
+ Decision Output:
50
+ If you decide to use a tool, return a JSON object in this format:
51
+ {"toolRequired": true, "toolFunction": "toolName", "toolMessage": "message to the user to wait a moment while you work", "toolReason": "detailed explanation of why this tool was chosen"}
52
+
53
+ If no tool is required, return:
54
+ {"toolRequired": false, "toolReason": "explanation of why no tool was necessary"}
55
+
56
+ Return only the JSON object without additional commentary.`,
57
+ },
58
+ {"role": "user", "content": "Analyze the provided conversation history and determine if you should use any of the tools to respond to the user. Generate a JSON object to indicate if a tool is needed."},
59
+ ]}),
60
+ ],
61
+ useInputChunking: false,
62
+ enableDuplicateRequests: false,
63
+ json: true,
64
+ }
@@ -1,7 +1,7 @@
1
1
  // sys_claude_35_sonnet.js
2
2
  // override handler for claude-35-sonnet
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_claude_3_haiku.js
2
2
  // override handler for claude-3-haiku
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_google_chat.js
2
2
  // override handler for palm-chat
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_google_code_chat.js
2
2
  // override handler for palm-code-chat
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_google_gemini_chat.js
2
2
  // override handler for gemini-chat
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_openai_chat.js
2
2
  // override handler for gpt-3.5-turbo
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_openai_chat_16.js
2
2
  // override handler for gpt-3.5-turbo-16k
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_openai_chat_gpt4.js
2
2
  // override handler for gpt-4
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_openai_chat_gpt4_32.js
2
2
  // override handler for gpt-4-32
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,7 +1,7 @@
1
1
  // sys_openai_chat_gpt4_turbo.js
2
2
  // override handler for gpt-4-turbo
3
3
 
4
- import { Prompt } from '../server/prompt.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
5
 
6
6
  export default {
7
7
  prompt:
@@ -1,10 +1,10 @@
1
- import { Prompt } from '../server/prompt.js';
1
+ import { Prompt } from '../../server/prompt.js';
2
2
 
3
3
  export default {
4
4
  prompt: [
5
5
  new Prompt({
6
6
  messages: [
7
- { "role": "system", "content": "Assistant is a list parsing AI. When user posts text including a numbered list and a desired set of fields, assistant will carefully read the list and attempt to convert the list into a JSON object with the given fields. If there are extra fields, assistant will ignore them. If there are some missing fields, assistant will just skip the missing fields and return the rest. If the conversion is not at all possible, assistant will return an empty JSON array. Assistant will generate only the repaired JSON object in a directly parseable format with no markdown surrounding it and no other response or commentary." },
7
+ { "role": "system", "content": "Assistant is a list parsing AI. When user posts text including a numbered list and a desired set of fields, assistant will carefully read the list and attempt to convert the list into a JSON object with the given fields. If a field value is numeric, it should be returned as a number in the JSON object. If there are extra fields, assistant will ignore them. If there are some missing fields, assistant will just skip the missing fields and return the rest. If the conversion is not at all possible, assistant will return an empty JSON array. Assistant will generate only the repaired JSON object in a directly parseable format with no markdown surrounding it and no other response or commentary." },
8
8
  { "role": "user", "content": `Fields: {{{format}}}\nList: {{{text}}}`},
9
9
  ]
10
10
  })
@@ -1,4 +1,4 @@
1
- import { Prompt } from '../server/prompt.js';
1
+ import { Prompt } from '../../server/prompt.js';
2
2
 
3
3
  export default {
4
4
  prompt: [
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Import required modules
3
- import { Prompt } from '../server/prompt.js';
3
+ import { Prompt } from '../../../server/prompt.js';
4
4
 
5
5
  export default {
6
6
  prompt: [
@@ -1,5 +1,5 @@
1
1
  // Import required modules
2
- import { Prompt } from '../server/prompt.js';
2
+ import { Prompt } from '../../../server/prompt.js';
3
3
 
4
4
  export default {
5
5
  prompt: [
@@ -1,5 +1,5 @@
1
1
  // Import required modules
2
- import { Prompt } from "../server/prompt.js"
2
+ import { Prompt } from "../../../server/prompt.js"
3
3
 
4
4
  export default {
5
5
  prompt: [
@@ -1,5 +1,5 @@
1
1
  // Import required modules
2
- import { Prompt } from '../server/prompt.js';
2
+ import { Prompt } from '../../../server/prompt.js';
3
3
 
4
4
  export default {
5
5
  prompt: [
@@ -1,5 +1,5 @@
1
1
  // Import required modules
2
- import { Prompt } from '../server/prompt.js';
2
+ import { Prompt } from '../../../server/prompt.js';
3
3
 
4
4
  export default {
5
5
  prompt: [
@@ -221,15 +221,59 @@ class PathwayResolver {
221
221
  // Get saved context from contextId or change contextId if needed
222
222
  const { contextId } = args;
223
223
  this.savedContextId = contextId ? contextId : uuidv4();
224
- this.savedContext = contextId ? (getv && (await getv(contextId)) || {}) : {};
225
-
226
- // Save the context before processing the request
227
- const savedContextStr = JSON.stringify(this.savedContext);
224
+
225
+ const loadMemory = async () => {
226
+ // Load initial values
227
+ this.savedContext = (getv && await getv(contextId)) || {};
228
+ this.memorySelf = (getv && await getv(`${contextId}-memorySelf`)) || "";
229
+ this.memoryDirectives = (getv && await getv(`${contextId}-memoryDirectives`)) || "";
230
+ this.memoryTopics = (getv && await getv(`${contextId}-memoryTopics`)) || "";
231
+ this.memoryUser = (getv && await getv(`${contextId}-memoryUser`)) || "";
232
+
233
+ // Store initial state for comparison
234
+ this.initialState = {
235
+ savedContext: this.savedContext,
236
+ memorySelf: this.memorySelf,
237
+ memoryDirectives: this.memoryDirectives,
238
+ memoryTopics: this.memoryTopics,
239
+ memoryUser: this.memoryUser
240
+ };
241
+ };
242
+
243
+ const saveChangedMemory = async () => {
244
+ this.savedContextId = this.savedContextId || uuidv4();
245
+
246
+ const currentState = {
247
+ savedContext: this.savedContext,
248
+ memorySelf: this.memorySelf,
249
+ memoryDirectives: this.memoryDirectives,
250
+ memoryTopics: this.memoryTopics,
251
+ memoryUser: this.memoryUser
252
+ };
253
+
254
+ if (currentState.savedContext !== this.initialState.savedContext) {
255
+ setv && await setv(this.savedContextId, this.savedContext);
256
+ }
257
+ if (currentState.memorySelf !== this.initialState.memorySelf) {
258
+ setv && await setv(`${this.savedContextId}-memorySelf`, this.memorySelf);
259
+ }
260
+ if (currentState.memoryDirectives !== this.initialState.memoryDirectives) {
261
+ setv && await setv(`${this.savedContextId}-memoryDirectives`, this.memoryDirectives);
262
+ }
263
+ if (currentState.memoryTopics !== this.initialState.memoryTopics) {
264
+ setv && await setv(`${this.savedContextId}-memoryTopics`, this.memoryTopics);
265
+ }
266
+ if (currentState.memoryUser !== this.initialState.memoryUser) {
267
+ setv && await setv(`${this.savedContextId}-memoryUser`, this.memoryUser);
268
+ }
269
+ };
228
270
 
229
271
  const MAX_RETRIES = 3;
230
272
  let data = null;
231
273
 
232
274
  for (let retries = 0; retries < MAX_RETRIES; retries++) {
275
+ await loadMemory(); // Reset memory state on each retry
276
+
233
277
  data = await this.processRequest(args);
234
278
  if (!data) {
235
279
  break;
@@ -241,13 +285,10 @@ class PathwayResolver {
241
285
  }
242
286
 
243
287
  logger.warn(`Bad pathway result - retrying pathway. Attempt ${retries + 1} of ${MAX_RETRIES}`);
244
- this.savedContext = JSON.parse(savedContextStr);
245
288
  }
246
289
 
247
- // Update saved context if it has changed, generating a new contextId if necessary
248
- if (savedContextStr !== JSON.stringify(this.savedContext)) {
249
- this.savedContextId = this.savedContextId || uuidv4();
250
- setv && setv(this.savedContextId, this.savedContext);
290
+ if (data !== null) {
291
+ await saveChangedMemory();
251
292
  }
252
293
 
253
294
  return data;
@@ -419,7 +460,14 @@ class PathwayResolver {
419
460
 
420
461
  // If this text is empty, skip applying the prompt as it will likely be a nonsensical result
421
462
  if (!/^\s*$/.test(text) || parameters?.file || parameters?.inputVector || this?.modelName.includes('cognitive')) {
422
- result = await this.modelExecutor.execute(text, { ...parameters, ...this.savedContext }, prompt, this);
463
+ result = await this.modelExecutor.execute(text, {
464
+ ...parameters,
465
+ ...this.savedContext,
466
+ memorySelf: this.memorySelf,
467
+ memoryDirectives: this.memoryDirectives,
468
+ memoryTopics: this.memoryTopics,
469
+ memoryUser: this.memoryUser
470
+ }, prompt, this);
423
471
  } else {
424
472
  result = text;
425
473
  }
@@ -439,6 +487,10 @@ class PathwayResolver {
439
487
 
440
488
  // save the result to the context if requested and no errors
441
489
  if (prompt.saveResultTo && this.errors.length === 0) {
490
+ // Update memory property if it matches a known type
491
+ if (["memorySelf", "memoryUser", "memoryDirectives", "memoryTopics"].includes(prompt.saveResultTo)) {
492
+ this[prompt.saveResultTo] = result;
493
+ }
442
494
  this.savedContext[prompt.saveResultTo] = result;
443
495
  }
444
496
  return result;
@@ -55,7 +55,7 @@ class AzureCognitivePlugin extends ModelPlugin {
55
55
  { search: searchQuery,
56
56
  "searchMode": "all",
57
57
  "queryType": "full",
58
- select: 'id', top: TOP
58
+ select: 'id', top: TOP, skip: 0
59
59
  };
60
60
 
61
61
  const docsToDelete = JSON.parse(await this.executeRequest(cortexRequest));
@@ -115,6 +115,19 @@ class AzureCognitivePlugin extends ModelPlugin {
115
115
  ];
116
116
  } else {
117
117
  data.search = modelPromptText;
118
+ data.top = parameters.top || 50;
119
+ data.skip = 0;
120
+ if (parameters.titleOnly) {
121
+ switch(indexName){
122
+ case 'indexcortex':
123
+ case 'indexwires':
124
+ data.select = 'title,id';
125
+ break;
126
+ default:
127
+ data.select = 'title,id,url';
128
+ break;
129
+ }
130
+ }
118
131
  }
119
132
 
120
133
  filter && (data.filter = filter);
@@ -1,9 +1,10 @@
1
1
  import OpenAIVisionPlugin from "./openAiVisionPlugin.js";
2
2
  import logger from "../../lib/logger.js";
3
+ import axios from 'axios';
3
4
 
4
5
  const allowedMIMETypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
5
- async function convertContentItem(item) {
6
6
 
7
+ async function convertContentItem(item, maxImageSize) {
7
8
  let imageUrl = "";
8
9
 
9
10
  try {
@@ -27,6 +28,14 @@ async function convertContentItem(item) {
27
28
  try {
28
29
  const urlData = imageUrl.startsWith("data:") ? imageUrl : await fetchImageAsDataURL(imageUrl);
29
30
  if (!urlData) { return null; }
31
+
32
+ // Check base64 size
33
+ const base64Size = (urlData.length * 3) / 4;
34
+ if (base64Size > maxImageSize) {
35
+ logger.warn(`Image size ${base64Size} bytes exceeds maximum allowed size ${maxImageSize} - skipping image content.`);
36
+ return null;
37
+ }
38
+
30
39
  const [, mimeType = "image/jpeg"] = urlData.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/) || [];
31
40
  const base64Image = urlData.split(",")[1];
32
41
 
@@ -60,25 +69,26 @@ async function convertContentItem(item) {
60
69
  // Fetch image and convert to base 64 data URL
61
70
  async function fetchImageAsDataURL(imageUrl) {
62
71
  try {
63
- const response = await fetch(imageUrl, { method: 'HEAD' });
64
-
65
- if (!response.ok) {
66
- throw new Error(`HTTP error! status: ${response.status}`);
67
- }
72
+ // First check headers
73
+ const headResponse = await axios.head(imageUrl, {
74
+ timeout: 30000, // 30 second timeout
75
+ maxRedirects: 5
76
+ });
68
77
 
69
- const contentType = response.headers.get('content-type');
78
+ const contentType = headResponse.headers['content-type'];
70
79
  if (!contentType || !allowedMIMETypes.includes(contentType)) {
71
80
  logger.warn(`Unsupported image type: ${contentType} - skipping image content.`);
72
81
  return null;
73
82
  }
74
83
 
75
- const dataResponse = await fetch(imageUrl);
76
- if (!dataResponse.ok) {
77
- throw new Error(`HTTP error! status: ${dataResponse.status}`);
78
- }
84
+ // Then get the actual image data
85
+ const dataResponse = await axios.get(imageUrl, {
86
+ timeout: 30000,
87
+ responseType: 'arraybuffer',
88
+ maxRedirects: 5
89
+ });
79
90
 
80
- const buffer = await dataResponse.arrayBuffer();
81
- const base64Image = Buffer.from(buffer).toString("base64");
91
+ const base64Image = Buffer.from(dataResponse.data).toString('base64');
82
92
  return `data:${contentType};base64,${base64Image}`;
83
93
  }
84
94
  catch (e) {
@@ -151,7 +161,7 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
151
161
  const claude3Messages = await Promise.all(
152
162
  finalMessages.map(async (message) => {
153
163
  const contentArray = Array.isArray(message.content) ? message.content : [message.content];
154
- const claude3Content = await Promise.all(contentArray.map(convertContentItem));
164
+ const claude3Content = await Promise.all(contentArray.map(item => convertContentItem(item, this.getModelMaxImageSize())));
155
165
  return {
156
166
  role: message.role,
157
167
  content: claude3Content.filter(Boolean),
@@ -301,7 +311,7 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
301
311
 
302
312
  shortenContent(content, maxWords = 40) {
303
313
  const words = content.split(" ");
304
- if (words.length <= maxWords) {
314
+ if (words.length <= maxWords || logger.level === 'debug') {
305
315
  return content;
306
316
  }
307
317
  return words.slice(0, maxWords / 2).join(" ") +
@@ -200,7 +200,7 @@ class Gemini15ChatPlugin extends ModelPlugin {
200
200
  } else if (Array.isArray(responseData)) {
201
201
  const { mergedResult, safetyRatings } = mergeResults(responseData);
202
202
  if (safetyRatings?.length) {
203
- logger.warn(`!!! response was blocked because the input or response potentially violates policies`);
203
+ logger.warn(`response was blocked because the input or response potentially violates policies`);
204
204
  logger.verbose(`Safety Ratings: ${JSON.stringify(safetyRatings, null, 2)}`);
205
205
  }
206
206
  const { length, units } = this.getLength(mergedResult);
@@ -195,7 +195,7 @@ class GeminiChatPlugin extends ModelPlugin {
195
195
  } else if (Array.isArray(responseData)) {
196
196
  const { mergedResult, safetyRatings } = mergeResults(responseData);
197
197
  if (safetyRatings?.length) {
198
- logger.warn(`!!! response was blocked because the input or response potentially violates policies`);
198
+ logger.warn(`response was blocked because the input or response potentially violates policies`);
199
199
  logger.verbose(`Safety Ratings: ${JSON.stringify(safetyRatings, null, 2)}`);
200
200
  }
201
201
  const { length, units } = this.getLength(mergedResult);
@@ -9,6 +9,7 @@ import { config } from '../../config.js';
9
9
  const DEFAULT_MAX_TOKENS = 4096;
10
10
  const DEFAULT_MAX_RETURN_TOKENS = 256;
11
11
  const DEFAULT_PROMPT_TOKEN_RATIO = 0.5;
12
+ const DEFAULT_MAX_IMAGE_SIZE = 20 * 1024 * 1024; // 20MB default
12
13
 
13
14
  class ModelPlugin {
14
15
  constructor(pathway, model) {
@@ -249,7 +250,12 @@ class ModelPlugin {
249
250
  let length = 0;
250
251
  let units = isProd ? 'characters' : 'tokens';
251
252
  if (data) {
252
- length = isProd ? data.length : encode(data).length;
253
+ if (isProd || data.length > 5000) {
254
+ length = data.length;
255
+ units = 'characters';
256
+ } else {
257
+ length = encode(data).length;
258
+ }
253
259
  }
254
260
  return {length, units};
255
261
  }
@@ -341,6 +347,9 @@ class ModelPlugin {
341
347
  return requestProgress;
342
348
  }
343
349
 
350
+ getModelMaxImageSize() {
351
+ return (this.promptParameters.maxImageSize ?? this.model.maxImageSize ?? DEFAULT_MAX_IMAGE_SIZE);
352
+ }
344
353
 
345
354
  }
346
355
 
@@ -115,9 +115,10 @@ class OpenAIChatPlugin extends ModelPlugin {
115
115
  const content = message.content === undefined ? JSON.stringify(message) : (Array.isArray(message.content) ? message.content.map(item => JSON.stringify(item)).join(', ') : message.content);
116
116
  const words = content.split(" ");
117
117
  const { length, units } = this.getLength(content);
118
- const preview = words.length < 41 ? content : words.slice(0, 20).join(" ") + " ... " + words.slice(-20).join(" ");
119
-
120
- logger.verbose(`message ${index + 1}: role: ${message.role}, ${units}: ${length}, content: "${preview}"`);
118
+
119
+ const displayContent = logger.level === 'debug' ? content : (words.length < 41 ? content : words.slice(0, 20).join(" ") + " ... " + words.slice(-20).join(" "));
120
+
121
+ logger.verbose(`message ${index + 1}: role: ${message.role}, ${units}: ${length}, content: "${displayContent}"`);
121
122
  totalLength += length;
122
123
  totalUnits = units;
123
124
  });
@@ -52,14 +52,20 @@ class OpenAIDallE3Plugin extends ModelPlugin {
52
52
 
53
53
  requestPromise
54
54
  .then((response) => handleResponse(response))
55
- .catch((error) => handleResponse(error));
55
+ .catch((error) => handleResponse(error, true));
56
56
 
57
- function handleResponse(response) {
57
+ function handleResponse(response, isError = false) {
58
58
  let status = "succeeded";
59
- let data = JSON.stringify(response);
60
- if (response.data.error) {
59
+ let data;
60
+
61
+ if (isError) {
62
+ status = "failed";
63
+ data = JSON.stringify({ error: response.message || response });
64
+ } else if (response.data?.error) {
61
65
  status = "failed";
62
66
  data = JSON.stringify(response.data);
67
+ } else {
68
+ data = JSON.stringify(response);
63
69
  }
64
70
 
65
71
  const requestProgress = {
@@ -80,12 +86,14 @@ class OpenAIDallE3Plugin extends ModelPlugin {
80
86
  let progress =
81
87
  requestDurationEstimator.calculatePercentComplete(callid);
82
88
 
89
+ if (typeof progress === 'number' && !isNaN(progress) && progress >= 0 && progress <= 1) {
83
90
  await publishRequestProgress({
84
91
  requestId,
85
92
  status: "pending",
86
93
  progress,
87
94
  data,
88
95
  });
96
+ }
89
97
 
90
98
  if (state.status !== "pending") {
91
99
  break;
@@ -31,8 +31,7 @@ class OpenAIVisionPlugin extends OpenAIChatPlugin {
31
31
  }
32
32
 
33
33
  if (typeof parsedItem === 'object' && parsedItem !== null && parsedItem.type === 'image_url') {
34
- parsedItem.image_url.url = parsedItem.url || parsedItem.image_url.url;
35
- return parsedItem;
34
+ return {type: parsedItem.type, image_url: {url: parsedItem.url || parsedItem.image_url.url}};
36
35
  }
37
36
 
38
37
  return parsedItem;