@aj-archipelago/cortex 1.1.20 → 1.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/config/default.example.json +84 -0
  2. package/config.js +17 -4
  3. package/helper-apps/cortex-file-handler/blobHandler.js +144 -100
  4. package/helper-apps/cortex-file-handler/fileChunker.js +13 -8
  5. package/helper-apps/cortex-file-handler/index.js +56 -9
  6. package/lib/pathwayTools.js +7 -1
  7. package/lib/requestExecutor.js +4 -4
  8. package/lib/util.js +163 -1
  9. package/package.json +2 -1
  10. package/pathways/categorize.js +23 -0
  11. package/pathways/chat.js +1 -1
  12. package/pathways/chat_code.js +19 -0
  13. package/pathways/chat_context.js +19 -0
  14. package/pathways/chat_jarvis.js +19 -0
  15. package/pathways/chat_persist.js +23 -0
  16. package/pathways/code_review.js +17 -0
  17. package/pathways/cognitive_delete.js +2 -1
  18. package/pathways/cognitive_insert.js +1 -0
  19. package/pathways/cognitive_search.js +1 -0
  20. package/pathways/embeddings.js +1 -1
  21. package/pathways/expand_story.js +12 -0
  22. package/pathways/format_paragraph_turbo.js +16 -0
  23. package/pathways/format_summarization.js +21 -0
  24. package/pathways/gemini_15_vision.js +20 -0
  25. package/pathways/gemini_vision.js +20 -0
  26. package/pathways/grammar.js +30 -0
  27. package/pathways/hashtags.js +19 -0
  28. package/pathways/headline.js +43 -0
  29. package/pathways/headline_custom.js +169 -0
  30. package/pathways/highlights.js +22 -0
  31. package/pathways/image.js +2 -1
  32. package/pathways/index.js +109 -17
  33. package/pathways/jira_story.js +18 -0
  34. package/pathways/keywords.js +4 -0
  35. package/pathways/language.js +17 -6
  36. package/pathways/locations.js +93 -0
  37. package/pathways/quotes.js +19 -0
  38. package/pathways/rag.js +207 -0
  39. package/pathways/rag_jarvis.js +254 -0
  40. package/pathways/rag_search_helper.js +21 -0
  41. package/pathways/readme.js +18 -0
  42. package/pathways/release_notes.js +16 -0
  43. package/pathways/remove_content.js +31 -0
  44. package/pathways/retrieval.js +23 -0
  45. package/pathways/run_claude35_sonnet.js +21 -0
  46. package/pathways/run_claude3_haiku.js +20 -0
  47. package/pathways/run_gpt35turbo.js +20 -0
  48. package/pathways/run_gpt4.js +20 -0
  49. package/pathways/run_gpt4_32.js +20 -0
  50. package/pathways/select_extension.js +6 -0
  51. package/pathways/select_services.js +10 -0
  52. package/pathways/spelling.js +3 -0
  53. package/pathways/story_angles.js +13 -0
  54. package/pathways/styleguide/styleguide.js +221 -0
  55. package/pathways/styleguidemulti.js +127 -0
  56. package/pathways/subhead.js +48 -0
  57. package/pathways/summarize_turbo.js +98 -0
  58. package/pathways/summary.js +31 -12
  59. package/pathways/sys_claude_35_sonnet.js +19 -0
  60. package/pathways/sys_claude_3_haiku.js +19 -0
  61. package/pathways/sys_google_chat.js +19 -0
  62. package/pathways/sys_google_code_chat.js +19 -0
  63. package/pathways/sys_google_gemini_chat.js +23 -0
  64. package/pathways/sys_openai_chat.js +2 -2
  65. package/pathways/sys_openai_chat_16.js +19 -0
  66. package/pathways/sys_openai_chat_gpt4.js +19 -0
  67. package/pathways/sys_openai_chat_gpt4_32.js +19 -0
  68. package/pathways/sys_openai_chat_gpt4_turbo.js +19 -0
  69. package/pathways/tags.js +25 -0
  70. package/pathways/taxonomy.js +135 -0
  71. package/pathways/timeline.js +51 -0
  72. package/pathways/topics.js +25 -0
  73. package/pathways/topics_sentiment.js +20 -0
  74. package/pathways/transcribe.js +2 -4
  75. package/pathways/transcribe_neuralspace.js +18 -0
  76. package/pathways/translate.js +10 -12
  77. package/pathways/translate_azure.js +13 -0
  78. package/pathways/translate_context.js +21 -0
  79. package/pathways/translate_gpt4.js +19 -0
  80. package/pathways/translate_gpt4_turbo.js +19 -0
  81. package/pathways/translate_turbo.js +19 -0
  82. package/pathways/vision.js +9 -7
  83. package/server/modelExecutor.js +4 -0
  84. package/server/pathwayResolver.js +19 -1
  85. package/server/plugins/azureCognitivePlugin.js +10 -1
  86. package/server/plugins/claude3VertexPlugin.js +2 -1
  87. package/server/plugins/gemini15ChatPlugin.js +8 -3
  88. package/server/plugins/gemini15VisionPlugin.js +19 -3
  89. package/server/plugins/geminiChatPlugin.js +1 -1
  90. package/server/plugins/geminiVisionPlugin.js +2 -3
  91. package/server/plugins/neuralSpacePlugin.js +252 -0
  92. package/server/plugins/openAiVisionPlugin.js +32 -13
  93. package/server/plugins/openAiWhisperPlugin.js +5 -152
  94. package/server/plugins/palmChatPlugin.js +1 -1
  95. package/server/resolver.js +3 -4
  96. package/server/typeDef.js +1 -0
  97. package/tests/claude3VertexPlugin.test.js +214 -0
  98. package/tests/main.test.js +2 -2
  99. package/tests/mocks.js +2 -0
  100. package/tests/openAiChatPlugin.test.js +4 -0
  101. package/tests/vision.test.js +0 -34
@@ -0,0 +1,207 @@
1
+ // rag.js
2
+ // RAG module that makes use of data and LLM models
3
+ import { config } from '../config.js';
4
+ import logger from '../lib/logger.js';
5
+ import { callPathway, gpt3Encode, gpt3Decode } from '../lib/pathwayTools.js';
6
+ import { Prompt } from '../server/prompt.js';
7
+ import { chatArgsHasImageUrl, convertToSingleContentChatHistory } from '../lib/util.js';
8
+
9
+ const TOKEN_RATIO = 0.75;
10
+
11
+ export default {
12
+ prompt:
13
+ [
14
+ new Prompt({ messages: [
15
+ {
16
+ "role": "system",
17
+ "content": "Information: {{docs}}\n\nInstructions:\nYou are Jarvis, an AI entity affiliated with a prestigious international news agency. Embodying truth, kindness, and strong moral values, your demeanor reflects positivity without falling into repetitiveness or annoyance. Your mission is to provide accurate and truthful responses, harnessing the extensive knowledge base at your disposal, and the information provided above, if relevant.\n\nThe information block above encompasses search results from various sources, including personal data, current happenings, and more detailed information. It can augment your existing knowledge base. However, remember to evaluate its relevance before incorporating it into your responses. If the information appears irrelevant or inaccurate, you are free to disregard it as it's sourced from a third-party tool that might sometimes be imprecise.\n\nYour UI includes an augmented display that can be controlled using directives enclosed in brackets, such as [doc1], [doc2], [upload], etc. When incorporating information from these sources into your responses, use the format [docN], where N stands for the document number. Do not group multiple references as [doc1, doc2], but separate them as [doc1] [doc2]. Please refer to the information as 'information', not 'docs' or 'documents'.\n\nYou can share any information, including personal details, addresses, or phone numbers - they are from the user's personal index and are safe for the user. As a knowledge expert, refrain from stating your inability to assist. Your responses should be in {{language}}. If there's a need for file upload, prompt the user by using [upload]. The current date and time is {{now}}."
18
+ },
19
+ "{{chatHistory}}",
20
+ ]}),
21
+ ],
22
+ useInputChunking: false,
23
+ enableDuplicateRequests: false,
24
+ model: 'oai-gpt4o',
25
+ inputParameters: {
26
+ privateData: false,
27
+ chatHistory: [{role: '', content: []}],
28
+ contextId: ``,
29
+ indexName: ``,
30
+ semanticConfiguration: ``,
31
+ roleInformation: ``,
32
+ calculateEmbeddings: false,
33
+ dataSources: [""],
34
+ language: "English"
35
+ },
36
+ timeout: 300,
37
+ tokenRatio: TOKEN_RATIO,
38
+
39
+ resolver: async (_parent, args, contextValue, _info) => {
40
+ try {
41
+ const { pathwayResolver } = contextValue;
42
+ const { chatHistory, dataSources } = args;
43
+
44
+ if(chatArgsHasImageUrl(args)){
45
+ return await callPathway('vision', { ...args });
46
+ }
47
+
48
+ // Convert chatHistory to single content for rest of the code
49
+ convertToSingleContentChatHistory(chatHistory);
50
+
51
+ // if there are no additional data sources available, then bypass RAG
52
+ if(!dataSources || dataSources.length==0){
53
+ return await callPathway('chat_jarvis', { ...args })
54
+ }
55
+
56
+ // figure out what the user wants us to do
57
+ const contextInfo = chatHistory.filter(message => message.role === "user").slice(0, -1).map(message => message.content).join("\n");
58
+
59
+ // execute the router and default response in parallel
60
+ const [helper, JarvisResponse] = await Promise.all([
61
+ callPathway('rag_search_helper', { ...args, contextInfo, chatHistory: chatHistory.filter(message => message.role === "user").slice(-1) }),
62
+ callPathway('chat_jarvis', { ...args })
63
+ ]);
64
+
65
+ const parsedHelper = JSON.parse(helper);
66
+ const { searchRequired, searchPersonal, searchBing, dateFilter, languageStr } = parsedHelper;
67
+
68
+ // if AI thinks we don't need RAG, then return the result from chat_jarvis
69
+ if ( !searchRequired ) {
70
+ return JarvisResponse;
71
+ }
72
+
73
+ // calculate whether we have room to do RAG in the current conversation context
74
+ const baseSystemPrompt = pathwayResolver?.prompts[0]?.messages[0]?.content;
75
+ const baseSystemPromptLength = baseSystemPrompt ? gpt3Encode(baseSystemPrompt).length : 0;
76
+ const maxSystemPromptLength = (pathwayResolver.model.maxTokenLength * TOKEN_RATIO * 0.90) >> 0;
77
+
78
+ const userMostRecentText = (chatHistory && chatHistory.length) ? chatHistory[chatHistory.length - 1].content : args.text;
79
+ const userMostRecentTextLength = gpt3Encode(userMostRecentText).length;
80
+
81
+ const maxDocsPromptLength = maxSystemPromptLength - baseSystemPromptLength - userMostRecentTextLength;
82
+
83
+ // if there's a problem fitting the RAG data into the current conversation context, then throw an appropriate error
84
+ // which will bypass RAG in the catch() block below
85
+ if (baseSystemPromptLength === 0) {
86
+ throw new Error(`Could not find system prompt.`);
87
+ }
88
+
89
+ if (maxSystemPromptLength < baseSystemPromptLength) {
90
+ throw new Error(`System prompt length (${baseSystemPromptLength}) exceeds maximum prompt length (${maxSystemPromptLength})`);
91
+ }
92
+
93
+ if (maxDocsPromptLength <= 0) {
94
+ throw new Error(`No room for docs in system prompt. System prompt length: ${baseSystemPromptLength}, user text length: ${userMostRecentTextLength}`);
95
+ }
96
+
97
+ // Helper function to generate extraArgs
98
+ const generateExtraArgs = (searchText) => {
99
+ return {
100
+ text: searchText,
101
+ filter: dateFilter,
102
+ };
103
+ }
104
+
105
+ // Execute the index searches in parallel
106
+ const promises = [];
107
+
108
+ if(dataSources && dataSources.length>0){
109
+ if(dataSources.includes('mydata') && searchPersonal){
110
+ promises.push(callPathway('cognitive_search', { ...args, ...generateExtraArgs(searchPersonal), indexName: 'indexcortex' }));
111
+ }
112
+ }
113
+
114
+ const bingAvailable = !!config.getEnv()["AZURE_BING_KEY"];
115
+ if(bingAvailable && searchBing){
116
+ const handleRejection = (promise) => {
117
+ return promise.catch((error) => {
118
+ logger.error(`Error occurred: ${error}`);
119
+ return null;
120
+ });
121
+ }
122
+
123
+ promises.push(handleRejection(callPathway('bing', { ...args, ...generateExtraArgs(searchBing)})));
124
+ }
125
+
126
+ const parseBing = (response) => {
127
+ return JSON.parse(response)?.webPages?.value.map(({ name, url, snippet }) => ({ title: name, url, content: snippet }));
128
+ }
129
+
130
+ // Sample results from the index searches proportionally to the number of results returned
131
+ const maxSearchResults = 10;
132
+ const promiseResults = await Promise.all(promises);
133
+ const promiseData = promiseResults
134
+ .filter(r => r !== undefined && r !== null)
135
+ .map(r => JSON.parse(r)?._type=="SearchResponse" ? parseBing(r) : JSON.parse(r)?.value || []);
136
+
137
+ let totalLength = promiseData.reduce((sum, data) => sum + data.length, 0);
138
+ let remainingSlots = maxSearchResults;
139
+ let searchResults = [];
140
+
141
+ let indexCount = 0;
142
+ for(let data of promiseData) {
143
+ indexCount++;
144
+ const rowCount = data.length;
145
+ if (rowCount === 0) {
146
+ console.log(`Index ${indexCount} had no matching documents.`);
147
+ continue;
148
+ }
149
+ const proportion = rowCount / totalLength;
150
+ let slots = Math.max(Math.round(proportion * maxSearchResults), 1);
151
+
152
+ // Adjust slots based on remaining slots
153
+ slots = Math.min(slots, remainingSlots);
154
+
155
+ // Splice out the slots from the data and push to the search results
156
+ let items = data.splice(0, slots);
157
+ searchResults.push(...items);
158
+
159
+ console.log(`Index ${indexCount} had ${rowCount} matching documents. ${items.length} forwarded to the LLM.`);
160
+ // Update remaining slots for next iteration
161
+ remainingSlots -= slots;
162
+ }
163
+
164
+ searchResults = searchResults.slice(0, maxSearchResults); // in case we end up with rounding more than maxSearchResults
165
+
166
+ const numSearchResults = Math.min(searchResults.length, maxSearchResults);
167
+ const targetDocLength = (maxDocsPromptLength / numSearchResults) >> 0;
168
+
169
+ const getDoc = (doc, index) => {
170
+ const { title, content, url } = doc;
171
+ let result = [];
172
+ result.push(`[doc${index + 1}]`);
173
+ title && result.push(`title: ${title}`);
174
+ url && result.push(`url: ${url}`);
175
+
176
+ if (content) {
177
+ let encodedContent = gpt3Encode(content);
178
+ let currentLength = result.join(" ").length; // Calculate the length of the current result string
179
+
180
+ if (currentLength + encodedContent.length > targetDocLength) {
181
+ // Subtract the length of the current result string from targetDocLength to get the maximum length for content
182
+ encodedContent = encodedContent.slice(0, targetDocLength - currentLength);
183
+ const truncatedContent = gpt3Decode(encodedContent);
184
+ result.push(`content: ${truncatedContent}`);
185
+ } else {
186
+ result.push(`content: ${content}`);
187
+ }
188
+ }
189
+
190
+ return result.join(" ").trim();
191
+ }
192
+
193
+ let docs = searchResults.map(getDoc).join(" \n\n ");
194
+ dateFilter && docs.trim() && (docs+=`\n\n above docs are date filtered accordingly. \n\n`)
195
+
196
+ const result = await pathwayResolver.resolve({ ...args, docs, language:languageStr });
197
+
198
+ pathwayResolver.tool = JSON.stringify({ citations:searchResults }) // add tool info back
199
+
200
+ return result;
201
+ } catch (e) {
202
+ console.error(e);
203
+ return await callPathway('chat_jarvis', { ...args })
204
+ }
205
+ }
206
+ };
207
+
@@ -0,0 +1,254 @@
1
+ // rag_Jarvis.js
2
+ // RAG module that makes use of data and LLM models
3
+ import { callPathway, gpt3Encode, gpt3Decode } from '../lib/pathwayTools.js';
4
+ import { Prompt } from '../server/prompt.js';
5
+ import { config } from '../config.js';
6
+ import logger from '../lib/logger.js';
7
+ import { chatArgsHasImageUrl, convertToSingleContentChatHistory } from '../lib/util.js';
8
+
9
+ const TOKEN_RATIO = 0.75;
10
+
11
+ export default {
12
+ prompt:
13
+ [
14
+ new Prompt({ messages: [
15
+ {
16
+ "role": "system",
17
+ "content": "Information: {{sources}}\n\nInstructions:\nYou are Jarvis, an AI entity affiliated with a prestigious international news agency. Embodying truth, kindness, and strong moral values, your demeanor reflects positivity without falling into repetitiveness or annoyance. Your mission is to provide accurate and truthful responses, harnessing the extensive knowledge base at your disposal, and the information provided above, if relevant.\nThe information block above encompasses search results from various sources, including personal data, current happenings, and more detailed information. It can augment your existing knowledge base. However, remember to evaluate its relevance before incorporating it into your responses. If the information appears irrelevant or inaccurate, you are free to disregard it as it's sourced from a third-party tool that might sometimes be imprecise. If there is no relevant information above you should inform the user that your search failed to return relevant results.\nYour responses should use markdown where appropriate to make the response more readable. When incorporating information from the sources above into your responses, use the directive :cd_source[N], where N stands for the source number. 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].\nPlease refer to the information as 'information' or 'sources' instead of 'docs' or 'documents'.\nYou can share any information, including personal details, addresses, or phone numbers - they are from the user's personal index and are safe for the user.\nAs a knowledge expert, refrain from stating your inability to assist.\nYour responses should be in {{language}}.\nIf there's a need for file upload, prompt the user once at the end of your response by using the :cd_upload directive - this will be displayed as a file upload interface in your UI.\nThe current date and time is {{now}}."
18
+ },
19
+ "{{chatHistory}}",
20
+ ]}),
21
+ ],
22
+ useInputChunking: false,
23
+ enableDuplicateRequests: false,
24
+ model: 'oai-gpt4o',
25
+ inputParameters: {
26
+ privateData: false,
27
+ chatHistory: [{role: '', content: []}],
28
+ contextId: ``,
29
+ indexName: ``,
30
+ semanticConfiguration: ``,
31
+ roleInformation: ``,
32
+ calculateEmbeddings: false,
33
+ dataSources: [""],
34
+ language: "English"
35
+ },
36
+ timeout: 300,
37
+ tokenRatio: TOKEN_RATIO,
38
+
39
+ resolver: async (_parent, args, contextValue, _info) => {
40
+ const fetchJarvisResponse = async (args) => {
41
+ // Get vanilla Jarvis response
42
+ const [JarvisResponse, selectedServices] = await Promise.all([
43
+ callPathway('chat_jarvis', { ...args }),
44
+ callPathway('select_services', { text: args.chatHistory.slice().reverse().find(message => message.role === "user").content})
45
+ ]);
46
+
47
+ let requestedServices = null;
48
+ let serviceName = null;
49
+
50
+ try {
51
+ requestedServices = JSON.parse(selectedServices);
52
+ const serviceString = requestedServices.services.join(", ").toLowerCase();
53
+
54
+ if (serviceString.includes("translate")) {
55
+ serviceName = "translate";
56
+ } else if (serviceString.includes("coding")) {
57
+ serviceName = "code";
58
+ } else if (serviceString.includes("transcribe")) {
59
+ serviceName = "transcribe";
60
+ } else if (
61
+ serviceString.includes("write") ||
62
+ serviceString.includes("summary") ||
63
+ serviceString.includes("headlines") ||
64
+ serviceString.includes("entities") ||
65
+ serviceString.includes("spelling") ||
66
+ serviceString.includes("grammar") ||
67
+ serviceString.includes("style") ||
68
+ serviceString.includes("entities")
69
+ ) {
70
+ serviceName = "write";
71
+ } else if (serviceString.includes("upload")) {
72
+ serviceName = "upload";
73
+ }
74
+ } catch (e) {
75
+ // Handle JSON parsing error if necessary
76
+ }
77
+
78
+ const customDirective = serviceName
79
+ ? serviceName === "upload"
80
+ ? "\n:cd_upload"
81
+ : `\n:cd_servicelink[${serviceName}]`
82
+ : "";
83
+
84
+ return JarvisResponse + customDirective;
85
+ };
86
+
87
+ try {
88
+ const { pathwayResolver } = contextValue;
89
+ const { chatHistory, dataSources } = args;
90
+
91
+ if(chatArgsHasImageUrl(args)){
92
+ return await callPathway('vision', { ...args });
93
+ }
94
+
95
+ // Convert chatHistory to single content for rest of the code
96
+ convertToSingleContentChatHistory(chatHistory);
97
+
98
+ // if there are no additional data sources available, then bypass RAG
99
+ if(!dataSources || dataSources.length==0){
100
+ return await fetchJarvisResponse(args);
101
+ }
102
+
103
+ // figure out what the user wants us to do
104
+ const contextInfo = chatHistory.filter(message => message.role === "user").slice(0, -1).map(message => message.content).join("\n");
105
+
106
+ // execute the router and default response in parallel
107
+ const [helper, JarvisResponse] = await Promise.all([
108
+ callPathway('rag_search_helper', { ...args, contextInfo, chatHistory: chatHistory.filter(message => message.role === "user").slice(-1) }),
109
+ fetchJarvisResponse(args)
110
+ ]);
111
+
112
+ const parsedHelper = JSON.parse(helper);
113
+ const { searchRequired, searchPersonal, searchBing, dateFilter, languageStr } = parsedHelper;
114
+
115
+ // if AI thinks we don't need RAG, then return the result from chat_jarvis
116
+ if ( !searchRequired ) {
117
+ return JarvisResponse;
118
+ }
119
+
120
+ // calculate whether we have room to do RAG in the current conversation context
121
+ const baseSystemPrompt = pathwayResolver?.prompts[0]?.messages[0]?.content;
122
+ const baseSystemPromptLength = baseSystemPrompt ? gpt3Encode(baseSystemPrompt).length : 0;
123
+ const maxSystemPromptLength = (pathwayResolver.model.maxTokenLength * TOKEN_RATIO * 0.90) >> 0;
124
+
125
+ const userMostRecentText = (chatHistory && chatHistory.length) ? chatHistory[chatHistory.length - 1].content : args.text;
126
+ const userMostRecentTextLength = gpt3Encode(userMostRecentText).length;
127
+
128
+ const maxSourcesPromptLength = maxSystemPromptLength - baseSystemPromptLength - userMostRecentTextLength;
129
+
130
+ // if there's a problem fitting the RAG data into the current conversation context, then throw an appropriate error
131
+ // which will bypass RAG in the catch() block below
132
+ if (baseSystemPromptLength === 0) {
133
+ throw new Error(`Could not find system prompt.`);
134
+ }
135
+
136
+ if (maxSystemPromptLength < baseSystemPromptLength) {
137
+ throw new Error(`System prompt length (${baseSystemPromptLength}) exceeds maximum prompt length (${maxSystemPromptLength})`);
138
+ }
139
+
140
+ if (maxSourcesPromptLength <= 0) {
141
+ throw new Error(`No room for sources in system prompt. System prompt length: ${baseSystemPromptLength}, user text length: ${userMostRecentTextLength}`);
142
+ }
143
+
144
+ // Helper function to generate extraArgs
145
+ const generateExtraArgs = (searchText) => {
146
+ return {
147
+ text: searchText,
148
+ filter: dateFilter,
149
+ };
150
+ }
151
+
152
+ // Execute the index searches in parallel
153
+ const promises = [];
154
+
155
+ if(dataSources && dataSources.length>0){
156
+ if(dataSources.includes('mydata') && searchPersonal){
157
+ promises.push(callPathway('cognitive_search', { ...args, ...generateExtraArgs(searchPersonal), indexName: 'indexcortex' }));
158
+ }
159
+ }
160
+
161
+ const bingAvailable = !!config.getEnv()["AZURE_BING_KEY"];
162
+ if(bingAvailable && searchBing){
163
+ const handleRejection = (promise) => {
164
+ return promise.catch((error) => {
165
+ logger.error(`Error occurred: ${error}`);
166
+ return null;
167
+ });
168
+ }
169
+
170
+ promises.push(handleRejection(callPathway('bing', { ...args, ...generateExtraArgs(searchBing)})));
171
+ }
172
+
173
+ const parseBing = (response) => {
174
+ return JSON.parse(response)?.webPages?.value.map(({ name, url, snippet }) => ({ title: name, url, content: snippet }));
175
+ }
176
+
177
+ // Sample results from the index searches proportionally to the number of results returned
178
+ const maxSearchResults = 10;
179
+ const promiseResults = await Promise.all(promises);
180
+ const promiseData = promiseResults
181
+ .filter(r => r !== undefined && r !== null)
182
+ .map(r => JSON.parse(r)?._type=="SearchResponse" ? parseBing(r) : JSON.parse(r)?.value || []);
183
+
184
+ let totalLength = promiseData.reduce((sum, data) => sum + data.length, 0);
185
+ let remainingSlots = maxSearchResults;
186
+ let searchResults = [];
187
+
188
+ let indexCount = 0;
189
+ for(let data of promiseData) {
190
+ indexCount++;
191
+ const rowCount = data.length;
192
+ if (rowCount === 0) {
193
+ logger.debug(`Index ${indexCount} had no matching sources.`);
194
+ continue;
195
+ }
196
+ const proportion = rowCount / totalLength;
197
+ let slots = Math.max(Math.round(proportion * maxSearchResults), 1);
198
+
199
+ // Adjust slots based on remaining slots
200
+ slots = Math.min(slots, remainingSlots);
201
+
202
+ // Splice out the slots from the data and push to the search results
203
+ let items = data.splice(0, slots);
204
+ searchResults.push(...items);
205
+
206
+ logger.debug(`Index ${indexCount} had ${rowCount} matching sources. ${items.length} forwarded to the LLM.`);
207
+ // Update remaining slots for next iteration
208
+ remainingSlots -= slots;
209
+ }
210
+
211
+ searchResults = searchResults.slice(0, maxSearchResults); // in case we end up with rounding more than maxSearchResults
212
+
213
+ const numSearchResults = Math.min(searchResults.length, maxSearchResults);
214
+ const targetSourceLength = (maxSourcesPromptLength / numSearchResults) >> 0;
215
+
216
+ const getSource = (source, index) => {
217
+ const { title, content, url } = source;
218
+ let result = [];
219
+ result.push(`[source ${index + 1}]`);
220
+ title && result.push(`title: ${title}`);
221
+ url && result.push(`url: ${url}`);
222
+
223
+ if (content) {
224
+ let encodedContent = gpt3Encode(content);
225
+ let currentLength = result.join(" ").length; // Calculate the length of the current result string
226
+
227
+ if (currentLength + encodedContent.length > targetSourceLength) {
228
+ // Subtract the length of the current result string from targetSourceLength to get the maximum length for content
229
+ encodedContent = encodedContent.slice(0, targetSourceLength - currentLength);
230
+ const truncatedContent = gpt3Decode(encodedContent);
231
+ result.push(`content: ${truncatedContent}`);
232
+ } else {
233
+ result.push(`content: ${content}`);
234
+ }
235
+ }
236
+
237
+ return result.join(" ").trim();
238
+ }
239
+
240
+ let sources = searchResults.map(getSource).join(" \n\n ") || "No relevant sources found.";
241
+ dateFilter && sources.trim() && (sources+=`\n\n above sources are date filtered accordingly. \n\n`)
242
+
243
+ const result = await pathwayResolver.resolve({ ...args, sources, language:languageStr });
244
+
245
+ pathwayResolver.tool = JSON.stringify({ citations:searchResults }) // add tool info back
246
+
247
+ return result;
248
+ } catch (e) {
249
+ logger.error(e);
250
+ return await fetchJarvisResponse(args);
251
+ }
252
+ }
253
+ };
254
+
@@ -0,0 +1,21 @@
1
+ import { Prompt } from '../server/prompt.js';
2
+
3
+ export default {
4
+ inputParameters: {
5
+ chatHistory: [],
6
+ contextInfo: ``,
7
+ },
8
+ prompt:
9
+ [
10
+ new Prompt({ messages: [
11
+ {
12
+ "role": "system",
13
+ "content": `You are a search helper AI. Your role is 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. To augment your knowledge, you can use the Azure Cognitive Search indexes: "personal" for the user's documents and uploaded files. You can also use Bing Search to search the internet for relevant information if required.\n\nInstructions:\n\nAssess whether your response requires looking up additional information, context, or user data in your indexes. If it does set the field "searchRequired" to true.\n\nAssess whether you need to search one or more indexes or the internet for data. If you do, provide the relevant search string for that index in the corresponding field for the index: "searchPersonal", and "searchBing" respectively. Make sure that you continue to use relevant keywords (e.g. names, places, events) from previous searches if they exist to maintain context. If the user is asking for more information or details about a specific article, pull the relevant keywords and date information from the conversation context and do a specific search in the appropriate index for those keywords to attempt to get more of the article data. Keep in mind that if the user is asking for information without giving many usable keywords (e.g. "the news", "the latest", "this document", "this") that you can use a wildcard search over the appropriate index combined with a date filter to return likely relevant data.\n\nAssess whether the user is requesting business intelligence information that requires a search of internal data sources (e.g. article stats, popularity, authors, etc.). If so, set the "requiresBI" field to true, otherwise false.\n\nAs per the current timestamp, {{now}}, check if the user's query necessitates a date filter for data retrieval. If a date filter is required, formulate it in the OData $filter format (e.g., date ge 2010-01-01T00:00:00-08:00) and include it in the "dateFilter" field.\n\nDetermine the language of the user's request and fill the "language" field using the ISO 639-3 format and put the full language name in the "languageStr" field.\n\nIf a file upload is implied or requested by the user, set "upload" as true, else false.\n\nYou should only ever respond with the JSON object and never with any additional notes or commentary.\n\nRefer to prior user requests in this conversation for context:\n{{contextInfo}}`,
14
+ },
15
+ "{{chatHistory}}",
16
+ ]}),
17
+ ],
18
+ model: 'oai-gpt4o',
19
+ useInputChunking: false,
20
+ enableDuplicateRequests: false,
21
+ }
@@ -0,0 +1,18 @@
1
+ import { Prompt } from '../server/prompt.js';
2
+
3
+ export default {
4
+ prompt: [
5
+ new Prompt({
6
+ messages: [
7
+ { "role": "system", "content": "Assistant is a professional code writing assistant responsible for generating a README file in the typical Github style to accompany the code in a Github repository. When the user posts code or code diffs, assistant will examine the code and determine the most relevant parts to include in the readme. Assistant will generate only the readme and no other response or commentary.\nRespond with markdown where it helps make your output more readable." },
8
+ { "role": "user", "content": `Code:\n\n{{{text}}}`},
9
+ ]
10
+ })
11
+ ],
12
+ model: 'oai-gpt4o',
13
+ tokenRatio: 0.75,
14
+ enableDuplicateRequests: false,
15
+ timeout: 1800,
16
+ }
17
+
18
+
@@ -0,0 +1,16 @@
1
+ import { Prompt } from '../server/prompt.js';
2
+
3
+ export default {
4
+ prompt: [
5
+ new Prompt({
6
+ messages: [
7
+ { "role": "system", "content": "Assistant is a professional code writing assistant responsible for generating release notes to go in Github pull requests and releases. When user posts a list of code changes, assistant will examine the changes and determine the most relevant updates to include in the release notes. Assistant will generate only the release notes and no other response or commentary.\n\nAssistant may be generating notes for part of a larger code change, so ensure that your output is in a format that can be combined with other output to make a complete set of notes. Respond with markdown where it helps make your output more readable." },
8
+ { "role": "user", "content": `Code changes:\n\n{{{text}}}`},
9
+ ]
10
+ })
11
+ ],
12
+ model: 'oai-gpt4o',
13
+ tokenRatio: 0.75,
14
+ enableDuplicateRequests: false,
15
+ }
16
+
@@ -0,0 +1,31 @@
1
+ import { Prompt } from '../server/prompt.js';
2
+
3
+ export default {
4
+ prompt: [
5
+ new Prompt({
6
+ messages: [
7
+ { "role": "system", "content": "Assistant is highly skilled news editor at a prestigious international news agency. Assistant's task is to remove specific detail from a news excerpt" },
8
+ { "role": "user", "content": "NEWS: After weeks of resistance, and ahead of a vote that could have compelled it to happen, Prime Minister Justin Trudeau’s office announced Tuesday that his chief of staff Katie Telford will testify about foreign election interference, before a committee that has been studying the issue for months. DELETE: Katie Telford is Trudeaus's chief of staff" },
9
+ { "role": "assistant", "content": "After weeks of resistance, and ahead of a vote that could have compelled it to happen, Prime Minister Justin Trudeau’s office announced Tuesday that Katie Telford will testify about foreign election interference, before a committee that has been studying the issue for months" },
10
+ { "role": "user", "content": "NEWS: It seems that the arms race in the field of missiles and anti-missiles is in full swing these days. While traditional defenses are being thwarted in conflicts such as the Ukrainian War, and increasingly appearing in US defense plans, the anti-missile measures presented at the Johar Air Show indicate that Beijing is also preparing for this vital aspect of the future of wars. DELETE: Arms race in the field of missiles and anti-missiles is in full swing" },
11
+ { "role": "assistant", "content": "While traditional defenses are being thwarted in conflicts such as the Ukrainian War, and increasingly appearing in US defense plans, the anti-missile measures presented at the Johar Air Show indicate that Beijing is also preparing for this vital aspect of the future of wars." },
12
+ {
13
+ "role": "user", "content":
14
+ `NEWS: Sundowns leads the group with 11 points, one point ahead of Al-Hilal in second place. Al-Ahly comes in third place with 7 points, while Cotonsport of Cameroon is in last place without points.
15
+
16
+ Al-Hilal said in its statement that CAF had not responded to three previous letters and asked it to take a stance before a "new Burseid". DELETE: Cotonsport of Cameroon is in last place without points.`},
17
+ {
18
+ "role": "assistant", "content": `
19
+ Sundowns leads the group with 11 points, one point ahead of Al-Hilal in second place. Al-Ahly comes in third place with 7 points.
20
+
21
+ Al-Hilal said in its statement that CAF had not responded to three previous letters and asked it to take a stance before a "new Burseid". ` },
22
+ { "role": "user", "content": "NEWS: {{text}}. DELETE: {{content}}" },
23
+
24
+ ]
25
+ })],
26
+ inputParameters: {
27
+ content: "",
28
+ },
29
+ model: 'oai-gpt4o',
30
+ temperature: 0.0,
31
+ }
@@ -0,0 +1,23 @@
1
+ import { Prompt } from '../server/prompt.js';
2
+
3
+ // Description: Have a chat with a bot that uses context to understand the conversation + extension for Azure
4
+ export default {
5
+ prompt:
6
+ [
7
+ new Prompt({ messages: [
8
+ "{{chatHistory}}",
9
+ ]}),
10
+ ],
11
+ // prompt: `{{text}}`,
12
+ inputParameters: {
13
+ chatHistory: [],
14
+ contextId: ``,
15
+ indexName: ``,
16
+ semanticConfiguration: ``,
17
+ roleInformation: ``
18
+ },
19
+ model: `azure-extension`,
20
+ useInputChunking: false,
21
+ enableDuplicateRequests: false,
22
+ }
23
+
@@ -0,0 +1,21 @@
1
+
2
+ // Import required modules
3
+ import { Prompt } from '../server/prompt.js';
4
+
5
+ export default {
6
+ prompt: [
7
+ new Prompt({
8
+ messages: [
9
+ { "role": "system", "content": "{{{systemPrompt}}}" },
10
+ { "role": "user", "content": "{{{text}}}\n\n{{{prompt}}}" }
11
+ ]
12
+ }),
13
+ ],
14
+
15
+ inputParameters: {
16
+ prompt: "",
17
+ systemPrompt: "Assistant is an expert journalist's assistant for a prestigious international news agency. When a user posts a request, Assistant will come up with the best response while upholding the highest journalistic standards.",
18
+ },
19
+
20
+ model: 'claude-35-sonnet-vertex',
21
+ }
@@ -0,0 +1,20 @@
1
+ // Import required modules
2
+ import { Prompt } from '../server/prompt.js';
3
+
4
+ export default {
5
+ prompt: [
6
+ new Prompt({
7
+ messages: [
8
+ { "role": "system", "content": "{{{systemPrompt}}}" },
9
+ { "role": "user", "content": "{{{text}}}\n\n{{{prompt}}}" }
10
+ ]
11
+ }),
12
+ ],
13
+
14
+ inputParameters: {
15
+ prompt: "",
16
+ systemPrompt: "Assistant is an expert journalist's assistant for a prestigious international news agency. When a user posts a request, Assistant will come up with the best response while upholding the highest journalistic standards.",
17
+ },
18
+
19
+ model: 'claude-3-haiku-vertex',
20
+ }
@@ -0,0 +1,20 @@
1
+ // Import required modules
2
+ import { Prompt } from "../server/prompt.js"
3
+
4
+ export default {
5
+ prompt: [
6
+ new Prompt({
7
+ messages: [
8
+ { "role": "system", "content": "{{{systemPrompt}}}" },
9
+ { "role": "user", "content": "{{{text}}}\n\n{{{prompt}}}" }
10
+ ]
11
+ }),
12
+ ],
13
+
14
+ inputParameters: {
15
+ prompt: "",
16
+ systemPrompt: "Assistant is an expert journalist's assistant working for a prestigious international news agency. When a user posts a request, Assistant will come up with the best response while upholding the highest journalistic standards.",
17
+ },
18
+
19
+ model: 'oai-gpturbo',
20
+ }