@aj-archipelago/cortex 1.3.5 → 1.3.7

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 (94) hide show
  1. package/helper-apps/cortex-autogen/agents.py +31 -2
  2. package/helper-apps/cortex-realtime-voice-server/.env.sample +6 -0
  3. package/helper-apps/cortex-realtime-voice-server/README.md +22 -0
  4. package/helper-apps/cortex-realtime-voice-server/bun.lockb +0 -0
  5. package/helper-apps/cortex-realtime-voice-server/client/bun.lockb +0 -0
  6. package/helper-apps/cortex-realtime-voice-server/client/index.html +12 -0
  7. package/helper-apps/cortex-realtime-voice-server/client/package.json +65 -0
  8. package/helper-apps/cortex-realtime-voice-server/client/postcss.config.js +6 -0
  9. package/helper-apps/cortex-realtime-voice-server/client/public/favicon.ico +0 -0
  10. package/helper-apps/cortex-realtime-voice-server/client/public/index.html +43 -0
  11. package/helper-apps/cortex-realtime-voice-server/client/public/logo192.png +0 -0
  12. package/helper-apps/cortex-realtime-voice-server/client/public/logo512.png +0 -0
  13. package/helper-apps/cortex-realtime-voice-server/client/public/manifest.json +25 -0
  14. package/helper-apps/cortex-realtime-voice-server/client/public/robots.txt +3 -0
  15. package/helper-apps/cortex-realtime-voice-server/client/public/sounds/connect.mp3 +0 -0
  16. package/helper-apps/cortex-realtime-voice-server/client/public/sounds/disconnect.mp3 +0 -0
  17. package/helper-apps/cortex-realtime-voice-server/client/src/App.test.tsx +9 -0
  18. package/helper-apps/cortex-realtime-voice-server/client/src/App.tsx +126 -0
  19. package/helper-apps/cortex-realtime-voice-server/client/src/SettingsModal.tsx +207 -0
  20. package/helper-apps/cortex-realtime-voice-server/client/src/chat/Chat.tsx +553 -0
  21. package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubble.tsx +22 -0
  22. package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubbleLeft.tsx +22 -0
  23. package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatBubbleRight.tsx +21 -0
  24. package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatMessage.tsx +27 -0
  25. package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatMessageInput.tsx +74 -0
  26. package/helper-apps/cortex-realtime-voice-server/client/src/chat/ChatTile.tsx +211 -0
  27. package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/SoundEffects.ts +56 -0
  28. package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavPacker.ts +112 -0
  29. package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavRecorder.ts +571 -0
  30. package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/WavStreamPlayer.ts +290 -0
  31. package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/analysis/AudioAnalysis.ts +186 -0
  32. package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/analysis/constants.ts +59 -0
  33. package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/worklets/AudioProcessor.ts +214 -0
  34. package/helper-apps/cortex-realtime-voice-server/client/src/chat/audio/worklets/StreamProcessor.ts +183 -0
  35. package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/AudioVisualizer.tsx +151 -0
  36. package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/CopyButton.tsx +32 -0
  37. package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/ImageOverlay.tsx +166 -0
  38. package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/MicrophoneVisualizer.tsx +95 -0
  39. package/helper-apps/cortex-realtime-voice-server/client/src/chat/components/ScreenshotCapture.tsx +116 -0
  40. package/helper-apps/cortex-realtime-voice-server/client/src/chat/hooks/useWindowResize.ts +27 -0
  41. package/helper-apps/cortex-realtime-voice-server/client/src/chat/utils/audio.ts +33 -0
  42. package/helper-apps/cortex-realtime-voice-server/client/src/index.css +20 -0
  43. package/helper-apps/cortex-realtime-voice-server/client/src/index.tsx +19 -0
  44. package/helper-apps/cortex-realtime-voice-server/client/src/logo.svg +1 -0
  45. package/helper-apps/cortex-realtime-voice-server/client/src/react-app-env.d.ts +1 -0
  46. package/helper-apps/cortex-realtime-voice-server/client/src/reportWebVitals.ts +15 -0
  47. package/helper-apps/cortex-realtime-voice-server/client/src/setupTests.ts +5 -0
  48. package/helper-apps/cortex-realtime-voice-server/client/src/utils/logger.ts +45 -0
  49. package/helper-apps/cortex-realtime-voice-server/client/tailwind.config.js +14 -0
  50. package/helper-apps/cortex-realtime-voice-server/client/tsconfig.json +30 -0
  51. package/helper-apps/cortex-realtime-voice-server/client/vite.config.ts +22 -0
  52. package/helper-apps/cortex-realtime-voice-server/index.ts +19 -0
  53. package/helper-apps/cortex-realtime-voice-server/package.json +28 -0
  54. package/helper-apps/cortex-realtime-voice-server/src/ApiServer.ts +35 -0
  55. package/helper-apps/cortex-realtime-voice-server/src/SocketServer.ts +737 -0
  56. package/helper-apps/cortex-realtime-voice-server/src/Tools.ts +520 -0
  57. package/helper-apps/cortex-realtime-voice-server/src/cortex/expert.ts +29 -0
  58. package/helper-apps/cortex-realtime-voice-server/src/cortex/image.ts +29 -0
  59. package/helper-apps/cortex-realtime-voice-server/src/cortex/memory.ts +91 -0
  60. package/helper-apps/cortex-realtime-voice-server/src/cortex/reason.ts +29 -0
  61. package/helper-apps/cortex-realtime-voice-server/src/cortex/search.ts +30 -0
  62. package/helper-apps/cortex-realtime-voice-server/src/cortex/style.ts +31 -0
  63. package/helper-apps/cortex-realtime-voice-server/src/cortex/utils.ts +95 -0
  64. package/helper-apps/cortex-realtime-voice-server/src/cortex/vision.ts +34 -0
  65. package/helper-apps/cortex-realtime-voice-server/src/realtime/client.ts +499 -0
  66. package/helper-apps/cortex-realtime-voice-server/src/realtime/realtimeTypes.ts +279 -0
  67. package/helper-apps/cortex-realtime-voice-server/src/realtime/socket.ts +27 -0
  68. package/helper-apps/cortex-realtime-voice-server/src/realtime/transcription.ts +75 -0
  69. package/helper-apps/cortex-realtime-voice-server/src/realtime/utils.ts +33 -0
  70. package/helper-apps/cortex-realtime-voice-server/src/utils/logger.ts +45 -0
  71. package/helper-apps/cortex-realtime-voice-server/src/utils/prompt.ts +81 -0
  72. package/helper-apps/cortex-realtime-voice-server/tsconfig.json +28 -0
  73. package/package.json +1 -1
  74. package/pathways/basePathway.js +3 -1
  75. package/pathways/system/entity/memory/sys_memory_manager.js +3 -0
  76. package/pathways/system/entity/memory/sys_memory_update.js +44 -45
  77. package/pathways/system/entity/memory/sys_read_memory.js +86 -6
  78. package/pathways/system/entity/memory/sys_search_memory.js +66 -0
  79. package/pathways/system/entity/shared/sys_entity_constants.js +2 -2
  80. package/pathways/system/entity/sys_entity_continue.js +2 -1
  81. package/pathways/system/entity/sys_entity_start.js +10 -0
  82. package/pathways/system/entity/sys_generator_expert.js +0 -2
  83. package/pathways/system/entity/sys_generator_memory.js +31 -0
  84. package/pathways/system/entity/sys_generator_voice_sample.js +36 -0
  85. package/pathways/system/entity/sys_router_tool.js +13 -10
  86. package/pathways/system/sys_parse_numbered_object_list.js +1 -1
  87. package/server/pathwayResolver.js +41 -31
  88. package/server/plugins/azureVideoTranslatePlugin.js +28 -16
  89. package/server/plugins/claude3VertexPlugin.js +0 -9
  90. package/server/plugins/gemini15ChatPlugin.js +18 -5
  91. package/server/plugins/modelPlugin.js +27 -6
  92. package/server/plugins/openAiChatPlugin.js +10 -8
  93. package/server/plugins/openAiVisionPlugin.js +56 -0
  94. package/tests/memoryfunction.test.js +73 -1
@@ -223,21 +223,42 @@ class PathwayResolver {
223
223
  this.savedContextId = contextId ? contextId : uuidv4();
224
224
 
225
225
  const loadMemory = async () => {
226
- // Load initial values
227
- this.savedContext = (getv && await getv(contextId)) || {};
228
- this.memorySelf = (getv && await getv(`${contextId}-memorySelf`)) || "";
229
- this.memoryDirectives = (getv && await getv(`${contextId}-memoryDirectives`)) || "";
230
- this.memoryTopics = (getv && await getv(`${contextId}-memoryTopics`)) || "";
231
- this.memoryUser = (getv && await getv(`${contextId}-memoryUser`)) || "";
232
-
233
- // Store initial state for comparison
234
- this.initialState = {
235
- savedContext: this.savedContext,
236
- memorySelf: this.memorySelf,
237
- memoryDirectives: this.memoryDirectives,
238
- memoryTopics: this.memoryTopics,
239
- memoryUser: this.memoryUser
240
- };
226
+ try {
227
+ // Load saved context and core memory if it exists
228
+ const [savedContext, memorySelf, memoryDirectives, memoryTopics, memoryUser, memoryContext] = await Promise.all([
229
+ (getv && await getv(this.savedContextId)) || {},
230
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memorySelf', priority: 1}),
231
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryDirectives', priority: 1 }),
232
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryTopics', priority: 0, numResults: 10 }),
233
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryUser', priority: 1 }),
234
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryContext', priority: 0 }),
235
+ ]).catch(error => {
236
+ this.logError(`Failed to load memory: ${error.message}`);
237
+ return [{},'','','','',''];
238
+ });
239
+
240
+ this.savedContext = savedContext;
241
+ this.memorySelf = memorySelf || '';
242
+ this.memoryDirectives = memoryDirectives || '';
243
+ this.memoryTopics = memoryTopics || '';
244
+ this.memoryUser = memoryUser || '';
245
+ this.memoryContext = memoryContext || '';
246
+
247
+ // Store initial state for comparison
248
+ this.initialState = {
249
+ savedContext: this.savedContext,
250
+ };
251
+ } catch (error) {
252
+ this.logError(`Error in loadMemory: ${error.message}`);
253
+ // Set default values in case of error
254
+ this.savedContext = {};
255
+ this.memorySelf = '';
256
+ this.memoryDirectives = '';
257
+ this.memoryTopics = '';
258
+ this.memoryUser = '';
259
+ this.memoryContext = '';
260
+ this.initialState = { savedContext: {} };
261
+ }
241
262
  };
242
263
 
243
264
  const saveChangedMemory = async () => {
@@ -245,27 +266,11 @@ class PathwayResolver {
245
266
 
246
267
  const currentState = {
247
268
  savedContext: this.savedContext,
248
- memorySelf: this.memorySelf,
249
- memoryDirectives: this.memoryDirectives,
250
- memoryTopics: this.memoryTopics,
251
- memoryUser: this.memoryUser
252
269
  };
253
270
 
254
271
  if (currentState.savedContext !== this.initialState.savedContext) {
255
272
  setv && await setv(this.savedContextId, this.savedContext);
256
273
  }
257
- if (currentState.memorySelf !== this.initialState.memorySelf) {
258
- setv && await setv(`${this.savedContextId}-memorySelf`, this.memorySelf);
259
- }
260
- if (currentState.memoryDirectives !== this.initialState.memoryDirectives) {
261
- setv && await setv(`${this.savedContextId}-memoryDirectives`, this.memoryDirectives);
262
- }
263
- if (currentState.memoryTopics !== this.initialState.memoryTopics) {
264
- setv && await setv(`${this.savedContextId}-memoryTopics`, this.memoryTopics);
265
- }
266
- if (currentState.memoryUser !== this.initialState.memoryUser) {
267
- setv && await setv(`${this.savedContextId}-memoryUser`, this.memoryUser);
268
- }
269
274
  };
270
275
 
271
276
  const MAX_RETRIES = 3;
@@ -344,6 +349,11 @@ class PathwayResolver {
344
349
 
345
350
  // Calculate the maximum token length for a chunk
346
351
  getChunkMaxTokenLength() {
352
+ // Skip expensive calculations if not using input chunking
353
+ if (!this.useInputChunking) {
354
+ return this.modelExecutor.plugin.getModelMaxTokenLength();
355
+ }
356
+
347
357
  // find the longest prompt
348
358
  const maxPromptTokenLength = Math.max(...this.prompts.map((promptData) => this.modelExecutor.plugin.getCompiledPrompt('', this.args, promptData).tokenLength));
349
359
 
@@ -7,13 +7,13 @@ import { config } from "../../config.js";
7
7
 
8
8
  function isValidJSON(str) {
9
9
  try {
10
- JSON.parse(str);
11
- return true;
10
+ JSON.parse(str);
11
+ return true;
12
12
  } catch (e) {
13
- return false;
13
+ return false;
14
14
  }
15
15
  }
16
-
16
+
17
17
  class AzureVideoTranslatePlugin extends ModelPlugin {
18
18
  constructor(pathway, model) {
19
19
  super(pathway, model);
@@ -21,8 +21,6 @@ class AzureVideoTranslatePlugin extends ModelPlugin {
21
21
  this.eventSource = null;
22
22
  this.jsonBuffer = '';
23
23
  this.jsonDepth = 0;
24
- this.currentStep = 0;
25
- this.totalNumOfSteps = 30;
26
24
  }
27
25
 
28
26
  getRequestParameters(_, parameters, __) {
@@ -77,11 +75,23 @@ class AzureVideoTranslatePlugin extends ModelPlugin {
77
75
  this.jsonDepth += (data.match(/{/g) || []).length - (data.match(/}/g) || []).length;
78
76
 
79
77
  if (this.jsonDepth === 0 && this.jsonBuffer.trim()) {
80
- console.log(this.jsonBuffer);
78
+ logger.debug(this.jsonBuffer);
81
79
  if (this.jsonBuffer.includes('Failed to run with exception')) {
82
80
  this.cleanup();
83
81
  throw new Error(this.jsonBuffer);
84
82
  }
83
+
84
+ if (isValidJSON(this.jsonBuffer)) {
85
+ const parsedData = JSON.parse(this.jsonBuffer);
86
+ if (parsedData.progress !== undefined) {
87
+ publishRequestProgress({
88
+ requestId: this.requestId,
89
+ progress: parsedData.progress,
90
+ info: this.jsonBuffer
91
+ });
92
+ }
93
+ }
94
+
85
95
  onData(this.jsonBuffer);
86
96
  this.jsonBuffer = '';
87
97
  this.jsonDepth = 0;
@@ -106,21 +116,23 @@ class AzureVideoTranslatePlugin extends ModelPlugin {
106
116
 
107
117
  return new Promise((resolve, reject) => {
108
118
  let finalJson = '';
109
- this.handleStream(response.data,
110
- (data) => {
111
- this.currentStep++;
119
+ this.handleStream(response.data,
120
+ (data) => {
112
121
  publishRequestProgress({
113
122
  requestId: this.requestId,
114
- progress: this.currentStep / this.totalNumOfSteps,
115
- // data: this.jsonBuffer,
116
123
  info: data
117
124
  });
118
- if (isValidJSON(data)) {
119
- finalJson = data;
120
- }
125
+ logger.debug('Data:', data);
126
+ // Extract JSON content if message contains targetLocales
127
+ const jsonMatch = data.match(/{[\s\S]*"targetLocales"[\s\S]*}/);
128
+ if (jsonMatch) {
129
+ const extractedJson = jsonMatch[0];
130
+ if (isValidJSON(extractedJson)) {
131
+ finalJson = extractedJson;
132
+ }
133
+ }
121
134
  },
122
135
  () => {
123
- // console.log('Full data:', fullData);
124
136
  resolve(finalJson)
125
137
  },
126
138
  (error) => reject(error)
@@ -309,15 +309,6 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
309
309
  return requestProgress;
310
310
  }
311
311
 
312
- shortenContent(content, maxWords = 40) {
313
- const words = content.split(" ");
314
- if (words.length <= maxWords || logger.level === 'debug') {
315
- return content;
316
- }
317
- return words.slice(0, maxWords / 2).join(" ") +
318
- " ... " +
319
- words.slice(-maxWords / 2).join(" ");
320
- }
321
312
  }
322
313
 
323
314
  export default Claude3VertexPlugin;
@@ -179,24 +179,37 @@ class Gemini15ChatPlugin extends ModelPlugin {
179
179
  const messageContent = message.parts.reduce((acc, part) => {
180
180
  if (part.text) {
181
181
  return acc + part.text;
182
+ } else if (part.inlineData) {
183
+ return acc + '* base64 data truncated for log *';
184
+ } else if (part.fileData) {
185
+ return acc + part.fileData.fileUri;
182
186
  }
183
187
  return acc;
184
188
  } , '');
185
- const words = messageContent.split(" ");
186
189
  const { length, units } = this.getLength(messageContent);
187
- const preview = words.length < 41 ? messageContent : words.slice(0, 20).join(" ") + " ... " + words.slice(-20).join(" ");
190
+ const preview = this.shortenContent(messageContent);
188
191
 
189
192
  logger.verbose(`message ${index + 1}: role: ${message.role}, ${units}: ${length}, content: "${preview}"`);
190
193
  });
191
194
  } else if (messages && messages.length === 1) {
192
- logger.verbose(`${messages[0].parts[0].text}`);
195
+ const messageContent = messages[0].parts.reduce((acc, part) => {
196
+ if (part.text) {
197
+ return acc + part.text;
198
+ } else if (part.inlineData) {
199
+ return acc + '* base64 data truncated for log *';
200
+ } else if (part.fileData) {
201
+ return acc + part.fileData.fileUri;
202
+ }
203
+ return acc;
204
+ } , '');
205
+ logger.verbose(`${this.shortenContent(messageContent)}`);
193
206
  }
194
207
 
195
208
  // check if responseData is an array or string
196
209
  if (typeof responseData === 'string') {
197
210
  const { length, units } = this.getLength(responseData);
198
211
  logger.info(`[response received containing ${length} ${units}]`);
199
- logger.verbose(`${responseData}`);
212
+ logger.verbose(`${this.shortenContent(responseData)}`);
200
213
  } else if (Array.isArray(responseData)) {
201
214
  const { mergedResult, safetyRatings } = mergeResults(responseData);
202
215
  if (safetyRatings?.length) {
@@ -205,7 +218,7 @@ class Gemini15ChatPlugin extends ModelPlugin {
205
218
  }
206
219
  const { length, units } = this.getLength(mergedResult);
207
220
  logger.info(`[response received containing ${length} ${units}]`);
208
- logger.verbose(`${mergedResult}`);
221
+ logger.verbose(`${this.shortenContent(mergedResult)}`);
209
222
  } else {
210
223
  logger.info(`[response received as an SSE stream]`);
211
224
  }
@@ -36,11 +36,19 @@ class ModelPlugin {
36
36
  this.requestCount = 0;
37
37
  }
38
38
 
39
+ safeGetEncodedLength(data) {
40
+ if (data && data.length > 100000) {
41
+ return data.length * 3 / 16;
42
+ } else {
43
+ return encode(data).length;
44
+ }
45
+ }
46
+
39
47
  truncateMessagesToTargetLength(messages, targetTokenLength) {
40
48
  // Calculate the token length of each message
41
49
  const tokenLengths = messages.map((message) => ({
42
50
  message,
43
- tokenLength: encode(this.messagesToChatML([message], false)).length,
51
+ tokenLength: this.safeGetEncodedLength(this.messagesToChatML([message], false)),
44
52
  }));
45
53
 
46
54
  // Calculate the total token length of all messages
@@ -89,7 +97,7 @@ class ModelPlugin {
89
97
 
90
98
  tokenLengths[index] = {
91
99
  message: truncatedMessage,
92
- tokenLength: encode(this.messagesToChatML([ truncatedMessage ], false)).length
100
+ tokenLength: this.safeGetEncodedLength(this.messagesToChatML([ truncatedMessage ], false))
93
101
  }
94
102
 
95
103
  // calculate the length again to keep us honest
@@ -141,9 +149,9 @@ class ModelPlugin {
141
149
  const modelPromptMessagesML = this.messagesToChatML(modelPromptMessages);
142
150
 
143
151
  if (modelPromptMessagesML) {
144
- return { modelPromptMessages, tokenLength: encode(modelPromptMessagesML).length, modelPrompt };
152
+ return { modelPromptMessages, tokenLength: this.safeGetEncodedLength(modelPromptMessagesML), modelPrompt };
145
153
  } else {
146
- return { modelPromptText, tokenLength: encode(modelPromptText).length, modelPrompt };
154
+ return { modelPromptText, tokenLength: this.safeGetEncodedLength(modelPromptText), modelPrompt };
147
155
  }
148
156
  }
149
157
 
@@ -260,19 +268,32 @@ class ModelPlugin {
260
268
  return {length, units};
261
269
  }
262
270
 
271
+ shortenContent(content, maxWords = 40) {
272
+ if (!content || typeof content !== 'string') {
273
+ return content;
274
+ }
275
+ const words = content.split(" ");
276
+ if (words.length <= maxWords || logger.level === 'debug') {
277
+ return content;
278
+ }
279
+ return words.slice(0, maxWords / 2).join(" ") +
280
+ " ... " +
281
+ words.slice(-maxWords / 2).join(" ");
282
+ }
283
+
263
284
  logRequestData(data, responseData, prompt) {
264
285
  const modelInput = data.prompt || (data.messages && data.messages[0].content) || (data.length > 0 && data[0].Text) || null;
265
286
 
266
287
  if (modelInput) {
267
288
  const { length, units } = this.getLength(modelInput);
268
289
  logger.info(`[request sent containing ${length} ${units}]`);
269
- logger.verbose(`${modelInput}`);
290
+ logger.verbose(`${this.shortenContent(modelInput)}`);
270
291
  }
271
292
 
272
293
  const responseText = JSON.stringify(responseData);
273
294
  const { length, units } = this.getLength(responseText);
274
295
  logger.info(`[response received containing ${length} ${units}]`);
275
- logger.verbose(`${responseText}`);
296
+ logger.verbose(`${this.shortenContent(responseText)}`);
276
297
 
277
298
  prompt && prompt.debugInfo && (prompt.debugInfo += `\n${JSON.stringify(data)}`);
278
299
  }
@@ -61,7 +61,7 @@ class OpenAIChatPlugin extends ModelPlugin {
61
61
  }
62
62
 
63
63
  // Check if the token length exceeds the model's max token length
64
- if (tokenLength > modelTargetTokenLength) {
64
+ if (tokenLength > modelTargetTokenLength && this.promptParameters?.manageTokenLength) {
65
65
  // Remove older messages until the token length is within the model's limit
66
66
  requestMessages = this.truncateMessagesToTargetLength(requestMessages, modelTargetTokenLength);
67
67
  }
@@ -112,11 +112,11 @@ class OpenAIChatPlugin extends ModelPlugin {
112
112
  let totalUnits;
113
113
  messages.forEach((message, index) => {
114
114
  //message.content string or array
115
- const content = message.content === undefined ? JSON.stringify(message) : (Array.isArray(message.content) ? message.content.map(item => JSON.stringify(item)).join(', ') : message.content);
116
- const words = content.split(" ");
115
+ const content = message.content === undefined ? JSON.stringify(message) : (Array.isArray(message.content) ? message.content.map(item => {
116
+ return JSON.stringify(item);
117
+ }).join(', ') : message.content);
117
118
  const { length, units } = this.getLength(content);
118
-
119
- const displayContent = logger.level === 'debug' ? content : (words.length < 41 ? content : words.slice(0, 20).join(" ") + " ... " + words.slice(-20).join(" "));
119
+ const displayContent = this.shortenContent(content);
120
120
 
121
121
  logger.verbose(`message ${index + 1}: role: ${message.role}, ${units}: ${length}, content: "${displayContent}"`);
122
122
  totalLength += length;
@@ -125,10 +125,12 @@ class OpenAIChatPlugin extends ModelPlugin {
125
125
  logger.info(`[chat request contained ${totalLength} ${totalUnits}]`);
126
126
  } else {
127
127
  const message = messages[0];
128
- const content = Array.isArray(message.content) ? message.content.map(item => JSON.stringify(item)).join(', ') : message.content;
128
+ const content = Array.isArray(message.content) ? message.content.map(item => {
129
+ return JSON.stringify(item);
130
+ }).join(', ') : message.content;
129
131
  const { length, units } = this.getLength(content);
130
132
  logger.info(`[request sent containing ${length} ${units}]`);
131
- logger.verbose(`${content}`);
133
+ logger.verbose(`${this.shortenContent(content)}`);
132
134
  }
133
135
 
134
136
  if (stream) {
@@ -137,7 +139,7 @@ class OpenAIChatPlugin extends ModelPlugin {
137
139
  const responseText = this.parseResponse(responseData);
138
140
  const { length, units } = this.getLength(responseText);
139
141
  logger.info(`[response received containing ${length} ${units}]`);
140
- logger.verbose(`${responseText}`);
142
+ logger.verbose(`${this.shortenContent(responseText)}`);
141
143
  }
142
144
 
143
145
  prompt && prompt.debugInfo && (prompt.debugInfo += `\n${JSON.stringify(data)}`);
@@ -1,4 +1,5 @@
1
1
  import OpenAIChatPlugin from './openAiChatPlugin.js';
2
+ import logger from '../../lib/logger.js';
2
3
 
3
4
  function safeJsonParse(content) {
4
5
  try {
@@ -44,6 +45,61 @@ class OpenAIVisionPlugin extends OpenAIChatPlugin {
44
45
  });
45
46
  }
46
47
 
48
+ // Override the logging function to display the messages and responses
49
+ logRequestData(data, responseData, prompt) {
50
+ const { stream, messages } = data;
51
+ if (messages && messages.length > 1) {
52
+ logger.info(`[chat request sent containing ${messages.length} messages]`);
53
+ let totalLength = 0;
54
+ let totalUnits;
55
+ messages.forEach((message, index) => {
56
+ //message.content string or array
57
+ const content = message.content === undefined ? JSON.stringify(message) : (Array.isArray(message.content) ? message.content.map(item => {
58
+ if (item.type === 'image_url' && item.image_url?.url?.startsWith('data:')) {
59
+ return JSON.stringify({
60
+ type: 'image_url',
61
+ image_url: { url: '* base64 data truncated for log *' }
62
+ });
63
+ }
64
+ return JSON.stringify(item);
65
+ }).join(', ') : message.content);
66
+ const { length, units } = this.getLength(content);
67
+ const displayContent = this.shortenContent(content);
68
+
69
+ logger.verbose(`message ${index + 1}: role: ${message.role}, ${units}: ${length}, content: "${displayContent}"`);
70
+ totalLength += length;
71
+ totalUnits = units;
72
+ });
73
+ logger.info(`[chat request contained ${totalLength} ${totalUnits}]`);
74
+ } else {
75
+ const message = messages[0];
76
+ const content = Array.isArray(message.content) ? message.content.map(item => {
77
+ if (item.type === 'image_url' && item.image_url?.url?.startsWith('data:')) {
78
+ return JSON.stringify({
79
+ type: 'image_url',
80
+ image_url: { url: '* base64 data truncated for log *' }
81
+ });
82
+ }
83
+ return JSON.stringify(item);
84
+ }).join(', ') : message.content;
85
+ const { length, units } = this.getLength(content);
86
+ logger.info(`[request sent containing ${length} ${units}]`);
87
+ logger.verbose(`${this.shortenContent(content)}`);
88
+ }
89
+
90
+ if (stream) {
91
+ logger.info(`[response received as an SSE stream]`);
92
+ } else {
93
+ const responseText = this.parseResponse(responseData);
94
+ const { length, units } = this.getLength(responseText);
95
+ logger.info(`[response received containing ${length} ${units}]`);
96
+ logger.verbose(`${this.shortenContent(responseText)}`);
97
+ }
98
+
99
+ prompt && prompt.debugInfo && (prompt.debugInfo += `\n${JSON.stringify(data)}`);
100
+ }
101
+
102
+
47
103
  getRequestParameters(text, parameters, prompt) {
48
104
  const requestParameters = super.getRequestParameters(text, parameters, prompt);
49
105
 
@@ -1,5 +1,5 @@
1
1
  import test from 'ava';
2
- import { enforceTokenLimit } from '../pathways/system/entity/memory/sys_memory_update.js';
2
+ import { enforceTokenLimit, modifyText } from '../pathways/system/entity/memory/sys_memory_update.js';
3
3
 
4
4
  test('enforceTokenLimit preserves priority order correctly', t => {
5
5
  const input = `
@@ -61,4 +61,76 @@ Item without priority
61
61
 
62
62
  t.true(result.includes('[P3] Item without priority'));
63
63
  t.true(result.includes('[P1] Item with priority'));
64
+ });
65
+
66
+ test('modifyText handles delete operations with escaped characters', t => {
67
+ const input = '[P2] Pizza Connection: Has special appreciation for Pisanello\'s';
68
+ const modifications = [{
69
+ type: 'delete',
70
+ pattern: '\\[P2\\] Pizza Connection: Has special appreciation for Pisanello\'s'
71
+ }];
72
+
73
+ const result = modifyText(input, modifications);
74
+ t.false(result.includes('Pizza Connection'));
75
+ });
76
+
77
+ test('modifyText handles delete with partial priority match', t => {
78
+ const input = '[P3] Test memory item';
79
+ const modifications = [{
80
+ type: 'delete',
81
+ pattern: 'Test memory item'
82
+ }];
83
+
84
+ const result = modifyText(input, modifications);
85
+ t.false(result.includes('Test memory item'));
86
+ });
87
+
88
+ test('modifyText handles multiple modifications in sequence', t => {
89
+ const input = '[P2] Keep this line\n[P3] Delete this line\n[P1] Also keep this';
90
+ const modifications = [
91
+ { type: 'delete', pattern: 'Delete this line' },
92
+ { type: 'add', newtext: 'New line added', priority: '2' }
93
+ ];
94
+
95
+ const result = modifyText(input, modifications);
96
+ t.true(result.includes('Keep this line'));
97
+ t.true(result.includes('Also keep this'));
98
+ t.true(result.includes('[P2] New line added'));
99
+ t.false(result.includes('Delete this line'));
100
+ });
101
+
102
+ test('modifyText handles delete with whitespace variations', t => {
103
+ const input = ' [P2] Item with spaces \n[P3]Item without spaces';
104
+ const modifications = [
105
+ { type: 'delete', pattern: 'Item with spaces' },
106
+ { type: 'delete', pattern: 'Item without spaces' }
107
+ ];
108
+
109
+ const result = modifyText(input, modifications);
110
+ t.false(result.includes('Item with spaces'));
111
+ t.false(result.includes('Item without spaces'));
112
+ });
113
+
114
+ test('modifyText preserves existing priority when adding with priority in text', t => {
115
+ const input = '';
116
+ const modifications = [{
117
+ type: 'add',
118
+ newtext: '[P1] High priority item',
119
+ priority: '3' // This should be ignored since priority is in text
120
+ }];
121
+
122
+ const result = modifyText(input, modifications);
123
+ t.true(result.includes('[P1] High priority item'));
124
+ t.false(result.includes('[P3] [P1] High priority item'));
125
+ });
126
+
127
+ test('modifyText handles delete with regex special characters', t => {
128
+ const input = '[P2] Special (chars) [test] {here} *star*';
129
+ const modifications = [{
130
+ type: 'delete',
131
+ pattern: 'Special \\(chars\\) \\[test\\] \\{here\\} \\*star\\*'
132
+ }];
133
+
134
+ const result = modifyText(input, modifications);
135
+ t.false(result.includes('Special (chars)'));
64
136
  });