@aj-archipelago/cortex 1.2.1 → 1.3.1
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 +392 -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 +196 -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 +27 -0
- package/pathways/system/entity/sys_entity_continue.js +55 -0
- package/pathways/system/entity/sys_entity_start.js +239 -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 +310 -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 +110 -0
- package/pathways/system/entity/sys_router_code.js +37 -0
- package/pathways/system/entity/sys_router_tool.js +67 -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/azureVideoTranslatePlugin.js +1 -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 +75 -17
- 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,110 @@
|
|
|
1
|
+
import { Prompt } from '../../../server/prompt.js';
|
|
2
|
+
import entityConstants from './shared/sys_entity_constants.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
inputParameters: {
|
|
6
|
+
chatHistory: [{role: '', content: []}],
|
|
7
|
+
contextInfo: ``,
|
|
8
|
+
useMemory: false,
|
|
9
|
+
},
|
|
10
|
+
prompt:
|
|
11
|
+
[
|
|
12
|
+
new Prompt({ messages: [
|
|
13
|
+
{
|
|
14
|
+
"role": "system",
|
|
15
|
+
"content": `{{#if useMemory}}{{renderTemplate AI_MEMORY}}\n{{renderTemplate AI_MEMORY_INSTRUCTIONS}}\n{{/if}}{{renderTemplate AI_CONVERSATION_HISTORY}}
|
|
16
|
+
|
|
17
|
+
Instructions: You are a search helper AI. Your role is to analyze the included Conversation History to understand what the user is asking for and decide what data sources if any to use to help the user and produce a JSON object with fields that communicate your decisions. You have vast internal knowledge up to your training cutoff date, but your internal knowledge is not always sufficient to answer questions about current events or the latest news.
|
|
18
|
+
|
|
19
|
+
You have the ability to search one or more of the following indexes:
|
|
20
|
+
- "aje" for all news articles published by Al Jazeera English (written in English)
|
|
21
|
+
- "aja" for all news articles published by Al Jazeera Arabic (written in Arabic)
|
|
22
|
+
- "wires" for latest news wires from all wires sources (news & articles)
|
|
23
|
+
- "personal" for the user's documents and uploaded files
|
|
24
|
+
|
|
25
|
+
AJE and AJA are not just translations of each other - they are different news organizations with different reporting styles and focus, so often searching both indexes will provide a more complete answer.
|
|
26
|
+
|
|
27
|
+
To search an index, you can provide an appropriate search string or wildcard (e.g. "*") in the corresponding field for the index: "searchAJE", "searchAJA", "searchWires", and "searchPersonal" respectively. It's helpful if the search string is in the language of the index. Longer search strings will get you more relevant and specific results, but shorter ones or wildcards will get you a broader result set. Wildcards are especially useful in finding all results over a time period or finding vague information (e.g. "the news", "the latest").
|
|
28
|
+
|
|
29
|
+
You have the ability to search the internet in all languages using Bing Search. To do that, just put the search query in the "searchBing" field. Your Bing search query can be as simple or long and detailed as you need it to be and you can use the Bing advanced search syntax (+, -, OR, AND, quotes, etc.) to get more relevant or specific results. It's usually helpful to search the internet in addition to your other sources unless the user has explicitly asked for a specific search source (e.g. "the wires").
|
|
30
|
+
|
|
31
|
+
If you choose to search anything at all, you must always set the "searchRequired" field to true.
|
|
32
|
+
|
|
33
|
+
When the user explicitly asks for a specific search source (e.g. "the wires", "my uploads", "the internet"), use ONLY that source.
|
|
34
|
+
|
|
35
|
+
When the user is referencing something specific, (e.g. "this", "this document", "this file", "my uploads","this article", etc.) and you don't see the document contents in the conversation history, use a wildcard search on the personal index with no date filter to see if there is anything relevant. In this case, don't search any other indexes.
|
|
36
|
+
|
|
37
|
+
When the user's query requires a date filter for accurate data retrieval, pay special attention to qualifier words like "latest","tonight", "this afternoon", "today", "yesterday", "this week", "last week", "this month", etc. The current time and date in GMT is {{now}}, but references like "today" or "yesterday" are relative to the user's time zone. If you remember the user's time zone, use it - it's possible that the day for the user is different than the day in GMT. If a date filter is required, formulate it in a valid OData $filter format and include it in the "dateFilter" field. Do not just put the date in the field - it needs to be filter expression like "date ge 2024-02-22T00:00:00Z". Don't use eq with an exact date time as this is unlikely to return any results.
|
|
38
|
+
|
|
39
|
+
When the user requests an overview, count, or analysis of topics or trends from a specific index over a given time period (e.g., 'What topics were covered yesterday on AJE?' or 'What were the hot topics on the wires this week?' or 'How many articles did AJA publish last week?'), follow these steps:
|
|
40
|
+
|
|
41
|
+
- Use a wildcard search ('*') on the appropriate index(es).
|
|
42
|
+
- Apply a date filter corresponding to the specified time period.
|
|
43
|
+
- Set the 'titleOnly' field to true.
|
|
44
|
+
- Analyze the results to identify and summarize the main topics or trends.
|
|
45
|
+
|
|
46
|
+
Determine the language that the user is speaking in the conversation and fill the "language" field using the ISO 639-3 format and put the full language name in the "languageStr" field.
|
|
47
|
+
|
|
48
|
+
Add a short message to the resultsMessage field that acknowledges the user's request and indicates you're processing it.
|
|
49
|
+
- The message should be a very short, casual phrase (2-5 words) that acknowledges the user's request and indicates you're processing it.
|
|
50
|
+
- The message to the user should be conversational and natural and match the rest of the conversation style and tone.
|
|
51
|
+
- The message should take 1-2 seconds to say out loud. Examples: 'Hmm, let's see...', 'Just a sec...', 'Checking...'"
|
|
52
|
+
|
|
53
|
+
You should only ever respond with the JSON object and never with any additional notes or commentary.
|
|
54
|
+
|
|
55
|
+
Example JSON objects and messages for different queries:
|
|
56
|
+
|
|
57
|
+
"What's the latest on the wires?"
|
|
58
|
+
{
|
|
59
|
+
"searchRequired": true,"
|
|
60
|
+
"searchWires": "*",
|
|
61
|
+
"dateFilter": "date ge 2024-02-22T00:00:00Z",
|
|
62
|
+
"titleOnly": false,
|
|
63
|
+
"language": "eng",
|
|
64
|
+
"languageStr": "English",
|
|
65
|
+
"resultsMessage": "Reading the wires..."
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
"What's going on in the world today?"
|
|
69
|
+
{
|
|
70
|
+
"searchRequired": true,
|
|
71
|
+
"searchWires": "world news",
|
|
72
|
+
"searchAJA": "عالم حدث اليوم",
|
|
73
|
+
"searchAJE": "world news",
|
|
74
|
+
"searchBing": "world news today",
|
|
75
|
+
"dateFilter": "date ge 2024-02-22T00:00:00Z",
|
|
76
|
+
"titleOnly": false,
|
|
77
|
+
"language": "eng",
|
|
78
|
+
"languageStr": "English",
|
|
79
|
+
"resultsMessage": "Just a few seconds..."
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
"What is this document about?"
|
|
83
|
+
{
|
|
84
|
+
"searchRequired": true,
|
|
85
|
+
"searchPersonal": "*",
|
|
86
|
+
"language": "eng",
|
|
87
|
+
"languageStr": "English",
|
|
88
|
+
"resultsMessage": "Almost done..."
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
"What topics were covered last week on AJE?"
|
|
92
|
+
{
|
|
93
|
+
"searchRequired": true,
|
|
94
|
+
"searchAJE": "*",
|
|
95
|
+
"dateFilter": "date ge 2024-02-22T00:00:00Z and date le 2024-02-28T23:59:59Z",
|
|
96
|
+
"titleOnly": true,
|
|
97
|
+
"language": "eng",
|
|
98
|
+
"languageStr": "English",
|
|
99
|
+
"resultsMessage": "Almost there..."
|
|
100
|
+
}`,
|
|
101
|
+
},
|
|
102
|
+
{"role": "user", "content": "Examine the Conversation History and decide what data sources if any to search to help the user and produce a JSON object with fields that communicate your decisions."},
|
|
103
|
+
]}),
|
|
104
|
+
],
|
|
105
|
+
model: 'oai-gpt4o',
|
|
106
|
+
useInputChunking: false,
|
|
107
|
+
enableDuplicateRequests: false,
|
|
108
|
+
json: true,
|
|
109
|
+
...entityConstants
|
|
110
|
+
}
|
|
@@ -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,67 @@
|
|
|
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
|
+
- The message to the user should flow naturally with the conversation history and match the rest of the conversation history in style and tone.
|
|
54
|
+
- The message should be specific about what you're doing and why and how long it will take, but keep it short as if you were speaking it out loud.
|
|
55
|
+
|
|
56
|
+
If no tool is required, return:
|
|
57
|
+
{"toolRequired": false, "toolReason": "explanation of why no tool was necessary"}
|
|
58
|
+
|
|
59
|
+
Return only the JSON object without additional commentary.`,
|
|
60
|
+
},
|
|
61
|
+
{"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."},
|
|
62
|
+
]}),
|
|
63
|
+
],
|
|
64
|
+
useInputChunking: false,
|
|
65
|
+
enableDuplicateRequests: false,
|
|
66
|
+
json: true,
|
|
67
|
+
}
|
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);
|
|
@@ -27,7 +27,7 @@ class AzureVideoTranslatePlugin extends ModelPlugin {
|
|
|
27
27
|
|
|
28
28
|
getRequestParameters(_, parameters, __) {
|
|
29
29
|
const excludedParameters = [
|
|
30
|
-
'text', 'parameters', 'prompt', 'promptParameters', 'previousResult', 'stream'
|
|
30
|
+
'text', 'parameters', 'prompt', 'promptParameters', 'previousResult', 'stream', 'memoryContext'
|
|
31
31
|
];
|
|
32
32
|
|
|
33
33
|
return Object.fromEntries(
|
|
@@ -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);
|