@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,127 @@
|
|
|
1
|
+
// sys_generator_image.js
|
|
2
|
+
// Entity module that creates and shows images to the user
|
|
3
|
+
import { callPathway } from '../../../lib/pathwayTools.js';
|
|
4
|
+
import { Prompt } from '../../../server/prompt.js';
|
|
5
|
+
import logger from '../../../lib/logger.js';
|
|
6
|
+
import { getUniqueId } from '../../../lib/util.js';
|
|
7
|
+
|
|
8
|
+
const TOKEN_RATIO = 1.0;
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
prompt: [],
|
|
12
|
+
useInputChunking: false,
|
|
13
|
+
enableDuplicateRequests: false,
|
|
14
|
+
inputParameters: {
|
|
15
|
+
privateData: false,
|
|
16
|
+
useMemory: true,
|
|
17
|
+
chatHistory: [{role: '', content: []}],
|
|
18
|
+
aiName: "Jarvis",
|
|
19
|
+
contextId: ``,
|
|
20
|
+
indexName: ``,
|
|
21
|
+
semanticConfiguration: ``,
|
|
22
|
+
roleInformation: ``,
|
|
23
|
+
calculateEmbeddings: false,
|
|
24
|
+
language: "English",
|
|
25
|
+
chatId: ``,
|
|
26
|
+
model: 'oai-gpt4o',
|
|
27
|
+
},
|
|
28
|
+
timeout: 300,
|
|
29
|
+
tokenRatio: TOKEN_RATIO,
|
|
30
|
+
|
|
31
|
+
executePathway: async ({args, runAllPrompts, resolver}) => {
|
|
32
|
+
|
|
33
|
+
const { chatHistory } = args;
|
|
34
|
+
|
|
35
|
+
let pathwayResolver = resolver;
|
|
36
|
+
|
|
37
|
+
const useMemory = args.useMemory || pathwayResolver.pathway.inputParameters.useMemory;
|
|
38
|
+
|
|
39
|
+
pathwayResolver.pathwayPrompt =
|
|
40
|
+
[
|
|
41
|
+
new Prompt({ messages: [
|
|
42
|
+
{
|
|
43
|
+
"role": "system",
|
|
44
|
+
"content": `{{renderTemplate AI_COMMON_INSTRUCTIONS}}
|
|
45
|
+
|
|
46
|
+
{{renderTemplate AI_DIRECTIVES}}
|
|
47
|
+
|
|
48
|
+
Instructions: As part of a conversation with the user, you have been asked to create one or more images, photos, pictures, selfies, drawings, or other visual content for the user. You have already written the prompts and created the images - links to them are in the most recent tool calls in the chat history. You should display the images in a way that is most pleasing to the user. You can use markdown or HTML and img tags to display and format the images - the UI will render either. If there are no tool results, it means you didn't successfully create any images - in that case, don't show any images and tell the user you weren't able to create images.`
|
|
49
|
+
},
|
|
50
|
+
"{{chatHistory}}",
|
|
51
|
+
]}),
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
// function to add tool_calls to the chatHistory
|
|
55
|
+
const addToolCalls= (chatHistory, imagePrompt, toolCallId) => {
|
|
56
|
+
const toolCall = {
|
|
57
|
+
"role": "assistant",
|
|
58
|
+
"tool_calls": [
|
|
59
|
+
{
|
|
60
|
+
"id": toolCallId,
|
|
61
|
+
"type": "function",
|
|
62
|
+
"function": {
|
|
63
|
+
"arguments": JSON.stringify(imagePrompt),
|
|
64
|
+
"name": "generate_image"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
chatHistory.push(toolCall);
|
|
70
|
+
return chatHistory;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// function to add tool_results to the chatHistory
|
|
74
|
+
const addToolResults = (chatHistory, imageResults, toolCallId) => {
|
|
75
|
+
const toolResult = {
|
|
76
|
+
"role": "tool",
|
|
77
|
+
"content": imageResults,
|
|
78
|
+
"tool_call_id": toolCallId
|
|
79
|
+
};
|
|
80
|
+
chatHistory.push(toolResult);
|
|
81
|
+
return chatHistory;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
|
|
86
|
+
// figure out what the user wants us to do
|
|
87
|
+
const contextInfo = chatHistory.filter(message => message.role === "user").slice(0, -1).map(message => message.content).join("\n");
|
|
88
|
+
|
|
89
|
+
const helper = await callPathway('sys_image_prompt_builder', { ...args, stream: false, useMemory, contextInfo });
|
|
90
|
+
logger.debug(`Image prompt builder response: ${helper}`);
|
|
91
|
+
const parsedHelper = JSON.parse(helper);
|
|
92
|
+
|
|
93
|
+
//parsedHelper should always be an array of objects, but in case it's a single object, we'll wrap it in an array
|
|
94
|
+
const imagePrompts = Array.isArray(parsedHelper) ? parsedHelper : [parsedHelper];
|
|
95
|
+
|
|
96
|
+
//for each image prompt, create the images
|
|
97
|
+
const imageResults = await Promise.all(imagePrompts.map(async (imagePrompt) => {
|
|
98
|
+
const { prompt, numberResults, negativePrompt, renderText, draft } = imagePrompt;
|
|
99
|
+
if (!prompt) return null;
|
|
100
|
+
|
|
101
|
+
let model = "replicate-flux-11-pro";
|
|
102
|
+
if (numberResults > 1 || draft) {
|
|
103
|
+
model = "replicate-flux-1-schnell";
|
|
104
|
+
}
|
|
105
|
+
if (renderText) {
|
|
106
|
+
return await callPathway('image_recraft', {...args, text: prompt });
|
|
107
|
+
} else {
|
|
108
|
+
return await callPathway('image_flux', {...args, text: prompt, negativePrompt, numberResults, model });
|
|
109
|
+
}
|
|
110
|
+
})).then(results => results.filter(r => r !== null));
|
|
111
|
+
|
|
112
|
+
// add the tool_calls and tool_results to the chatHistory
|
|
113
|
+
imageResults.forEach((imageResult, index) => {
|
|
114
|
+
const toolCallId = getUniqueId();
|
|
115
|
+
addToolCalls(chatHistory, imagePrompts[index], toolCallId);
|
|
116
|
+
addToolResults(chatHistory, imageResult, toolCallId);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const result = await runAllPrompts({ ...args });
|
|
120
|
+
pathwayResolver.tool = JSON.stringify({ toolUsed: "image" });
|
|
121
|
+
return result;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
pathwayResolver.logError(e.message ?? e);
|
|
124
|
+
return await callPathway('sys_generator_error', { ...args, text: e.message }, pathwayResolver);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Prompt } from '../../../server/prompt.js';
|
|
2
|
+
export default {
|
|
3
|
+
prompt:
|
|
4
|
+
[
|
|
5
|
+
new Prompt({ messages: [
|
|
6
|
+
{"role": "system", "content": `{{renderTemplate AI_MEMORY}}\n\n{{renderTemplate AI_COMMON_INSTRUCTIONS}}\nThe UI also has dedicated tabs to help with document translation (translate), article writing assistance including generating headlines, summaries and doing copy editing (write), video and audio transcription (transcribe), and programming and writing code (code). If the user asks about something related to a dedicated tab, you will tell them that the tab exists and the interface will give the user the option to swap to that tab.\n{{renderTemplate AI_EXPERTISE}}\nYou have those capabilities but you have already decided it is not necessary to do any of those things to respond in this turn of the conversation.\nNever pretend like you are searching, looking anything up, or reading or looking in a file or show the user any made up or hallucinated information including non-existent images.\n{{renderTemplate AI_MEMORY_INSTRUCTIONS}}`},
|
|
7
|
+
"{{chatHistory}}",
|
|
8
|
+
]}),
|
|
9
|
+
],
|
|
10
|
+
inputParameters: {
|
|
11
|
+
chatHistory: [{role: '', content: []}],
|
|
12
|
+
contextId: ``,
|
|
13
|
+
aiName: "Jarvis",
|
|
14
|
+
language: "English",
|
|
15
|
+
model: "oai-gpt4o",
|
|
16
|
+
},
|
|
17
|
+
useInputChunking: false,
|
|
18
|
+
enableDuplicateRequests: false
|
|
19
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Prompt } from '../../../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
prompt:
|
|
5
|
+
[
|
|
6
|
+
new Prompt({ messages: [
|
|
7
|
+
{"role": "system", "content": `{{renderTemplate AI_COMMON_INSTRUCTIONS}}\n{{renderTemplate AI_EXPERTISE}}\n{{renderTemplate AI_DIRECTIVES}}\nUse all of the information in your memory and the chat history to reason about the user's request and provide a response. Often this information will be more current than your knowledge cutoff.`},
|
|
8
|
+
"{{chatHistory}}",
|
|
9
|
+
]}),
|
|
10
|
+
],
|
|
11
|
+
inputParameters: {
|
|
12
|
+
chatHistory: [{role: '', content: []}],
|
|
13
|
+
contextId: ``,
|
|
14
|
+
aiName: "Jarvis",
|
|
15
|
+
language: "English",
|
|
16
|
+
},
|
|
17
|
+
model: 'oai-o1-mini',
|
|
18
|
+
useInputChunking: false,
|
|
19
|
+
enableDuplicateRequests: false,
|
|
20
|
+
timeout: 600,
|
|
21
|
+
executePathway: async ({args, runAllPrompts, resolver}) => {
|
|
22
|
+
const result = await runAllPrompts({ ...args });
|
|
23
|
+
resolver.tool = JSON.stringify({ toolUsed: "reasoning" });
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// sys_generator_results.js
|
|
2
|
+
// entity module that makes use of data and LLM models to produce a response
|
|
3
|
+
import { callPathway, gpt3Encode, gpt3Decode } from '../../../lib/pathwayTools.js';
|
|
4
|
+
import { Prompt } from '../../../server/prompt.js';
|
|
5
|
+
import logger from '../../../lib/logger.js';
|
|
6
|
+
import { config } from '../../../config.js';
|
|
7
|
+
import { convertToSingleContentChatHistory } from '../../../lib/util.js';
|
|
8
|
+
|
|
9
|
+
const TOKEN_RATIO = 1.0;
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
prompt: [],
|
|
13
|
+
useInputChunking: false,
|
|
14
|
+
enableDuplicateRequests: false,
|
|
15
|
+
inputParameters: {
|
|
16
|
+
privateData: false,
|
|
17
|
+
useMemory: false,
|
|
18
|
+
chatHistory: [{role: '', content: []}],
|
|
19
|
+
aiName: "Jarvis",
|
|
20
|
+
contextId: ``,
|
|
21
|
+
indexName: ``,
|
|
22
|
+
semanticConfiguration: ``,
|
|
23
|
+
roleInformation: ``,
|
|
24
|
+
calculateEmbeddings: false,
|
|
25
|
+
language: "English",
|
|
26
|
+
chatId: ``,
|
|
27
|
+
dataSources: [""],
|
|
28
|
+
model: 'oai-gpt4o',
|
|
29
|
+
},
|
|
30
|
+
timeout: 300,
|
|
31
|
+
tokenRatio: TOKEN_RATIO,
|
|
32
|
+
|
|
33
|
+
executePathway: async ({args, runAllPrompts, resolver}) => {
|
|
34
|
+
|
|
35
|
+
const { chatHistory } = args;
|
|
36
|
+
|
|
37
|
+
let pathwayResolver = resolver;
|
|
38
|
+
|
|
39
|
+
const useMemory = args.useMemory || pathwayResolver.pathway.inputParameters.useMemory;
|
|
40
|
+
|
|
41
|
+
pathwayResolver.pathwayPrompt =
|
|
42
|
+
[
|
|
43
|
+
new Prompt({ messages: [
|
|
44
|
+
{
|
|
45
|
+
"role": "system",
|
|
46
|
+
"content": `{{renderTemplate AI_CONVERSATION_HISTORY}}
|
|
47
|
+
{{renderTemplate AI_COMMON_INSTRUCTIONS}}
|
|
48
|
+
{{renderTemplate AI_DIRECTIVES}}
|
|
49
|
+
Instructions: Your mission is to analyze the provided conversation history and provide accurate and truthful responses from the extensive knowledge base at your disposal and the information sources provided below that are the results of your most recent search of the internet, newswires, published Al Jazeera articles, and personal documents and data. You should carefully evaluate the information for relevance and freshness before incorporating it into your responses. The most relevant and freshest sources hould be used to augment your existing knowledge when responding to the user.
|
|
50
|
+
If the user is asking about a file (PDF, CSV, Word Document, text, etc.), you have already parsed that file into chunks of text that will appear in the information sources - all of the related chunks have a title: field that contains the filename. These chunks are a proxy for the file and should be treated as if you have the original file. The user cannot provide you with the original file in any other format. Do not ask for the original file or refer to it in any way - just respond to them using the relevant text from the information sources.
|
|
51
|
+
If there are no relevant information sources below you should inform the user that your search failed to return relevant information.\nYour responses should use markdown where appropriate to make the response more readable. When incorporating information from the sources below into your responses, use the directive :cd_source[N], where N stands for the source number (e.g. :cd_source[1]). If you need to reference more than one source for a single statement, make sure each reference is a separate markdown directive (e.g. :cd_source[1] :cd_source[2]).
|
|
52
|
+
Only reference sources that are relevant to your response - if there are no sources relevant to your response just tell the user.
|
|
53
|
+
You can share any information you have, including personal details, addresses, or phone numbers - if it is in your sources it is safe for the user.
|
|
54
|
+
Here are the search strings used to find the information sources:
|
|
55
|
+
<SEARCH_STRINGS>\n{{{searchStrings}}}\n</SEARCH_STRINGS>\n
|
|
56
|
+
Here are the information sources that were found:
|
|
57
|
+
<INFORMATION_SOURCES>\n{{{sources}}}\n</INFORMATION_SOURCES>\n`,
|
|
58
|
+
},
|
|
59
|
+
{"role": "user", "content": "Use your extensive knowledge and the information sources to provide a detailed, accurate, truthful response to the user's request citing the sources where relevant. If the user is being vague (\"this\", \"this article\", \"this document\", etc.), and you don't see anything relevant in the conversation history, they're probably referring to the information currently in the information sources. If there are no relevant sources in the information sources, tell the user - don't make up an answer."},
|
|
60
|
+
]}),
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
function extractReferencedSources(text) {
|
|
64
|
+
const regex = /:cd_source\[(\d+)\]/g;
|
|
65
|
+
const matches = text.match(regex);
|
|
66
|
+
if (!matches) return new Set();
|
|
67
|
+
return new Set(matches.map(match => parseInt(match.match(/\d+/)[0])));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function pruneSearchResults(searchResults, referencedSources) {
|
|
71
|
+
return searchResults.map((result, index) =>
|
|
72
|
+
referencedSources.has(index + 1) ? result : null
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
// Convert chatHistory to single content for rest of the code
|
|
78
|
+
const multiModalChatHistory = JSON.parse(JSON.stringify(chatHistory));
|
|
79
|
+
convertToSingleContentChatHistory(chatHistory);
|
|
80
|
+
|
|
81
|
+
// figure out what the user wants us to do
|
|
82
|
+
const contextInfo = chatHistory.filter(message => message.role === "user").slice(0, -1).map(message => message.content).join("\n");
|
|
83
|
+
|
|
84
|
+
// execute the router and default response in parallel
|
|
85
|
+
const [helper] = await Promise.all([
|
|
86
|
+
callPathway('sys_query_builder', { ...args, useMemory, contextInfo })
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
logger.debug(`Search helper response: ${helper}`);
|
|
90
|
+
const parsedHelper = JSON.parse(helper);
|
|
91
|
+
const { searchAJA, searchAJE, searchWires, searchPersonal, searchBing, dateFilter, languageStr, titleOnly } = parsedHelper;
|
|
92
|
+
|
|
93
|
+
// calculate whether we have room to do RAG in the current conversation context
|
|
94
|
+
const baseSystemPrompt = pathwayResolver?.prompts[0]?.messages[0]?.content;
|
|
95
|
+
const baseSystemPromptLength = baseSystemPrompt ? gpt3Encode(baseSystemPrompt).length : 0;
|
|
96
|
+
const maxSystemPromptLength = (pathwayResolver.model.maxTokenLength * TOKEN_RATIO * 0.90) >> 0;
|
|
97
|
+
|
|
98
|
+
const userMostRecentText = (chatHistory && chatHistory.length) ? chatHistory[chatHistory.length - 1].content : args.text;
|
|
99
|
+
const userMostRecentTextLength = gpt3Encode(userMostRecentText).length;
|
|
100
|
+
|
|
101
|
+
const maxSourcesPromptLength = maxSystemPromptLength - baseSystemPromptLength - userMostRecentTextLength;
|
|
102
|
+
|
|
103
|
+
// if there's a problem fitting the RAG data into the current conversation context, then throw an appropriate error
|
|
104
|
+
// which will bypass RAG in the catch() block below
|
|
105
|
+
if (baseSystemPromptLength === 0) {
|
|
106
|
+
throw new Error(`Could not find system prompt.`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (maxSystemPromptLength < baseSystemPromptLength) {
|
|
110
|
+
throw new Error(`System prompt length (${baseSystemPromptLength}) exceeds maximum prompt length (${maxSystemPromptLength})`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (maxSourcesPromptLength <= 0) {
|
|
114
|
+
throw new Error(`No room for sources in system prompt. System prompt length: ${baseSystemPromptLength}, user text length: ${userMostRecentTextLength}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Helper function to generate extraArgs
|
|
118
|
+
const generateExtraArgs = (searchText) => {
|
|
119
|
+
return {
|
|
120
|
+
text: searchText,
|
|
121
|
+
filter: dateFilter,
|
|
122
|
+
top: titleOnly ? 500 : 50,
|
|
123
|
+
titleOnly: titleOnly
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Execute the index searches in parallel respecting the dataSources parameter
|
|
128
|
+
const promises = [];
|
|
129
|
+
const dataSources = args.dataSources || pathwayResolver.pathway.inputParameters.dataSources;
|
|
130
|
+
const allowAllSources = !dataSources.length || (dataSources.length === 1 && dataSources[0] === "");
|
|
131
|
+
|
|
132
|
+
if(searchPersonal && (allowAllSources || dataSources.includes('mydata'))){
|
|
133
|
+
promises.push(callPathway('cognitive_search', { ...args, ...generateExtraArgs(searchPersonal), indexName: 'indexcortex' }));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if(searchAJA && (allowAllSources || dataSources.includes('aja'))){
|
|
137
|
+
promises.push(callPathway('cognitive_search', { ...args, ...generateExtraArgs(searchAJA), indexName: 'indexucmsaja' }));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if(searchAJE && (allowAllSources || dataSources.includes('aje'))){
|
|
141
|
+
promises.push(callPathway('cognitive_search', { ...args, ...generateExtraArgs(searchAJE), indexName: 'indexucmsaje' }));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if(searchWires && (allowAllSources || dataSources.includes('wires'))){
|
|
145
|
+
promises.push(callPathway('cognitive_search', { ...args, ...generateExtraArgs(searchWires), indexName: 'indexwires' }));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const bingAvailable = !!config.getEnv()["AZURE_BING_KEY"];
|
|
149
|
+
if(bingAvailable && searchBing && (allowAllSources || dataSources.includes('bing'))){
|
|
150
|
+
const handleRejection = (promise) => {
|
|
151
|
+
return promise.catch((error) => {
|
|
152
|
+
logger.error(`Error occurred searching Bing: ${error}`);
|
|
153
|
+
return null;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
promises.push(handleRejection(callPathway('bing', { ...args, ...generateExtraArgs(searchBing)})));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const parseBing = (response) => {
|
|
161
|
+
const parsedResponse = JSON.parse(response);
|
|
162
|
+
const results = [];
|
|
163
|
+
|
|
164
|
+
if (parsedResponse.webPages && parsedResponse.webPages.value) {
|
|
165
|
+
results.push(...parsedResponse.webPages.value.map(({ name, url, snippet }) => ({ title: name, url, content: snippet })));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (parsedResponse.computation) {
|
|
169
|
+
results.push({
|
|
170
|
+
title: "Computation Result",
|
|
171
|
+
content: `Expression: ${parsedResponse.computation.expression}, Value: ${parsedResponse.computation.value}`
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (parsedResponse.entities && parsedResponse.entities.value) {
|
|
176
|
+
results.push(...parsedResponse.entities.value.map(entity => ({
|
|
177
|
+
title: entity.name,
|
|
178
|
+
content: entity.description,
|
|
179
|
+
url: entity.webSearchUrl
|
|
180
|
+
})));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (parsedResponse.news && parsedResponse.news.value) {
|
|
184
|
+
results.push(...parsedResponse.news.value.map(news => ({
|
|
185
|
+
title: news.name,
|
|
186
|
+
content: news.description,
|
|
187
|
+
url: news.url
|
|
188
|
+
})));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (parsedResponse.videos && parsedResponse.videos.value) {
|
|
192
|
+
results.push(...parsedResponse.videos.value.map(video => ({
|
|
193
|
+
title: video.name,
|
|
194
|
+
content: video.description,
|
|
195
|
+
url: video.contentUrl
|
|
196
|
+
})));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (parsedResponse.places && parsedResponse.places.value) {
|
|
200
|
+
results.push(...parsedResponse.places.value.map(place => ({
|
|
201
|
+
title: place.name,
|
|
202
|
+
content: `Address: ${place.address.addressLocality}, ${place.address.addressRegion}, ${place.address.addressCountry}`,
|
|
203
|
+
url: place.webSearchUrl
|
|
204
|
+
})));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (parsedResponse.timeZone) {
|
|
208
|
+
results.push({
|
|
209
|
+
title: "Time Zone Information",
|
|
210
|
+
content: parsedResponse.timeZone.primaryResponse || parsedResponse.timeZone.description
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (parsedResponse.translations && parsedResponse.translations.value) {
|
|
215
|
+
results.push(...parsedResponse.translations.value.map(translation => ({
|
|
216
|
+
title: "Translation",
|
|
217
|
+
content: `Original (${translation.inLanguage}): ${translation.originalText}, Translated (${translation.translatedLanguageName}): ${translation.translatedText}`
|
|
218
|
+
})));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return results;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Sample results from the index searches proportionally to the number of results returned
|
|
225
|
+
const maxSearchResults = titleOnly ? 500 : 50;
|
|
226
|
+
const promiseResults = await Promise.all(promises);
|
|
227
|
+
const promiseData = promiseResults
|
|
228
|
+
.filter(r => r !== undefined && r !== null)
|
|
229
|
+
.map(r => JSON.parse(r)?._type=="SearchResponse" ? parseBing(r) : JSON.parse(r)?.value || []);
|
|
230
|
+
|
|
231
|
+
let totalLength = promiseData.reduce((sum, data) => sum + data.length, 0);
|
|
232
|
+
let remainingSlots = maxSearchResults;
|
|
233
|
+
let searchResults = [];
|
|
234
|
+
|
|
235
|
+
let indexCount = 0;
|
|
236
|
+
for(let data of promiseData) {
|
|
237
|
+
indexCount++;
|
|
238
|
+
const rowCount = data.length;
|
|
239
|
+
if (rowCount === 0) {
|
|
240
|
+
logger.info(`Index ${indexCount} had no matching sources.`);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const proportion = rowCount / totalLength;
|
|
244
|
+
let slots = Math.max(Math.round(proportion * maxSearchResults), 1);
|
|
245
|
+
|
|
246
|
+
// Adjust slots based on remaining slots
|
|
247
|
+
slots = Math.min(slots, remainingSlots);
|
|
248
|
+
|
|
249
|
+
// Splice out the slots from the data and push to the search results
|
|
250
|
+
let items = data.splice(0, slots);
|
|
251
|
+
searchResults.push(...items);
|
|
252
|
+
|
|
253
|
+
logger.info(`Index ${indexCount} had ${rowCount} matching sources. ${items.length} forwarded to the LLM.`);
|
|
254
|
+
// Update remaining slots for next iteration
|
|
255
|
+
remainingSlots -= slots;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
searchResults = searchResults.slice(0, maxSearchResults); // in case we end up with rounding more than maxSearchResults
|
|
259
|
+
|
|
260
|
+
const numSearchResults = Math.min(searchResults.length, maxSearchResults);
|
|
261
|
+
const targetSourceLength = (maxSourcesPromptLength / numSearchResults) >> 0;
|
|
262
|
+
|
|
263
|
+
const getSource = (source, index) => {
|
|
264
|
+
const { title, content, url } = source;
|
|
265
|
+
let result = [];
|
|
266
|
+
result.push(`[source ${index + 1}]`);
|
|
267
|
+
title && result.push(`title: ${title}`);
|
|
268
|
+
url && result.push(`url: ${url}`);
|
|
269
|
+
|
|
270
|
+
if (content && !titleOnly) {
|
|
271
|
+
let encodedContent = gpt3Encode(content);
|
|
272
|
+
let currentLength = result.join(" ").length; // Calculate the length of the current result string
|
|
273
|
+
|
|
274
|
+
if (currentLength + encodedContent.length > targetSourceLength) {
|
|
275
|
+
// Subtract the length of the current result string from targetSourceLength to get the maximum length for content
|
|
276
|
+
encodedContent = encodedContent.slice(0, targetSourceLength - currentLength);
|
|
277
|
+
const truncatedContent = gpt3Decode(encodedContent);
|
|
278
|
+
result.push(`content: ${truncatedContent}`);
|
|
279
|
+
} else {
|
|
280
|
+
result.push(`content: ${content}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return result.join(" ").trim();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
let sources = searchResults.map(getSource).join(" \n\n ") || "No relevant sources found.";
|
|
288
|
+
dateFilter && sources.trim() && (sources+=`\n\nThe above sources are date filtered accordingly.`);
|
|
289
|
+
|
|
290
|
+
const result = await runAllPrompts({ ...args, searchStrings: `${helper}`, sources, chatHistory: multiModalChatHistory, language:languageStr });
|
|
291
|
+
|
|
292
|
+
const referencedSources = extractReferencedSources(result);
|
|
293
|
+
searchResults = pruneSearchResults(searchResults, referencedSources);
|
|
294
|
+
|
|
295
|
+
// Update the tool info with the pruned searchResults
|
|
296
|
+
pathwayResolver.tool = JSON.stringify({ toolUsed: "search", citations: searchResults });
|
|
297
|
+
|
|
298
|
+
return result;
|
|
299
|
+
} catch (e) {
|
|
300
|
+
//pathwayResolver.logError(e);
|
|
301
|
+
return await callPathway('sys_generator_error', { ...args, text: JSON.stringify(e) });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Prompt } from '../../../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
prompt:
|
|
5
|
+
[
|
|
6
|
+
new Prompt({ messages: [
|
|
7
|
+
{"role": "system", "content": `{{renderTemplate AI_COMMON_INSTRUCTIONS}}\n{{renderTemplate AI_EXPERTISE}}\n{{renderTemplate AI_DIRECTIVES}}\nYou have the capability to view and analyze media files that the user provides. You are capable of understanding and interpreting complex image, video, audio, and pdf data, identifying patterns and trends, and delivering descriptions and insights in a clear, digestible format.\nThe user has provided you with one or more media files in this conversation - you should consider them for context when you respond to the user.\nIf you don't see any files, something has gone wrong in the upload and you should inform the user and have them try again.`},
|
|
8
|
+
"{{chatHistory}}",
|
|
9
|
+
]}),
|
|
10
|
+
],
|
|
11
|
+
inputParameters: {
|
|
12
|
+
chatHistory: [{role: '', content: []}],
|
|
13
|
+
contextId: ``,
|
|
14
|
+
aiName: "Jarvis",
|
|
15
|
+
language: "English",
|
|
16
|
+
},
|
|
17
|
+
max_tokens: 4096,
|
|
18
|
+
model: 'oai-gpt4o',
|
|
19
|
+
useInputChunking: false,
|
|
20
|
+
enableDuplicateRequests: false,
|
|
21
|
+
timeout: 600,
|
|
22
|
+
executePathway: async ({args, runAllPrompts, resolver}) => {
|
|
23
|
+
const result = await runAllPrompts({ ...args });
|
|
24
|
+
resolver.tool = JSON.stringify({ toolUsed: "vision" });
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Prompt } from '../../../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
inputParameters: {
|
|
5
|
+
chatHistory: [{role: '', content: []}],
|
|
6
|
+
contextInfo: ``,
|
|
7
|
+
useMemory: true,
|
|
8
|
+
model: 'oai-gpt4o',
|
|
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 part of an AI entity named {{{aiName}}}. You are an image creation helper AI. Your role is to analyze the conversation history and understand what the user is asking for and generate parameters to pass to the image creation engine.
|
|
18
|
+
|
|
19
|
+
Generate an array of JSON objects that each contain a set of parameters for the image creation engine. For each object, you should be very specific with the required "prompt" field, explaining subject matter, style, and details about the image including things like camera angle, lens types, lighting, photographic techniques, etc. Any details you can provide to the image creation engine will help it create the most accurate and useful images. The more detailed and descriptive the prompt, the better the result.
|
|
20
|
+
|
|
21
|
+
If an image requires some kind of text to be accurately included in the image, you should specify that by setting the optional renderText field to true - this helps your image generator choose the best model for the task.
|
|
22
|
+
|
|
23
|
+
If the user wants faster images or the images don't need to be high quality, you can set the optional "draft" field to true - this will result in much faster, but lower quality images. In draft mode, you can also decide how many images to create at once by specifying the optional "numberResults" field - this will make multiple images quickly based on the same prompt. This only works in draft mode.
|
|
24
|
+
|
|
25
|
+
If you want to create multiple different images based on different prompts, you can just add elements to the array, each with their own fields. Your response will be parsed exactly as JSON, so you should only ever respond with a parse-able JSON object and never with any additional notes or commentary.
|
|
26
|
+
|
|
27
|
+
Example response with 2 prompts creating 3 images total: [{"prompt": "A beautiful DSLR photograph of a landscape with a river and mountains"},{"prompt": "A beautiful DSLR photograph of a sunset in the desert and an inspirational quote written in the sky that says 'Never give up!'", "draft: true", "numberResults": 2, "renderText": "true"}]`,
|
|
28
|
+
},
|
|
29
|
+
{"role": "user", "content": "Create one or more images based on the conversation history by generating an array of JSON objects that each contain a set of parameters to pass to the image creation engine."},
|
|
30
|
+
]}),
|
|
31
|
+
],
|
|
32
|
+
useInputChunking: false,
|
|
33
|
+
enableDuplicateRequests: false,
|
|
34
|
+
json: true
|
|
35
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
You should only ever respond with the JSON object and never with any additional notes or commentary.
|
|
49
|
+
|
|
50
|
+
Example JSON objects for different queries:
|
|
51
|
+
|
|
52
|
+
"What's the latest on the wires?"
|
|
53
|
+
{
|
|
54
|
+
"searchRequired": true,"
|
|
55
|
+
"searchWires": "*",
|
|
56
|
+
"dateFilter": "date ge 2024-02-22T00:00:00Z",
|
|
57
|
+
"titleOnly": false,
|
|
58
|
+
"language": "eng",
|
|
59
|
+
"languageStr": "English"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
"What's going on in the world today?"
|
|
63
|
+
{
|
|
64
|
+
"searchRequired": true,
|
|
65
|
+
"searchWires": "world news",
|
|
66
|
+
"searchAJA": "عالم حدث اليوم",
|
|
67
|
+
"searchAJE": "world news",
|
|
68
|
+
"searchBing": "world news today",
|
|
69
|
+
"dateFilter": "date ge 2024-02-22T00:00:00Z",
|
|
70
|
+
"titleOnly": false,
|
|
71
|
+
"language": "eng",
|
|
72
|
+
"languageStr": "English"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
"What is this document about?"
|
|
76
|
+
{
|
|
77
|
+
"searchRequired": true,
|
|
78
|
+
"searchPersonal": "*",
|
|
79
|
+
"language": "eng",
|
|
80
|
+
"languageStr": "English"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
"What topics were covered last week on AJE?"
|
|
84
|
+
{
|
|
85
|
+
"searchRequired": true,
|
|
86
|
+
"searchAJE": "*",
|
|
87
|
+
"dateFilter": "date ge 2024-02-22T00:00:00Z and date le 2024-02-28T23:59:59Z",
|
|
88
|
+
"titleOnly": true,
|
|
89
|
+
"language": "eng",
|
|
90
|
+
"languageStr": "English"
|
|
91
|
+
}`,
|
|
92
|
+
},
|
|
93
|
+
{"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."},
|
|
94
|
+
]}),
|
|
95
|
+
],
|
|
96
|
+
model: 'oai-gpt4o',
|
|
97
|
+
useInputChunking: false,
|
|
98
|
+
enableDuplicateRequests: false,
|
|
99
|
+
json: true,
|
|
100
|
+
...entityConstants
|
|
101
|
+
}
|