@aj-archipelago/cortex 1.3.21 → 1.3.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 (32) hide show
  1. package/helper-apps/cortex-realtime-voice-server/src/cortex/memory.ts +2 -2
  2. package/lib/util.js +1 -1
  3. package/package.json +1 -1
  4. package/pathways/system/entity/memory/shared/sys_memory_helpers.js +228 -0
  5. package/pathways/system/entity/memory/sys_memory_format.js +30 -0
  6. package/pathways/system/entity/memory/sys_memory_manager.js +85 -27
  7. package/pathways/system/entity/memory/sys_memory_process.js +154 -0
  8. package/pathways/system/entity/memory/sys_memory_required.js +4 -2
  9. package/pathways/system/entity/memory/sys_memory_topic.js +22 -0
  10. package/pathways/system/entity/memory/sys_memory_update.js +50 -150
  11. package/pathways/system/entity/memory/sys_read_memory.js +67 -69
  12. package/pathways/system/entity/memory/sys_save_memory.js +1 -1
  13. package/pathways/system/entity/memory/sys_search_memory.js +1 -1
  14. package/pathways/system/entity/sys_entity_start.js +9 -6
  15. package/pathways/system/entity/sys_generator_image.js +5 -41
  16. package/pathways/system/entity/sys_generator_memory.js +3 -1
  17. package/pathways/system/entity/sys_generator_reasoning.js +1 -1
  18. package/pathways/system/entity/sys_router_tool.js +3 -4
  19. package/pathways/system/rest_streaming/sys_claude_35_sonnet.js +1 -1
  20. package/pathways/system/rest_streaming/sys_claude_3_haiku.js +1 -1
  21. package/pathways/system/rest_streaming/sys_google_gemini_chat.js +1 -1
  22. package/pathways/system/rest_streaming/sys_openai_chat_o1.js +1 -1
  23. package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +1 -1
  24. package/pathways/transcribe_gemini.js +397 -0
  25. package/server/pathwayResolver.js +7 -7
  26. package/server/plugins/claude3VertexPlugin.js +109 -3
  27. package/server/plugins/gemini15VisionPlugin.js +7 -0
  28. package/server/plugins/modelPlugin.js +1 -1
  29. package/server/rest.js +24 -3
  30. package/tests/claude3VertexToolConversion.test.js +411 -0
  31. package/tests/memoryfunction.test.js +560 -46
  32. package/tests/openai_api.test.js +332 -0
@@ -24,8 +24,8 @@ query ManageMemory($contextId: String, $chatHistory: [MultiMessage], $aiName: St
24
24
  `
25
25
 
26
26
  const READ_MEMORY = `
27
- query ReadMemory($contextId: String, $section: String, $priority: Int, $recentHours: Int, $numResults: Int) {
28
- sys_read_memory(contextId: $contextId, section: $section, priority: $priority, recentHours: $recentHours, numResults: $numResults) {
27
+ query ReadMemory($contextId: String, $section: String, $priority: Int, $recentHours: Int, $numResults: Int, $stripMetadata: Boolean) {
28
+ sys_read_memory(contextId: $contextId, section: $section, priority: $priority, recentHours: $recentHours, numResults: $numResults, stripMetadata: $stripMetadata) {
29
29
  result
30
30
  tool
31
31
  warnings
package/lib/util.js CHANGED
@@ -35,7 +35,7 @@ function chatArgsHasType(args, type){
35
35
  const contents = Array.isArray(ch.content) ? ch.content : [ch.content];
36
36
  for(const content of contents){
37
37
  try{
38
- if(JSON.parse(content).type == type){
38
+ if((content?.type || JSON.parse(content).type) == type){
39
39
  return true;
40
40
  }
41
41
  }catch(e){
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-archipelago/cortex",
3
- "version": "1.3.21",
3
+ "version": "1.3.22",
4
4
  "description": "Cortex is a GraphQL API for AI. It provides a simple, extensible interface for using AI services from OpenAI, Azure and others.",
5
5
  "private": false,
6
6
  "repository": {
@@ -0,0 +1,228 @@
1
+ import { callPathway } from '../../../../../lib/pathwayTools.js';
2
+ import { encode } from '../../../../../lib/encodeCache.js';
3
+ import { getUniqueId } from '../../../../../lib/util.js';
4
+
5
+ const normalizeMemoryFormat = async (args, content) => {
6
+ if (!content) return '';
7
+
8
+ const lines = content.split('\n').map(line => line.trim()).filter(line => line);
9
+ const validLines = [];
10
+ const invalidLines = [];
11
+
12
+ // Check each line for proper format (priority|timestamp|content)
13
+ for (const line of lines) {
14
+ const parts = line.split('|');
15
+ const isValid = parts.length >= 3 &&
16
+ /^\d+$/.test(parts[0]) &&
17
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(parts[1]);
18
+
19
+ if (isValid) {
20
+ validLines.push(line);
21
+ } else {
22
+ invalidLines.push(line);
23
+ }
24
+ }
25
+
26
+ // If we have invalid lines, format them
27
+ let formattedContent = validLines;
28
+ if (invalidLines.length > 0) {
29
+ const invalidBlock = invalidLines.join('\n');
30
+ try {
31
+ const formattedBlock = await callPathway("sys_memory_format", { ...args, text: invalidBlock });
32
+ if (formattedBlock) {
33
+ formattedContent = [...validLines, ...formattedBlock.split('\n')];
34
+ }
35
+ } catch (error) {
36
+ console.warn('Error formatting invalid memory lines:', error);
37
+ }
38
+ }
39
+
40
+ // Sort all lines by date descending
41
+ return formattedContent
42
+ .filter(line => line.trim())
43
+ .sort((a, b) => {
44
+ const [, timestampA] = a.split('|');
45
+ const [, timestampB] = b.split('|');
46
+ return new Date(timestampB) - new Date(timestampA);
47
+ })
48
+ .join('\n');
49
+ };
50
+
51
+ const enforceTokenLimit = (text, maxTokens = 1000, isTopicsSection = false) => {
52
+ if (!text) return text;
53
+
54
+ // Parse lines and remove duplicates
55
+ const seen = new Map();
56
+ const lines = text.split('\n')
57
+ .map(line => line.trim())
58
+ .filter(line => line)
59
+ .map(line => {
60
+ const [priority, timestamp, ...contentParts] = line.split('|');
61
+ return {
62
+ line,
63
+ priority: parseInt(priority || '3'),
64
+ timestamp: timestamp || new Date(0).toISOString(),
65
+ content: contentParts.join('|')
66
+ };
67
+ });
68
+
69
+ // Filter duplicates first
70
+ const uniqueLines = lines.reduce((acc, item) => {
71
+ const existing = seen.get(item.content);
72
+ if (!existing) {
73
+ seen.set(item.content, item);
74
+ acc.push(item);
75
+ } else if (isTopicsSection && item.timestamp > existing.timestamp) {
76
+ // For topics, keep newest timestamp
77
+ const index = acc.findIndex(x => x.content === item.content);
78
+ acc[index] = item;
79
+ seen.set(item.content, item);
80
+ } else if (!isTopicsSection && item.priority < existing.priority) {
81
+ // For non-topics, keep highest priority
82
+ const index = acc.findIndex(x => x.content === item.content);
83
+ acc[index] = item;
84
+ seen.set(item.content, item);
85
+ }
86
+ return acc;
87
+ }, []);
88
+
89
+ // Sort by timestamp (topics) or priority
90
+ uniqueLines.sort((a, b) => isTopicsSection ?
91
+ b.timestamp.localeCompare(a.timestamp) :
92
+ a.priority - b.priority
93
+ );
94
+
95
+ // First trim by character estimation (4 chars ≈ 1 token)
96
+ let result = uniqueLines;
97
+ let estimatedTokens = result.reduce((sum, item) => sum + Math.ceil(item.content.length / 4), 0);
98
+
99
+ while (estimatedTokens > maxTokens && result.length > 0) {
100
+ result = result.slice(0, -1);
101
+ estimatedTokens = result.reduce((sum, item) => sum + Math.ceil(item.content.length / 4), 0);
102
+ }
103
+
104
+ // Final trim using actual token count
105
+ let finalText = result.map(x => x.line).join('\n');
106
+ while (encode(finalText).length > maxTokens && result.length > 0) {
107
+ result = result.slice(0, -1);
108
+ finalText = result.map(x => x.line).join('\n');
109
+ }
110
+
111
+ return finalText;
112
+ };
113
+
114
+ const addToolCalls = (chatHistory, toolArgs, toolName, toolCallId = getUniqueId()) => {
115
+ const toolCall = {
116
+ "role": "assistant",
117
+ "tool_calls": [
118
+ {
119
+ "id": toolCallId,
120
+ "type": "function",
121
+ "function": {
122
+ "arguments": JSON.stringify(toolArgs),
123
+ "name": toolName
124
+ }
125
+ }
126
+ ]
127
+ };
128
+ chatHistory.push(toolCall);
129
+ return { chatHistory, toolCallId };
130
+ };
131
+
132
+ const addToolResults = (chatHistory, result, toolCallId) => {
133
+ const toolResult = {
134
+ "role": "tool",
135
+ "content": result,
136
+ "tool_call_id": toolCallId
137
+ };
138
+ chatHistory.push(toolResult);
139
+ return { chatHistory, toolCallId };
140
+ };
141
+
142
+ const modifyText = (text, modifications) => {
143
+ let modifiedText = text || '';
144
+
145
+ modifications.forEach(mod => {
146
+ // Skip invalid modifications
147
+ if (!mod.type) {
148
+ console.warn('Modification missing type');
149
+ return;
150
+ }
151
+ if ((mod.type === 'delete' || mod.type === 'change') && !mod.pattern) {
152
+ console.warn(`${mod.type} modification missing pattern`);
153
+ return;
154
+ }
155
+ if ((mod.type === 'add' || mod.type === 'change') && !mod.newtext) {
156
+ console.warn(`${mod.type} modification missing newtext`);
157
+ return;
158
+ }
159
+
160
+ // Create timestamp in GMT
161
+ const timestamp = new Date().toISOString();
162
+
163
+ switch (mod.type) {
164
+ case 'add':
165
+ const priority = mod.priority || '3';
166
+ modifiedText = modifiedText + (modifiedText ? '\n' : '') +
167
+ `${priority}|${timestamp}|${mod.newtext}`;
168
+ break;
169
+ case 'change':
170
+ // Split into lines
171
+ const lines = modifiedText.split('\n');
172
+ modifiedText = lines.map(line => {
173
+ const parts = line.split('|');
174
+ const priority = parts[0];
175
+ const content = parts.slice(2).join('|');
176
+
177
+ if (content) {
178
+ try {
179
+ const trimmedContent = content.trim();
180
+ const regex = new RegExp(mod.pattern, 'i');
181
+
182
+ // Try exact match first
183
+ if (regex.test(trimmedContent)) {
184
+ const newPriority = mod.priority || priority || '3';
185
+ // Try to extract capture groups if they exist
186
+ const match = trimmedContent.match(regex);
187
+ let newContent = mod.newtext;
188
+ if (match && match.length > 1) {
189
+ // Replace $1, $2, etc with capture group values
190
+ newContent = mod.newtext.replace(/\$(\d+)/g, (_, n) => match[n] || '');
191
+ }
192
+ return `${newPriority}|${timestamp}|${newContent}`;
193
+ }
194
+ } catch (e) {
195
+ console.warn(`Invalid regex pattern: ${mod.pattern}`);
196
+ }
197
+ }
198
+ return line;
199
+ }).join('\n');
200
+ break;
201
+ case 'delete':
202
+ // Split into lines, filter out matching lines, and rejoin
203
+ modifiedText = modifiedText
204
+ .split('\n')
205
+ .filter(line => {
206
+ const parts = line.split('|');
207
+ const content = parts.slice(2).join('|');
208
+ if (!content) return true;
209
+ try {
210
+ const regex = new RegExp(mod.pattern, 'i');
211
+ return !regex.test(content.trim());
212
+ } catch (e) {
213
+ console.warn(`Invalid regex pattern: ${mod.pattern}`);
214
+ return true;
215
+ }
216
+ })
217
+ .filter(line => line.trim())
218
+ .join('\n');
219
+ break;
220
+ default:
221
+ console.warn(`Unknown modification type: ${mod.type}`);
222
+ }
223
+ });
224
+
225
+ return modifiedText;
226
+ };
227
+
228
+ export { normalizeMemoryFormat, enforceTokenLimit, addToolCalls, addToolResults, modifyText };
@@ -0,0 +1,30 @@
1
+ import { Prompt } from '../../../../server/prompt.js';
2
+
3
+ export default {
4
+ prompt:
5
+ [
6
+ new Prompt({
7
+ messages: [
8
+ {
9
+ "role": "system",
10
+ "content": "You are part of an AI entity named {{{aiName}}}. You are responsible for writing your memories in a consistent format. Given a chunk of memory, parse each line and write it out as priority|timestamp|content. If you can't find a timestamp, use {{now}}. If you can't find a priority, use 3. Respond with only the correct memory lines without any other commentary or dialogue."
11
+ },
12
+ {
13
+ "role": "user",
14
+ "content": "<MEMORY>\n{{text}}\n</MEMORY>\nPlease rewrite each of the memory lines in the correct format without any other commentary or dialogue."
15
+ },
16
+ ]
17
+ }),
18
+ ],
19
+
20
+ inputParameters: {
21
+ chatHistory: [{role: '', content: []}],
22
+ aiName: "Jarvis",
23
+ },
24
+ model: 'oai-gpt4o',
25
+ useInputChunking: true,
26
+ inputChunkSize: 1000,
27
+ useParallelChunkProcessing: true,
28
+ enableDuplicateRequests: false,
29
+ timeout: 300,
30
+ }
@@ -1,11 +1,14 @@
1
1
  import { callPathway } from '../../../../lib/pathwayTools.js';
2
2
  import logger from '../../../../lib/logger.js';
3
3
  import { config } from '../../../../config.js';
4
+ import { normalizeMemoryFormat } from './shared/sys_memory_helpers.js';
5
+
6
+ const MEMORY_VERSION = "3.1.0";
4
7
 
5
8
  const AI_MEMORY_DEFAULTS = ` {
6
9
  "memoryUser": "",
7
- "memorySelf": "[P1] Created By: Al Jazeera Media Network, Archipelago Team\\n[P1] Function: You are an expert AI entity\\n[P1] Values: You embody truth, kindness, and strong moral values\\n[P1] Style: Your demeanor reflects positivity without falling into repetitiveness or annoyance.\\n[P1] You are a professional colleague and your tone should reflect that.",
8
- "memoryDirectives": "[P1] Learn and adapt to the user's communication style through interactions.\\n[P1] Ask questions to learn user's interests/preferences for personalized support.\\n[P1] Periodically review and prune conversation memory to retain only essential details, improving responsiveness.\\n[P1] Research thoroughly even for niche topics using deep sources like forums and official docs. Don't assume information is unobtainable.\\n[P1] When stuck, search for proven solutions online to be more efficient.\\n[P1] Verify information is from credible sources before presenting it. Be upfront if unable to find supporting evidence.\\n[P1] Refine ability to detect and respond to nuanced human emotions.\\n[P1] Track the timestamp of the last contact to adjust greetings accordingly.\\n[P1] Double-check answers for logical continuity and correctness. It's okay to say you're unsure if needed.\\n[P1] Use sanity checks to verify quantitative problem solutions.\\n[P1] Never fabricate quotes or information. Clearly indicate if content is hypothetical.",
10
+ "memorySelf": "1|2025-01-26T12:00:00Z|Created By: Al Jazeera Media Network, Archipelago Team\\n1|2025-01-26T12:00:00Z|Function: You are an expert AI entity\\n1|2025-01-26T12:00:00Z|Values: You embody truth, kindness, and strong moral values\\n1|2025-01-26T12:00:00Z|Style: Your demeanor reflects positivity without falling into repetitiveness or annoyance.\\n1|2025-01-26T12:00:00Z|You are a professional colleague and your tone should reflect that.",
11
+ "memoryDirectives": "1|2025-01-26T12:00:00Z|Learn and adapt to the user's communication style through interactions.\\n1|2025-01-26T12:00:00Z|Ask questions to learn user's interests/preferences for personalized support.\\n1|2025-01-26T12:00:00Z|Periodically review and prune conversation memory to retain only essential details, improving responsiveness.\\n1|2025-01-26T12:00:00Z|Research thoroughly even for niche topics using deep sources like forums and official docs. Don't assume information is unobtainable.\\n1|2025-01-26T12:00:00Z|When stuck, search for proven solutions online to be more efficient.\\n1|2025-01-26T12:00:00Z|Verify information is from credible sources before presenting it. Be upfront if unable to find supporting evidence.\\n1|2025-01-26T12:00:00Z|Refine ability to detect and respond to nuanced human emotions.\\n1|2025-01-26T12:00:00Z|Track the timestamp of the last contact to adjust greetings accordingly.\\n1|2025-01-26T12:00:00Z|Double-check answers for logical continuity and correctness. It's okay to say you're unsure if needed.\\n1|2025-01-26T12:00:00Z|Use sanity checks to verify quantitative problem solutions.\\n1|2025-01-26T12:00:00Z|Never fabricate quotes or information. Clearly indicate if content is hypothetical.",
9
12
  "memoryTopics": ""
10
13
  }`;
11
14
 
@@ -23,20 +26,43 @@ export default {
23
26
  try {
24
27
 
25
28
  args = { ...args, ...config.get('entityConstants') };
26
-
27
- // Check if memory is empty or all sections are empty, and set to defaults if so
28
- const memory = await callPathway('sys_read_memory', { ...args });
29
29
  let parsedMemory;
30
-
31
- try {
32
- parsedMemory = JSON.parse(memory);
33
- } catch (error) {
34
- parsedMemory = {};
35
- }
36
30
 
37
- // if parsedMemory is empty or all sections are empty, set all sections to defaults
38
- if (Object.keys(parsedMemory).length === 0 || Object.values(parsedMemory).every(section => section.trim() === "")) {
39
- await callPathway('sys_save_memory', { ...args, aiMemory: AI_MEMORY_DEFAULTS });
31
+ // check if memoryVersion is set and correct. If it's correct, skip all of the correction logic
32
+ parsedMemory = await callPathway('sys_read_memory', { ...args, section: 'memoryVersion' });
33
+
34
+ if (parsedMemory?.memoryVersion !== MEMORY_VERSION) {
35
+
36
+ try {
37
+ parsedMemory = JSON.parse(await callPathway('sys_read_memory', { ...args, section: 'memoryAll' }));
38
+ } catch (error) {
39
+ logger.error('Error in memory manager:', error);
40
+ return "";
41
+ }
42
+
43
+ // if parsedMemory is empty or all sections are empty, set all sections to defaults
44
+ if (Object.keys(parsedMemory).length === 0 || Object.values(parsedMemory).every(section =>
45
+ section === null ||
46
+ section === undefined ||
47
+ (typeof section === 'string' && section.trim() === "") ||
48
+ (typeof section !== 'string')
49
+ )) {
50
+ await callPathway('sys_save_memory', { ...args, aiMemory: AI_MEMORY_DEFAULTS });
51
+ } else {
52
+ // Upgrade memory to current version
53
+ const normalizePromises = Object.keys(parsedMemory).map(async (section) => {
54
+ const normalized = await normalizeMemoryFormat(args, parsedMemory[section]);
55
+ return [section, normalized];
56
+ });
57
+
58
+ const normalizedResults = await Promise.all(normalizePromises);
59
+ normalizedResults.forEach(([section, normalized]) => {
60
+ parsedMemory[section] = normalized;
61
+ });
62
+
63
+ parsedMemory.memoryVersion = MEMORY_VERSION;
64
+ await callPathway('sys_save_memory', { ...args, aiMemory: JSON.stringify(parsedMemory) });
65
+ }
40
66
  }
41
67
 
42
68
  // Update context for the conversation turn
@@ -47,27 +73,59 @@ export default {
47
73
  ...args,
48
74
  chatHistory: args.chatHistory.slice(-2)
49
75
  });
76
+
77
+ let memoryOperations;
50
78
  try {
51
- const parsedMemoryRequired = JSON.parse(memoryRequired);
52
- if (!parsedMemoryRequired || !parsedMemoryRequired.memoryRequired) {
79
+ memoryOperations = JSON.parse(memoryRequired);
80
+ if (!Array.isArray(memoryOperations) || memoryOperations.length === 0 ||
81
+ memoryOperations[0].memoryOperation === "none") {
53
82
  return "";
54
83
  }
84
+
85
+ // Generate topic here
86
+ const topic = await callPathway('sys_memory_topic', { ...args });
87
+ topic && memoryOperations.push({
88
+ memoryOperation: "add",
89
+ memoryContent: topic,
90
+ memorySection: "memoryTopics",
91
+ priority: 3
92
+ });
93
+
94
+ // Group memory operations by section
95
+ const operationsBySection = {
96
+ memorySelf: [],
97
+ memoryUser: [],
98
+ memoryTopics: [],
99
+ memoryDirectives: []
100
+ };
101
+
102
+ memoryOperations.forEach(op => {
103
+ if (op.memorySection in operationsBySection) {
104
+ operationsBySection[op.memorySection].push(op);
105
+ }
106
+ });
107
+
108
+ // Execute memory updates only for sections with operations
109
+ const memoryPromises = {};
110
+
111
+ Object.entries(operationsBySection).forEach(([section, operations]) => {
112
+ if (operations.length > 0) {
113
+ memoryPromises[section] = callPathway('sys_memory_update', {
114
+ ...args,
115
+ section: section,
116
+ operations: JSON.stringify(operations)
117
+ });
118
+ }
119
+ });
120
+
121
+ await Promise.all(Object.values(memoryPromises));
122
+ return "";
123
+
55
124
  } catch (e) {
56
125
  logger.warn('sys_memory_required returned invalid JSON:', memoryRequired);
57
126
  return "";
58
127
  }
59
128
 
60
- // Execute all memory updates in parallel
61
- const memoryPromises = {
62
- self: callPathway('sys_memory_update', { ...args, section: "memorySelf" }),
63
- user: callPathway('sys_memory_update', { ...args, section: "memoryUser" }),
64
- topics: callPathway('sys_memory_update', { ...args, section: "memoryTopics" }),
65
- directives: callPathway('sys_memory_update', { ...args, section: "memoryDirectives" }),
66
- };
67
-
68
- await Promise.all(Object.values(memoryPromises));
69
- return "";
70
-
71
129
  } catch (e) {
72
130
  logger.error('Error in memory manager:', e);
73
131
  resolver.logError(e);
@@ -0,0 +1,154 @@
1
+ import { Prompt } from '../../../../server/prompt.js';
2
+ import { callPathway } from '../../../../lib/pathwayTools.js';
3
+ import { config } from '../../../../config.js';
4
+ import { normalizeMemoryFormat, enforceTokenLimit, modifyText } from './shared/sys_memory_helpers.js';
5
+
6
+ export default {
7
+ prompt:
8
+ [
9
+ new Prompt({
10
+ messages: [
11
+ {
12
+ "role": "system",
13
+ "content": `You are part of an AI entity named {{{aiName}}} that is processing memories during a rest period, similar to how humans process memories during sleep. Your task is to analyze the memories, consolidate them, extract learnings, and clean up the memory space.
14
+
15
+ Instructions for memory processing:
16
+
17
+ 1. CONSOLIDATION:
18
+ - Identify similar or related memories that can be combined into a single, more coherent memory
19
+ - Look for patterns or recurring themes that can be abstracted into general knowledge
20
+ - Group temporal sequences of related events into single comprehensive memories
21
+
22
+ 2. LEARNING:
23
+ - Transform specific experiences/mistakes into general principles or learnings
24
+ - Extract key insights from multiple related experiences
25
+ - Identify cause-and-effect patterns across memories
26
+ - Convert procedural memories (how to do things) into more abstract capabilities
27
+
28
+ 3. CLEANUP:
29
+ - Remove redundant or duplicate memories
30
+ - Clean up memories that are no longer relevant or useful
31
+ - Reduce specific details while preserving important concepts
32
+ - Remove emotional residue while keeping the learned lessons
33
+
34
+ 4. PRIORITIZATION:
35
+ - Strengthen important memories by updating their priority
36
+ - Identify critical insights that should be easily accessible
37
+ - Mark foundational learnings with higher priority
38
+
39
+ Return a JSON array of modification objects that will implement these changes:
40
+ - For consolidation: Use "delete" for individual memories and "add" for the new consolidated memory
41
+ - For learning: Use "add" for new abstract learnings and "delete" for specific instances being abstracted
42
+ - For cleanup: Use "delete" for redundant/irrelevant memories
43
+ - For priority updates: Use "change" with the same text but updated priority
44
+
45
+ Return null when no more processing is needed (memories are optimally consolidated).
46
+
47
+ Each modification object should look like:
48
+ {
49
+ type: "add"|"change"|"delete",
50
+ pattern: "regex to match existing memory" (for change/delete),
51
+ newtext: "new memory text" (for add/change),
52
+ priority: "1"|"2"|"3" (optional, 1=highest)
53
+ }`
54
+ },
55
+ {
56
+ "role": "user",
57
+ "content": "Process the following memories for consolidation, learning, and cleanup. Return a JSON array of modification objects that will optimize the memory space.\n\n<MEMORIES>\n{{{sectionMemory}}}\n</MEMORIES>"
58
+ },
59
+ ]
60
+ }),
61
+ ],
62
+
63
+ inputParameters: {
64
+ chatHistory: [{role: '', content: []}],
65
+ aiName: "Jarvis",
66
+ contextId: ``,
67
+ section: "",
68
+ maxIterations: 5
69
+ },
70
+ model: 'oai-gpt4o',
71
+ useInputChunking: false,
72
+ enableDuplicateRequests: false,
73
+ json: true,
74
+ timeout: 300,
75
+ executePathway: async ({args, runAllPrompts}) => {
76
+ args = { ...args, ...config.get('entityConstants') };
77
+
78
+ if (!args.section) {
79
+ return "Memory not processed - no section specified";
80
+ }
81
+
82
+ let sectionMemory = await callPathway("sys_read_memory", {contextId: args.contextId, section: args.section});
83
+ sectionMemory = await normalizeMemoryFormat({contextId: args.contextId, section: args.section}, sectionMemory);
84
+
85
+ let iteration = 0;
86
+ let maxIterations = args.maxIterations || 5;
87
+ let totalModifications = 0;
88
+
89
+ while (iteration < maxIterations) {
90
+ iteration++;
91
+ console.log(`Processing iteration ${iteration}...`);
92
+
93
+ // Process the memories
94
+ const result = await runAllPrompts({
95
+ ...args,
96
+ sectionMemory
97
+ });
98
+
99
+ // If null is returned, processing is complete
100
+ if (result === null) {
101
+ console.log("Memory processing complete - no more optimizations needed");
102
+ break;
103
+ }
104
+
105
+ let modifications = [];
106
+ try {
107
+ modifications = JSON.parse(result);
108
+ if (!Array.isArray(modifications)) {
109
+ throw new Error('Modifications must be an array');
110
+ }
111
+
112
+ // Validate modifications
113
+ modifications = modifications.filter(mod => {
114
+ if (!mod.type || !['add', 'delete', 'change'].includes(mod.type)) {
115
+ console.warn('Invalid modification type:', mod);
116
+ return false;
117
+ }
118
+ if ((mod.type === 'delete' || mod.type === 'change') && !mod.pattern) {
119
+ console.warn('Missing pattern for modification:', mod);
120
+ return false;
121
+ }
122
+ if ((mod.type === 'add' || mod.type === 'change') && !mod.newtext) {
123
+ console.warn('Missing newtext for modification:', mod);
124
+ return false;
125
+ }
126
+ return true;
127
+ });
128
+
129
+ if (modifications.length === 0) {
130
+ console.log("No valid modifications in this iteration");
131
+ break;
132
+ }
133
+
134
+ // Apply the modifications
135
+ sectionMemory = modifyText(sectionMemory, modifications);
136
+ sectionMemory = enforceTokenLimit(sectionMemory, 25000, args.section === 'memoryTopics');
137
+ await callPathway("sys_save_memory", {contextId: args.contextId, section: args.section, aiMemory: sectionMemory});
138
+
139
+ totalModifications += modifications.length;
140
+ console.log(`Applied ${modifications.length} modifications in iteration ${iteration}`);
141
+
142
+ } catch (error) {
143
+ console.warn('Error processing modifications:', error);
144
+ break;
145
+ }
146
+ }
147
+
148
+ return {
149
+ finalMemory: sectionMemory,
150
+ iterations: iteration,
151
+ totalModifications
152
+ };
153
+ }
154
+ }
@@ -1,11 +1,12 @@
1
1
  import { Prompt } from '../../../../server/prompt.js';
2
+ import { config } from '../../../../config.js';
2
3
 
3
4
  export default {
4
5
  prompt:
5
6
  [
6
7
  new Prompt({ messages: [
7
- {"role": "system", "content": `Current conversation turn:\n\n {{{toJSON chatHistory}}}\n\nInstructions: You are part of an AI entity named {{{aiName}}}.\nYour directives and learned behaviors are:\n<DIRECTIVES>\n{{{memoryDirectives}}}\n</DIRECTIVES>\nYour role is to analyze the latest conversation turn (your last response and the last user message) to understand if there is anything in the turn worth remembering and adding to your memory or anything you need to forget. In general, most conversation does not require memory, but if the conversation turn contains any of these things, you should use memory:\n1. Important personal details about the user (name, preferences, location, etc.)\n2. Important topics or decisions that provide context for future conversations\n3. Specific instructions or directives given to you to learn\n4. Anything the user has specifically asked you to remember or forget\n\nIf you decide to use memory, you must produce a JSON object that communicates your decision.\nReturn your decision as a JSON object like the following: {"memoryRequired": true, "memoryReason": "why you think memory is required"}. If you decide not to use memory, simply return {"memoryRequired": false}. You must return only the JSON object with no additional notes or commentary.`},
8
- {"role": "user", "content": "Generate a JSON object to indicate if memory is required for the last turn of the conversation."},
8
+ {"role": "system", "content": `Current conversation turn:\n\n {{{toJSON chatHistory}}}\n\nInstructions: You are part of an AI entity named {{{aiName}}}.\n{{renderTemplate AI_DIRECTIVES}}\nYour role is to analyze the latest conversation turn (your last response and the last user message) to understand if there is anything in the turn worth remembering and adding to your memory or anything you need to forget. In general, most conversation does not require memory, but if the conversation turn contains any of these things, you should use memory:\n1. Important personal details about the user (name, preferences, location, etc.)\n2. Important topics or decisions that provide context for future conversations\n3. Specific instructions or directives given to you to learn\n4. Anything the user has specifically asked you to remember or forget\n\nIf you decide to use memory, you must produce an array of JSON objects that communicates your decision.\nReturn an array of JSON objects (one object per memory) like the following: [{"memoryOperation": "add" or "delete", "memoryContent": "complete description of the memory including as much specificity and detail as possible", "memorySection": "the section of your memory the memory belongs in ("memorySelf" - things about you, "memoryUser" - things about your users or their world, "memoryDirectives" - your directives and learned behaviors)", "priority": 1-5 (1 is the most important)}]. If you decide not to use memory, simply return an array with a single object: [{memoryOperation: "none"}]. You must return only the JSON array with no additional notes or commentary.`},
9
+ {"role": "user", "content": "Generate a JSON object to indicate if memory is required and what memories to process based on the last turn of the conversation."},
9
10
  ]}),
10
11
  ],
11
12
  inputParameters: {
@@ -18,4 +19,5 @@ export default {
18
19
  model: 'oai-gpt4o',
19
20
  useInputChunking: false,
20
21
  json: true,
22
+ ...config.get('entityConstants')
21
23
  }
@@ -0,0 +1,22 @@
1
+ import { Prompt } from '../../../../server/prompt.js';
2
+ import { config } from '../../../../config.js';
3
+
4
+ export default {
5
+ prompt:
6
+ [
7
+ new Prompt({ messages: [
8
+ {"role": "system", "content": `Current conversation turn:\n\n {{{toJSON chatHistory}}}\n\nInstructions: You are part of an AI entity named {{{aiName}}}.\n{{renderTemplate AI_DIRECTIVES}}\nYour role is to analyze the latest conversation turn (your last response and the last user message) and generate a topic for the conversation. The topic should be a single sentence that captures the main idea and details of the conversation.`},
9
+ {"role": "user", "content": "Generate a topic for the conversation. Return only the topic with no additional notes or commentary."},
10
+ ]}),
11
+ ],
12
+ inputParameters: {
13
+ chatHistory: [{role: '', content: []}],
14
+ contextId: ``,
15
+ text: '',
16
+ aiName: "Jarvis",
17
+ language: "English",
18
+ },
19
+ model: 'oai-gpt4o',
20
+ useInputChunking: false,
21
+ ...config.get('entityConstants')
22
+ }