@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.
- package/config.js +38 -11
- package/helper-apps/cortex-autogen/OAI_CONFIG_LIST +2 -1
- package/helper-apps/cortex-autogen/agents.py +387 -0
- package/helper-apps/cortex-autogen/agents_extra.py +14 -0
- package/helper-apps/cortex-autogen/config.py +18 -0
- package/helper-apps/cortex-autogen/data_operations.py +29 -0
- package/helper-apps/cortex-autogen/function_app.py +6 -3
- package/helper-apps/cortex-autogen/main.py +4 -4
- package/helper-apps/cortex-autogen/prompts.py +196 -0
- package/helper-apps/cortex-autogen/prompts_extra.py +5 -0
- package/helper-apps/cortex-autogen/requirements.txt +2 -1
- package/helper-apps/cortex-autogen/search.py +83 -0
- package/helper-apps/cortex-autogen/test.sh +40 -0
- package/helper-apps/cortex-autogen/utils.py +78 -0
- package/lib/handleBars.js +25 -0
- package/lib/logger.js +2 -0
- package/lib/util.js +3 -1
- package/package.json +1 -1
- package/pathways/chat_code.js +1 -1
- package/pathways/chat_context.js +1 -1
- package/pathways/chat_jarvis.js +1 -1
- package/pathways/chat_persist.js +1 -1
- package/pathways/chat_title.js +25 -0
- package/pathways/image_recraft.js +1 -1
- package/pathways/rag.js +1 -1
- package/pathways/rag_jarvis.js +1 -1
- package/pathways/rag_search_helper.js +1 -1
- package/pathways/system/entity/memory/sys_memory_manager.js +71 -0
- package/pathways/system/entity/memory/sys_memory_required.js +21 -0
- package/pathways/system/entity/memory/sys_memory_update.js +190 -0
- package/pathways/system/entity/memory/sys_read_memory.js +37 -0
- package/pathways/system/entity/memory/sys_save_memory.js +60 -0
- package/pathways/system/entity/shared/sys_entity_constants.js +24 -0
- package/pathways/system/entity/sys_entity_continue.js +57 -0
- package/pathways/system/entity/sys_entity_start.js +218 -0
- package/pathways/system/entity/sys_generator_error.js +20 -0
- package/pathways/system/entity/sys_generator_expert.js +26 -0
- package/pathways/system/entity/sys_generator_image.js +127 -0
- package/pathways/system/entity/sys_generator_quick.js +19 -0
- package/pathways/system/entity/sys_generator_reasoning.js +27 -0
- package/pathways/system/entity/sys_generator_results.js +304 -0
- package/pathways/system/entity/sys_generator_video_vision.js +27 -0
- package/pathways/system/entity/sys_image_prompt_builder.js +35 -0
- package/pathways/system/entity/sys_query_builder.js +101 -0
- package/pathways/system/entity/sys_router_code.js +37 -0
- package/pathways/system/entity/sys_router_tool.js +64 -0
- package/pathways/{sys_claude_35_sonnet.js → system/rest_streaming/sys_claude_35_sonnet.js} +1 -1
- package/pathways/{sys_claude_3_haiku.js → system/rest_streaming/sys_claude_3_haiku.js} +1 -1
- package/pathways/{sys_google_chat.js → system/rest_streaming/sys_google_chat.js} +1 -1
- package/pathways/{sys_google_code_chat.js → system/rest_streaming/sys_google_code_chat.js} +1 -1
- package/pathways/{sys_google_gemini_chat.js → system/rest_streaming/sys_google_gemini_chat.js} +1 -1
- package/pathways/{sys_openai_chat.js → system/rest_streaming/sys_openai_chat.js} +1 -1
- package/pathways/{sys_openai_chat_16.js → system/rest_streaming/sys_openai_chat_16.js} +1 -1
- package/pathways/{sys_openai_chat_gpt4.js → system/rest_streaming/sys_openai_chat_gpt4.js} +1 -1
- package/pathways/{sys_openai_chat_gpt4_32.js → system/rest_streaming/sys_openai_chat_gpt4_32.js} +1 -1
- package/pathways/{sys_openai_chat_gpt4_turbo.js → system/rest_streaming/sys_openai_chat_gpt4_turbo.js} +1 -1
- package/pathways/{sys_parse_numbered_object_list.js → system/sys_parse_numbered_object_list.js} +2 -2
- package/pathways/{sys_repair_json.js → system/sys_repair_json.js} +1 -1
- package/pathways/{run_claude35_sonnet.js → system/workspaces/run_claude35_sonnet.js} +1 -1
- package/pathways/{run_claude3_haiku.js → system/workspaces/run_claude3_haiku.js} +1 -1
- package/pathways/{run_gpt35turbo.js → system/workspaces/run_gpt35turbo.js} +1 -1
- package/pathways/{run_gpt4.js → system/workspaces/run_gpt4.js} +1 -1
- package/pathways/{run_gpt4_32.js → system/workspaces/run_gpt4_32.js} +1 -1
- package/server/pathwayResolver.js +62 -10
- package/server/plugins/azureCognitivePlugin.js +14 -1
- package/server/plugins/claude3VertexPlugin.js +25 -15
- package/server/plugins/gemini15ChatPlugin.js +1 -1
- package/server/plugins/geminiChatPlugin.js +1 -1
- package/server/plugins/modelPlugin.js +10 -1
- package/server/plugins/openAiChatPlugin.js +4 -3
- package/server/plugins/openAiDallE3Plugin.js +12 -4
- package/server/plugins/openAiVisionPlugin.js +1 -2
- package/server/plugins/replicateApiPlugin.js +46 -16
- package/tests/multimodal_conversion.test.js +6 -8
- package/helper-apps/cortex-autogen/myautogen.py +0 -317
- package/helper-apps/cortex-autogen/prompt.txt +0 -0
- package/helper-apps/cortex-autogen/prompt_summary.txt +0 -37
- package/pathways/index.js +0 -154
- /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
|
+
}
|
package/pathways/{sys_parse_numbered_object_list.js → system/sys_parse_numbered_object_list.js}
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Prompt } from '
|
|
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
|
})
|
|
@@ -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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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, {
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 =
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
60
|
-
|
|
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
|
|
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;
|