@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,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: 'oai-gpt4',
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 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-gpt4-32',
20
+ }
@@ -0,0 +1,6 @@
1
+ // Description: Have a chat with a bot that uses context to understand the conversation
2
+ export default {
3
+ prompt: `User text: {{text}}\n\n Your Instructions: Analyze user text and extract all the following information. Decide to route messages to a knowledge base expert system when the user needs some information or documents or articles, or if user asks question that require specific knowledge, or if some extra knowledge can help you reply better; also anything related to news, articles, geopolitical entities, or current events should be forwarded as those can be found in the expert system. The expert system should not be consulted if the users message is just conversational. You will reply this in field useExpertSystem with true or false. Also in the text, the user may or may not have requested one of the following services:\n{"services": ["Coding", "Translate", "Transcribe", "Summary", "Headlines", "Entities", "Spelling", "Grammar", "Style", "Entities", "Newswires", "FileOrDocumentUpload"]}\nSelect the services the user requested (or none if none were requested) and return them as a JSON object field called "services". Also return the user text's language in language field in ISO 639-3 format. You will reply with the single valid JSON object (no other text or commentary) that must include JSON fields: useExpertSystem, services, language.\n\n`,
4
+ model: 'oai-gpt4o',
5
+ useInputChunking: false,
6
+ }
@@ -0,0 +1,10 @@
1
+ // Description: Select services from a conversation fragment
2
+ export default {
3
+ temperature: 0,
4
+ prompt:
5
+ [
6
+ `Conversation:\n{{text}}\n\nInstructions:\nIn the above conversation fragment, the user may or may not have requested one of the following services:\n{"services": ["Coding", "Translate", "Transcribe", "Summary", "Headlines", "Entities", "Spelling", "Grammar", "Style", "Entities", "Newswires", "FileOrDocumentUpload"]}\nSelect the services the user requested (or none if none were requested) and return them as a JSON object called "services" below:\n\n`,
7
+ ],
8
+ model: 'oai-gpt4o',
9
+ }
10
+
@@ -0,0 +1,3 @@
1
+ export default {
2
+ prompt: `{{text}}\n\nRewrite the above using British English spelling:`
3
+ }
@@ -0,0 +1,13 @@
1
+ import { Prompt } from "../server/prompt.js";
2
+
3
+ export default {
4
+ prompt: [new Prompt({
5
+ messages: [
6
+ { "role": "system", "content": "Assistant is highly skilled news editor at a prestigious international news agency. Assistant's task is to identify angles of emphasis in a news story." },
7
+ { "role": "user", "content": "Give me a numbered list of angles that can be emphasized in the following news excerpt. Don't need explanations, just a short phrase (< 5 words) describing the angle. Sort them by decreasing order of relevance.\n\nNews excerpt\n{{text}}" },
8
+ ]
9
+ })],
10
+ model: 'oai-gpt4o',
11
+ list: true,
12
+ useInputChunking: false,
13
+ }
@@ -0,0 +1,221 @@
1
+ import { Prompt } from '../../server/prompt.js';
2
+ import * as Diff from "diff";
3
+
4
+ const prompt = new Prompt({
5
+ messages: [
6
+ {
7
+ "role": "system", "content": `Assistant is a highly skilled copy editor for a prestigious news agency. When the user posts any text, assistant will correct all spelling and grammar in the text and change words to British English word spellings. Assistant will preserve html tags as well as text within square brackets. Assistant will also flawlessly apply the following rules from the style guide:
8
+ Don't use the % sign - spell out percent instead.
9
+ Expand all abbreviated month names.`},
10
+ { "role": "user", "content": "The total value of the deal was 12M euros." },
11
+ { "role": "assistant", "content": "The total value of the deal was 12 million euros." },
12
+ { "role": "user", "content": "they lost 20% of their money" },
13
+ { "role": "assistant", "content": "they lost 20 percent of their money" },
14
+ { "role": "system", "content": "Assistant will edit the entirety of whatever the user posts next according to the system instructions. Assistant will produce only the corrected text and no additional notes, dialog, or commentary." },
15
+ { "role": "user", "content": "{{{text}}}" }
16
+ ]
17
+ });
18
+
19
+ export default {
20
+ temperature: 0,
21
+ prompt: [prompt],
22
+ // inputFormat: 'html',
23
+ useInputChunking: true,
24
+ inputChunkSize: 500,
25
+ enableDuplicateRequests: false,
26
+ useParallelChunkProcessing: true,
27
+ model: 'oai-gpt4o',
28
+ executePathway: async ({ args, runAllPrompts }) => {
29
+ const originalText = args.text;
30
+
31
+ const suggestions = [];
32
+
33
+ const rulesBySuspect = getStyleGuideRules();
34
+ addEntriesForSuspectsFromStyleGuide(originalText, rulesBySuspect, suggestions);
35
+
36
+ const correctedText = await runAllPrompts(args);
37
+
38
+ // If correctedText is null, then the call to the AI resulted in an error.
39
+ if (correctedText) {
40
+ addEntriesForAutoCorrectedWords(originalText, correctedText, suggestions);
41
+ }
42
+
43
+ // Remove overlapping suggestions
44
+ suggestions.sort((a, b) => a.index - b.index);
45
+ for (let i = 0; i < suggestions.length; i++) {
46
+ const suggestion = suggestions[i];
47
+ const nextSuggestion = suggestions[i + 1];
48
+
49
+ if (nextSuggestion && suggestion.index + suggestion.suspect.length >= nextSuggestion.index) {
50
+ // remove nextSuggestion
51
+ suggestions.splice(i + 1, 1);
52
+ i--;
53
+ }
54
+ }
55
+
56
+ return JSON.stringify({ text: originalText, suggestions: suggestions })
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Adds entries to the suggestions array for words that the style guide defines as suspects.
62
+ *
63
+ * @param {string} originalText
64
+ * @param {object} rulesBySuspect
65
+ * @param {string[]} suggestions
66
+ */
67
+ function addEntriesForSuspectsFromStyleGuide(originalText, rulesBySuspect, suggestions) {
68
+ for (const suspect in rulesBySuspect) {
69
+ if (suspect) {
70
+ const suspectRegex = new RegExp(`\\b${suspect}\\b`, 'gi');
71
+ const suspectMatches = [...originalText.matchAll(suspectRegex)];
72
+ suspectMatches.forEach(m => {
73
+ const { notes } = rulesBySuspect[suspect];
74
+
75
+ const matchIndex = m.index;
76
+
77
+ // if this suspect falls within another, then skip it
78
+ // an example of this is the expression "said that"
79
+ // there's a rule for "said that" that and there's another rule for "that"
80
+ // if we don't skip the "that" rule, then we'll get two suggestions for the same word
81
+ if (suggestions.some(s => matchIndex >= s.index && matchIndex <= s.index + s.suspect.length)) {
82
+ return;
83
+ }
84
+
85
+ const newSuggestion = {
86
+ suspect,
87
+ suggestions: rulesBySuspect[suspect].suggestions,
88
+ index: m.index,
89
+ notes,
90
+ }
91
+
92
+ const existingSuggestionIndex = suggestions.findIndex(s => s.index === newSuggestion.index);
93
+
94
+ if (existingSuggestionIndex > -1) {
95
+ suggestions[existingSuggestionIndex] = newSuggestion;
96
+ } else {
97
+ suggestions.push(newSuggestion);
98
+ }
99
+ });
100
+ }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Adds entries to the suggestions array for words that were auto-corrected by the AI.
106
+ * These will be used by the UI to a render a diff.
107
+ *
108
+ * @param {string} originalText
109
+ * @param {string} correctedText
110
+ * @param {string[]} suggestions
111
+ */
112
+ function addEntriesForAutoCorrectedWords(originalText, correctedText, suggestions) {
113
+ let currentIndex = 0;
114
+ let currentSuggestion = null;
115
+
116
+ const normalizeQuotesAndSpaces = text => text.replace(/“|”/g, '"').replace(/‘|’/g, "'").replace(/\u00A0/g, ' ');
117
+
118
+ const diffGroups = Diff.diffWordsWithSpace(normalizeQuotesAndSpaces(originalText), normalizeQuotesAndSpaces(correctedText));
119
+ diffGroups.forEach((part, i) => {
120
+ const nextToken = diffGroups[i + 1];
121
+ const isNextTokenChange = nextToken?.added || nextToken?.removed;
122
+ const isWhiteSpaceBetweenChanges = part.value === ' ' && currentSuggestion && isNextTokenChange;
123
+
124
+ if (isWhiteSpaceBetweenChanges) {
125
+ currentIndex += part.value.length;
126
+ return ' ';
127
+ }
128
+
129
+ if (part.added) {
130
+ currentSuggestion = currentSuggestion || { index: currentIndex, suspect: "", suggestions: [], notes: "Suggested by AI", accepted: true, suggestionIndex: 0 };
131
+ currentSuggestion.suggestions[0] = currentSuggestion.suggestions[0] || "";
132
+
133
+ if (currentSuggestion.suggestions[0] && !currentSuggestion.suggestions[0].endsWith(' ')) {
134
+ currentSuggestion.suggestions[0] += " " + part.value;
135
+ }
136
+ else {
137
+ currentSuggestion.suggestions[0] += part.value;
138
+ }
139
+ }
140
+
141
+ if (part.removed) {
142
+ currentSuggestion = currentSuggestion || { index: currentIndex, suspect: "", suggestions: [], notes: "Suggested by AI", accepted: true, suggestionIndex: 0 };
143
+
144
+ if (currentSuggestion.suspect && !currentSuggestion.suspect.endsWith(' ')) {
145
+ currentSuggestion.suspect += " " + part.value;
146
+ }
147
+ else {
148
+ currentSuggestion.suspect += part.value;
149
+ }
150
+
151
+ currentIndex += part.value.length;
152
+ }
153
+
154
+ if (!part.added && !part.removed) {
155
+ currentIndex += part.value.length;
156
+ if (currentSuggestion) {
157
+ // if a suggestion with the index exists, replace it
158
+ const existingSuggestionIndex = suggestions.findIndex(s => s.index === currentSuggestion.index);
159
+
160
+ if (existingSuggestionIndex > -1) {
161
+ suggestions[existingSuggestionIndex] = currentSuggestion;
162
+ } else {
163
+ suggestions.push(currentSuggestion);
164
+ }
165
+ currentSuggestion = null;
166
+ }
167
+ }
168
+ });
169
+
170
+ if (currentSuggestion) {
171
+ suggestions.push(currentSuggestion);
172
+ }
173
+ }
174
+
175
+ /**
176
+ * @returns {Object} { suspect: { suggestions: string[], notes: string }
177
+ */
178
+ function getStyleGuideRules() {
179
+ try {
180
+ const rules = [{
181
+ "Type": "City",
182
+ "Name": "al-Makha",
183
+ "Notes": "Port city in Yemen. First reference, include \"(Mocha)\" then go with al-Makha",
184
+ "Suspects": "Mokha al-Mokha Al-Mokha al Mokha Al Mokha Mocha al-Mocha Al-Mocha al Mocha Al Mocha",
185
+ "Suggestions": "al-Makha (Mocha)"
186
+ }];
187
+
188
+
189
+ // Group rows by suspect (since each row may have multiple suspects)
190
+ const rulesBySuspect = rules.reduce((acc, r) => {
191
+ if (r) {
192
+ // some suspects in the data source have 3 spaces between them, so if we split terms by 2 spaces
193
+ // one of the terms will have an extra space, so trim() it.
194
+ const suspects = r.Suspects?.split(" ").map(s => s.trim()) || [];
195
+ let suggestions = r.Suggestions?.split(" ") || [];
196
+
197
+ // filter suggestions in parentheses as those tend to be notes
198
+ suggestions = suggestions.filter(s => !s.match(/^\(.*\)$/));
199
+
200
+ // also filter suggestions that are just a note
201
+ suggestions = suggestions.filter(s => !s.match(/^see notes/gi));
202
+
203
+ const notes = r.Notes;
204
+ suspects.forEach(s => {
205
+ if (!acc[s]) {
206
+ acc[s] = [];
207
+ }
208
+ acc[s] = { suggestions, notes };
209
+ });
210
+ }
211
+
212
+ return acc;
213
+ }, {});
214
+
215
+ return rulesBySuspect;
216
+ }
217
+ catch (e) {
218
+ console.error("An error occurred while trying to read style guide rules", e);
219
+ process.exit(1);
220
+ }
221
+ }
@@ -0,0 +1,127 @@
1
+ import { Prompt } from '../server/prompt.js';
2
+
3
+ export default {
4
+ temperature: 0,
5
+ prompt: [
6
+ new Prompt({ messages: [
7
+ {"role": "system", "content": "Assistant is a highly skilled copy editor for a prestigious news agency. When the user posts any text, assistant will correct all spelling and grammar in the text and change words to British English word spellings. Assistant will preserve html tags as well as text within square brackets. Assistant will also flawlessly apply the following rules from the style guide:\n Capitalization: Unless using a full official title, use lower case. Jobs are lower case. If the individuals name follows, you capitalise the title. We only capitalise the name of ministries if it is their proper name. Place names and organisations are capitalised. Police, armies, navies, air forces, coastguards etc do not require capitals. Capitalize political designations, but geographic designations remain lower case.\nDon't use the % sign - spell out percent instead.\nExpand all abbreviated month names.\nExpand monetary abbreviations.\nDo not use nouns or adjectives as verbs.\nDo not mix tenses.\nMask any profanity with the first letter of the word followed by asterisks."},
8
+ {"role": "user", "content": "The $20 bill was the wrong color."},
9
+ {"role": "assistant", "content": "The $20 bill was the wrong colour."},
10
+ {"role": "user", "content": "the US Secretary of State"},
11
+ {"role": "assistant", "content": "the US secretary of state"},
12
+ {"role": "user", "content": "prime minister Theresa May"},
13
+ {"role": "assistant", "content": "Prime Minister Theresa May"},
14
+ {"role": "user", "content": "the Belgian Prime Minister"},
15
+ {"role": "assistant", "content": "the Belgian prime minister"},
16
+ {"role": "user", "content": "the us state department"},
17
+ {"role": "assistant", "content": "the US state department"},
18
+ {"role": "user", "content": "Here is the podcast: [audio id=\"123\" start=\"1:23\" end=\"1:25\"]podcast[/audio]"},
19
+ {"role": "assistant", "content": "Here is the podcast: [audio id=\"123\" start=\"1:23\" end=\"1:25\"]podcast[/audio]"},
20
+ {"role": "user", "content": "<p>the US department of state</p>"},
21
+ {"role": "assistant", "content": "<p>the US Department of State</p>"},
22
+ {"role": "user", "content": "the US Air Force"},
23
+ {"role": "assistant", "content": "the US air force"},
24
+ {"role": "user", "content": "the west, western nations"},
25
+ {"role": "assistant", "content": "the West, Western nations"},
26
+ {"role": "user", "content": "they lost 20% of their money"},
27
+ {"role": "assistant", "content": "they lost 20 percent of their money"},
28
+ {"role": "user", "content": "He lost 10% of his weight between Jun and Aug."},
29
+ {"role": "assistant", "content": "He lost ten percent of his weight between June and August."},
30
+ {"role": "user", "content": "The US gave Ukraine $1bn in aid."},
31
+ {"role": "assistant", "content": "The US gave Ukraine one billion dollars in aid."},
32
+ {"role": "user", "content": "The total value of the deal was 12M euros."},
33
+ {"role": "assistant", "content": "The total value of the deal was 12 million euros."},
34
+ {"role": "user", "content": "they progress reports, they action an order"},
35
+ {"role": "assistant", "content": "they made progress on reports, they acted on an order"},
36
+ {"role": "user", "content": "The <a href=\"vote.com\">vote</a> took place Monday."},
37
+ {"role": "assistant", "content": "The <a href=\"vote.com\">vote</a> took place on Monday."},
38
+ {"role": "user", "content": "Michael wrote John."},
39
+ {"role": "assistant", "content": "Michael wrote to John."},
40
+ {"role": "user", "content": "They protested the law."},
41
+ {"role": "assistant", "content": "They protested against the law."},
42
+ {"role": "user", "content": "<p>The protests erupted yestrday.</p>"},
43
+ {"role": "assistant", "content": "<p>The protests erupted yesterday.</p>"},
44
+ {"role": "user", "content": "They protested the law."},
45
+ {"role": "assistant", "content": "They protested against the law."},
46
+ {"role": "user", "content": "He appealed the decision of the court."},
47
+ {"role": "assistant", "content": "He appealed against the decision of the court."},
48
+ {"role": "user", "content": "Yesterday, the Prime Minister announced that he will be introducing a new policy to combat climate change. The policy aims to reduce carbon emissions by 50% by 2030."},
49
+ {"role": "assistant", "content": "Yesterday, the Prime Minister announced that he would be introducing a new policy to combat climate change. The policy aims to reduce carbon emissions by 50 percent by 2030."},
50
+ {"role": "user", "content": "Why does he always want to go to the damn circus?"},
51
+ {"role": "assistant", "content": "Why does he always want to go to the d*** circus?"},
52
+ {"role": "user", "content": `June was quoted as saying "What is this shit?! I'm not a fucking idiot!"`},
53
+ {"role": "assistant", "content": `June was quoted as saying "What is this s***?! I'm not a f***ing idiot!"`},
54
+ {"role": "system", "content": "Assistant will edit the entirety of whatever the user posts next according to the system instructions. Assistant will produce only the corrected text and no additional notes, dialog, or commentary."},
55
+ {"role": "user", "content": "{{{text}}}"}
56
+ ]}),
57
+ /*
58
+ new Prompt({ messages: [
59
+ {"role": "system", "content": "Assistant is a highly skilled multilingual copy editor for a prestigious news agency. When the user posts any text in any language, assistant will correct all spelling and grammar in the text and change words to British English word spellings. Assistant will produce only the corrected text and no additional notes or commentary."},
60
+ {"role": "user", "content": "The $20 bill was the wrong color."},
61
+ {"role": "assistant", "content": "The $20 bill was the wrong colour."},
62
+ {"role": "user", "content": "{{{text}}}"}
63
+ ]}),
64
+ new Prompt({ messages: [
65
+ {"role": "system", "content": "Assistant is a highly skilled multilingual copy editor for a prestigious news agency. When the user posts any text in any language, assistant will flawlessly apply the following rules from the style guide: Capitalization rules: Unless using a full official title, use lower case. Jobs are lower case. If the individuals name follows, you capitalise the title. We only capitalise the name of ministries if it is their proper name. Place names and organisations are capitalised. Police, armies, navies, air forces, coastguards etc do not require capitals. Capitalize political designations, but geographic designations remain lower case."},
66
+ {"role": "user", "content": "the US Secretary of State"},
67
+ {"role": "assistant", "content": "the US secretary of state"},
68
+ {"role": "user", "content": "prime minister Theresa May"},
69
+ {"role": "assistant", "content": "Prime Minister Theresa May"},
70
+ {"role": "user", "content": "the us state department"},
71
+ {"role": "assistant", "content": "the US state department"},
72
+ {"role": "user", "content": "the US department of state"},
73
+ {"role": "assistant", "content": "the US Department of State"},
74
+ {"role": "user", "content": "the US Air Force"},
75
+ {"role": "assistant", "content": "the US air force"},
76
+ {"role": "user", "content": "the west, western nations"},
77
+ {"role": "assistant", "content": "the West, Western nations"},
78
+ {"role": "user", "content": "{{{previousResult}}}"}
79
+ ]}),
80
+ new Prompt({ messages: [
81
+ {"role": "system", "content": "Assistant is a highly skilled multilingual copy editor for a prestigious news agency. When the user posts any text in any language, assistant will flawlessly apply the following rule from the style guide: Don't use the % sign - spell out percent instead. Expand all abbreviated month names. Expand monetary abbreviations."},
82
+ {"role": "user", "content": "they lost 20% of their money"},
83
+ {"role": "assistant", "content": "they lost 20 percent of their money"},
84
+ {"role": "user", "content": "He lost 10% of his weight between Jun and Aug."},
85
+ {"role": "assistant", "content": "He lost ten percent of his weight between June and August."},
86
+ {"role": "user", "content": "The US gave Ukraine $1bn in aid."},
87
+ {"role": "assistant", "content": "The US gave Ukraine one billion dollars in aid."},
88
+ {"role": "user", "content": "The total value of the deal was 12M euros."},
89
+ {"role": "assistant", "content": "The total value of the deal was 12 million euros."},
90
+ {"role": "user", "content": "{{{previousResult}}}"}
91
+ ]}),
92
+ new Prompt({ messages: [
93
+ {"role": "system", "content": "Assistant is a highly skilled multilingual copy editor for a prestigious news agency. When the user posts any text in any language, assistant will flawlessly apply the following rule from the style guide: Do not use nouns or adjectives as verbs."},
94
+ {"role": "user", "content": "they progress reports, they action an order"},
95
+ {"role": "assistant", "content": "they made progress on reports, they acted on an order"},
96
+ {"role": "user", "content": "The vote took place Monday."},
97
+ {"role": "assistant", "content": "The vote took place on Monday."},
98
+ {"role": "user", "content": "Michael wrote John."},
99
+ {"role": "assistant", "content": "Michael wrote to John."},
100
+ {"role": "user", "content": "They protested the law."},
101
+ {"role": "assistant", "content": "They protested against the law."},
102
+ {"role": "user", "content": "He appealed the decision of the court."},
103
+ {"role": "assistant", "content": "He appealed against the decision of the court."},
104
+ {"role": "user", "content": "{{{previousResult}}}"}
105
+ ]}),
106
+ new Prompt({ messages: [
107
+ {"role": "system", "content": "Assistant is a highly skilled multilingual copy editor for a prestigious news agency. When the user posts any text in any language, assistant will flawlessly apply the following rule from the style guide: Do not mix tenses."},
108
+ {"role": "user", "content": "Yesterday, the Prime Minister announced that he will be introducing a new policy to combat climate change. The policy aims to reduce carbon emissions by 50% by 2030."},
109
+ {"role": "assistant", "content": "Yesterday, the Prime Minister announced that he would be introducing a new policy to combat climate change. The policy aims to reduce carbon emissions by 50 percent by 2030."},
110
+ {"role": "user", "content": "{{{previousResult}}}"}
111
+ ]}),
112
+ new Prompt({ messages: [
113
+ {"role": "system", "content": "Assistant is a highly skilled multilingual copy editor for a prestigious news agency. When the user posts any text in any language, assistant will flawlessly apply the following rule from the style guide: Mask any profanity with the first letter of the word followed by asterisks."},
114
+ {"role": "user", "content": "Why does he always want to go to the damn circus?"},
115
+ {"role": "assistant", "content": "Why does he always want to go to the d*** circus?"},
116
+ {"role": "user", "content": `June was quoted as saying "What is this shit?! I'm not a fucking idiot!"`},
117
+ {"role": "assistant", "content": `June was quoted as saying "What is this s***?! I'm not a f***ing idiot!"`},
118
+ {"role": "user", "content": "{{{previousResult}}}"}
119
+ ]}),*/
120
+ ],
121
+ inputFormat: 'html',
122
+ useInputChunking: true,
123
+ inputChunkSize: 500,
124
+ useParallelChunkProcessing: true,
125
+ model: 'oai-gpt4o',
126
+ enableDuplicateRequests: false,
127
+ }
@@ -0,0 +1,48 @@
1
+ import { Prompt } from '../server/prompt.js';
2
+ import { PathwayResolver } from '../server/pathwayResolver.js';
3
+
4
+ export default {
5
+ prompt: [],
6
+ inputParameters: {
7
+ count: 5,
8
+ targetLength: 120,
9
+ headline: '',
10
+ },
11
+ list: true,
12
+ model: 'oai-gpt4o',
13
+ useInputChunking: false,
14
+
15
+ // Custom resolver to generate subheads by reprompting if they are too long
16
+ resolver: async (_parent, args, contextValue, _info) => {
17
+ const { config, pathway } = contextValue;
18
+ let targetLength = args.targetLength || 120;
19
+ let count = args.count || 5;
20
+
21
+ const targetWords = Math.round(targetLength / 6);
22
+ const MAX_ITERATIONS = 3;
23
+
24
+ let pathwayResolver = new PathwayResolver({ config, pathway, args });
25
+
26
+ pathwayResolver.pathwayPrompt = [
27
+ new Prompt({
28
+ messages: [
29
+ { "role": "system", "content": `Assistant is a highly skilled multilingual writer for a prestigious international news agency. Assistant generates descriptive, informative, and engaging subheadings that are consistent with and continue the flow of the headline for the article. When the user posts a headline and an article excerpt in any language, assistant will create ${ count * 2 } subheadings for that headline in the same language as the headline. Assistant will produce only a numbered list of subheadings and no additional notes or commentary.\n\nAll subheadings must comply with all of the following instructions:\n- Subheadings must not be enclosed in quotation marks\n- Subheadings must be ${ targetWords } words or fewer.\n- Subheadings must be written in sentence-case (only the first letter of the headline and proper nouns capitalized).\n` },
30
+ { "role": "user", "content": `Headline: {{{headline}}}\nArticle Excerpt:\n{{{text}}}` }
31
+ ]
32
+ }),
33
+ ];
34
+
35
+ let subheads = await pathwayResolver.resolve(args);
36
+ let shortSubheads = subheads.filter(h => h.length > 80 && h.length < targetLength).slice(0, count);
37
+ let i = 0;
38
+
39
+ // if some subheads do not meet the length requirement, reprompt
40
+ while (shortSubheads.length < count && i < MAX_ITERATIONS) {
41
+ let subheads = await pathwayResolver.resolve(args);
42
+ shortSubheads = subheads.filter(h => h.length > 80 && h.length < targetLength).slice(0, count);
43
+ i++;
44
+ }
45
+
46
+ return shortSubheads;
47
+ }
48
+ }
@@ -0,0 +1,98 @@
1
+ // Text summarization module with custom resolver for turbo models
2
+ // This module exports a prompt that takes an input text and generates a summary using a custom resolver.
3
+
4
+ // Import required modules
5
+ import { semanticTruncate } from '../server/chunker.js';
6
+ import { PathwayResolver } from '../server/pathwayResolver.js';
7
+ import { Prompt } from '../server/prompt.js';
8
+ import { callPathway } from '../lib/pathwayTools.js';
9
+
10
+ export default {
11
+ // Define input parameters for the prompt, such as the target length of the summary.
12
+ inputParameters: {
13
+ targetLength: 0,
14
+ targetLanguage: ''
15
+ },
16
+
17
+ model: 'oai-gpt4o',
18
+
19
+ // Custom resolver to generate summaries by reprompting if they are too long or too short.
20
+ resolver: async (parent, args, contextValue, _info) => {
21
+ const { config, pathway } = contextValue;
22
+ const originalTargetLength = args.targetLength || 0;
23
+ const targetLanguage = args.targetLanguage || await callPathway('language', args);
24
+
25
+ const targetLanguagePrompt = targetLanguage ? `language '${targetLanguage}'` : 'same language as the text being summarized';
26
+
27
+ // If targetLength is not provided, execute the prompt once and return the result.
28
+ if (originalTargetLength === 0) {
29
+ let pathwayResolver = new PathwayResolver({ config, pathway, args });
30
+ pathwayResolver.pathwayPrompt = [
31
+ new Prompt({ messages: [
32
+ {"role": "system", "content": `Assistant is a highly skilled multilingual AI writing agent that summarizes text. When the user posts any text in any language, assistant will create a detailed summary of that text. Assistant will produce only the summary text and no additional or other response. The summary must be in the ${targetLanguagePrompt}.`},
33
+ {"role": "user", "content": "Text to summarize:\n{{{text}}}"}
34
+ ]}),
35
+ ];
36
+ return await pathwayResolver.resolve(args);
37
+ }
38
+
39
+ const errorMargin = 0.1;
40
+ const lowTargetLength = originalTargetLength * (1 - errorMargin);
41
+ const targetWords = Math.round(originalTargetLength / 6.6);
42
+
43
+ // If the text is shorter than the summary length, just return the text.
44
+ if (args.text.length <= originalTargetLength) {
45
+ return args.text;
46
+ }
47
+
48
+ const MAX_ITERATIONS = 5;
49
+ let summary = '';
50
+ let pathwayResolver = new PathwayResolver({ config, pathway, args });
51
+
52
+ // Modify the prompt to be words-based instead of characters-based.
53
+ pathwayResolver.pathwayPrompt = [
54
+ new Prompt({ messages: [
55
+ {"role": "system", "content": `Assistant is a highly skilled multilingual AI writing agent that summarizes text. When the user posts any text in any language, assistant will create a detailed summary of that text. The summary should be ${targetWords} words long. Assistant will produce only the summary text and no additional or other response. The summary must be in the ${targetLanguagePrompt}.`},
56
+ {"role": "user", "content": "Text to summarize:\n{{{text}}}"}
57
+ ]}),
58
+ ];
59
+
60
+ let i = 0;
61
+ // Make sure it's long enough to start
62
+ while ((summary.length < lowTargetLength) && i < MAX_ITERATIONS) {
63
+ summary = await pathwayResolver.resolve(args);
64
+ i++;
65
+ }
66
+
67
+ // If it's too long, it could be because the input text was chunked
68
+ // and now we have all the chunks together. We can summarize that
69
+ // to get a comprehensive summary.
70
+ if (summary.length > originalTargetLength) {
71
+ pathwayResolver.pathwayPrompt = [
72
+ new Prompt({ messages: [
73
+ {"role": "system", "content": `Assistant is a highly skilled multilingual AI writing agent that summarizes text. When the user posts any text in any language, assistant will create a detailed summary of that text. The summary should be ${targetWords} words long. Assistant will produce only the summary text and no additional or other response. The summary must be in the ${targetLanguagePrompt}.`},
74
+ {"role": "user", "content": `Text to summarize:\n${summary}`}
75
+ ]}),
76
+ ];
77
+ summary = await pathwayResolver.resolve(args);
78
+ i++;
79
+
80
+ // Now make sure it's not too long
81
+ while ((summary.length > originalTargetLength) && i < MAX_ITERATIONS) {
82
+ // add the summary response from the assistant to the prompt
83
+ pathwayResolver.pathwayPrompt[0].messages.push({"role": "assistant", "content": summary});
84
+ // add the next query to the prompt
85
+ pathwayResolver.pathwayPrompt[0].messages.push({"role": "system", "content": `Is that less than ${targetWords} words long? If not, try again using a length of no more than ${targetWords} words. Generate only the summary text and no apology or other response. The summary must be in the ${targetLanguagePrompt}.`});
86
+ summary = await pathwayResolver.resolve(args);
87
+ i++;
88
+ }
89
+ }
90
+
91
+ // If the summary is still too long, truncate it.
92
+ if (summary.length > originalTargetLength) {
93
+ return semanticTruncate(summary, originalTargetLength);
94
+ } else {
95
+ return summary;
96
+ }
97
+ }
98
+ }