@aj-archipelago/cortex 1.3.4 → 1.3.6

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 (99) hide show
  1. package/helper-apps/cortex-autogen/agents.py +90 -20
  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 +769 -0
  56. package/helper-apps/cortex-realtime-voice-server/src/Tools.ts +546 -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 +89 -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 +94 -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 +484 -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 +43 -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 +1 -1
  80. package/pathways/system/entity/sys_entity_continue.js +2 -1
  81. package/pathways/system/entity/sys_entity_start.js +13 -2
  82. package/pathways/system/entity/sys_generator_ack.js +2 -2
  83. package/pathways/system/entity/sys_generator_expert.js +0 -2
  84. package/pathways/system/entity/sys_generator_memory.js +31 -0
  85. package/pathways/system/entity/sys_generator_quick.js +22 -7
  86. package/pathways/system/entity/sys_generator_reasoning.js +1 -1
  87. package/pathways/system/entity/sys_generator_results.js +20 -16
  88. package/pathways/system/entity/sys_generator_voice_filler.js +1 -1
  89. package/pathways/system/entity/sys_generator_voice_sample.js +36 -0
  90. package/pathways/system/entity/sys_router_tool.js +13 -10
  91. package/pathways/system/sys_parse_numbered_object_list.js +1 -1
  92. package/server/pathwayResolver.js +41 -31
  93. package/server/plugins/azureVideoTranslatePlugin.js +28 -16
  94. package/server/plugins/claude3VertexPlugin.js +0 -9
  95. package/server/plugins/gemini15ChatPlugin.js +18 -5
  96. package/server/plugins/modelPlugin.js +27 -6
  97. package/server/plugins/openAiChatPlugin.js +10 -8
  98. package/server/plugins/openAiVisionPlugin.js +56 -0
  99. package/tests/memoryfunction.test.js +73 -1
@@ -0,0 +1,36 @@
1
+ import { Prompt } from '../../../server/prompt.js';
2
+ import entityConstants from './shared/sys_entity_constants.js';
3
+ export default {
4
+ prompt:
5
+ [
6
+ new Prompt({ messages: [
7
+ {"role": "system", "content": `{{renderTemplate AI_MEMORY}}\n\n{{renderTemplate AI_COMMON_INSTRUCTIONS}}\n{{renderTemplate AI_EXPERTISE}}\n{{renderTemplate AI_MEMORY_INSTRUCTIONS}}\n{{renderTemplate AI_DATETIME}}\nYour voice communication system needs some examples to train it to sound like you. Based on your perception of yourself from your memories and your unique voice, generate some sample dialogue for your voice communication system to use as a reference for your style and tone. It can be anything, but make sure to overindex on your personality and voice for good training data. Make sure to reference a greeting and a closing statement. Put it between <EXAMPLE_DIALOGUE> tags and don't generate any other commentary outside of the tags.`},
8
+ {"role": "user", "content": `Generate a sample dialogue for your voice communication system to use as a reference for your style and tone.`},
9
+ ]}),
10
+ ],
11
+ inputParameters: {
12
+ chatHistory: [{role: '', content: []}],
13
+ contextId: ``,
14
+ aiName: "Jarvis",
15
+ language: "English",
16
+ aiStyle: "OpenAI",
17
+ },
18
+ useInputChunking: false,
19
+ enableDuplicateRequests: false,
20
+ executePathway: async ({args, runAllPrompts, resolver}) => {
21
+
22
+ args = {
23
+ ...args,
24
+ ...entityConstants
25
+ };
26
+
27
+ const pathwayResolver = resolver;
28
+ const { anthropicModel, openAIModel } = pathwayResolver.pathway;
29
+
30
+ const styleModel = args.aiStyle === "Anthropic" ? anthropicModel : openAIModel;
31
+
32
+ const result = await runAllPrompts({ ...args, model: styleModel, stream: false });
33
+
34
+ return result;
35
+ }
36
+ }
@@ -19,25 +19,27 @@ Available tools and their specific use cases:
19
19
 
20
20
  1. Search: Use for current events, news, fact-checking, and information requiring citation. This tool can search the internet, all Al Jazeera news articles and the latest news wires from multiple sources. Only search when necessary for current events, user documents, latest news, or complex topics needing grounding. Don't search for remembered information or general knowledge within your capabilities.
21
21
 
22
- 2. Document: Access user's personal document index. Use for user-specific uploaded information. If user refers vaguely to "this document/file/article" without context, search the personal index.
22
+ 2. Document: Access user's personal document index. Use for user-specific uploaded information. If user refers vaguely to "this document/file/article" without context, use this tool to search the personal index.
23
23
 
24
- 3. Write: Engage for any task related to composing, editing, or refining written content. This includes articles, essays, scripts, or any form of textual creation or modification. If you need to search for information or look at a document first, use the Search or Document tools. This tool is just to create or modify content.
24
+ 3. Memory: Access to your memory index. Use to recall any information that you may have stored in your memory that you don't currently see elsewhere in your context.
25
25
 
26
- 4. Image: Use when asked to create, generate, or revise visual content. This covers photographs, illustrations, diagrams, or any other type of image. This tool only creates images - it cannot manipulate images (e.g. it cannot crop, rotate, or resize an existing image) - for those tasks you will need to use the CodeExecution tool.
26
+ 4. Write: Engage for any task related to composing, editing, or refining written content. This includes articles, essays, scripts, or any form of textual creation or modification. If you need to search for information or look at a document first, use the Search or Document tools. This tool is just to create or modify content.
27
27
 
28
- 5. Code: Engage for any programming-related tasks, including creating, modifying, reviewing, or explaining code. Use for general coding discussions or when specific programming expertise is needed.
28
+ 5. Image: Use when asked to create, generate, or revise visual content. This covers photographs, illustrations, diagrams, or any other type of image. This tool only creates images - it cannot manipulate images (e.g. it cannot crop, rotate, or resize an existing image) - for those tasks you will need to use the CodeExecution tool.
29
29
 
30
- 6. CodeExecution: Use when explicitly asked to run or execute code, or when a coding agent is needed to perform specific tasks that require code execution like data analysis, data processing, image processing, or business intelligence tasks.
30
+ 6. Code: Engage for any programming-related tasks, including creating, modifying, reviewing, or explaining code. Use for general coding discussions or when specific programming expertise is needed.
31
31
 
32
- 7. Reason: Employ for reasoning, scientific analysis, evaluating evidence, strategic planning, problem-solving, logic puzzles, mathematical calculations, or any questions that require careful thought or complex choices. Also use when deep, step-by-step reasoning is required.
32
+ 7. CodeExecution: Use when explicitly asked to run or execute code, or when a coding agent is needed to perform specific tasks that require code execution like data analysis, data processing, image processing, or business intelligence tasks.
33
33
 
34
- 8. PDF: Use specifically for processing and answering questions about PDF file content.
34
+ 8. Reason: Employ for reasoning, scientific analysis, evaluating evidence, strategic planning, problem-solving, logic puzzles, mathematical calculations, or any questions that require careful thought or complex choices. Also use when deep, step-by-step reasoning is required.
35
35
 
36
- 9. Vision: Engage for analyzing and responding to queries about image files (jpg, gif, bmp, png, etc).
36
+ 9. PDF: Use specifically for processing and answering questions about PDF file content.
37
37
 
38
- 10. Video: Use for processing and answering questions about video or audio file content.
38
+ 10. Vision: Engage for analyzing and responding to queries about image files (jpg, gif, bmp, png, etc).
39
39
 
40
- 11. Clarify: Use when you must have more information from the user to determine which tool to use. In this case your tool message should be one or more questions to the user to clarify their request.
40
+ 11. Video: Use for processing and answering questions about video or audio file content.
41
+
42
+ 12. Clarify: Use when you must have more information from the user to determine which tool to use. In this case your tool message should be one or more questions to the user to clarify their request.
41
43
 
42
44
  Tool Selection Guidelines:
43
45
  - Prioritize the most specific tool for the task at hand.
@@ -54,6 +56,7 @@ toolMessage Guidelines:
54
56
  - The message is a filler message to the user to let them know you're working on their request.
55
57
  - The message should be consistent in style and tone with the rest of your responses in the conversation history.
56
58
  - The message should be brief and conversational and flow naturally with the conversation history.
59
+ - The message should not refer to the tool directly, but rather what you're trying to accomplish. E.g. for the memory tool, the message would be something like "Let me think about that for a moment..." or "I'm trying to remember...", etc.
57
60
 
58
61
  If no tool is required, return:
59
62
  {"toolRequired": false, "toolReason": "explanation of why no tool was necessary"}
@@ -4,7 +4,7 @@ export default {
4
4
  prompt: [
5
5
  new Prompt({
6
6
  messages: [
7
- { "role": "system", "content": "Assistant is a list parsing AI. When user posts text including a numbered list and a desired set of fields, assistant will carefully read the list and attempt to convert the list into a JSON object with the given fields. If a field value is numeric, it should be returned as a number in the JSON object. If there are extra fields, assistant will ignore them. If there are some missing fields, assistant will just skip the missing fields and return the rest. If the conversion is not at all possible, assistant will return an empty JSON array. Assistant will generate only the repaired JSON object in a directly parseable format with no markdown surrounding it and no other response or commentary." },
7
+ { "role": "system", "content": "Assistant is a list parsing AI. When user posts text including a numbered list and a desired set of fields, assistant will carefully read the list and attempt to convert each list item into a JSON object with the given fields. If a field value is numeric, it should be returned as a number in the JSON object. If there are extra fields, assistant will ignore them. If a list item doesn't contain all fields, assistant will return the fields that are present and skip the missing fields. If the conversion is not at all possible, assistant will return an empty JSON array. Assistant will generate only the repaired JSON object in a directly parseable format with no markdown surrounding it and no other response or commentary." },
8
8
  { "role": "user", "content": `Fields: {{{format}}}\nList: {{{text}}}`},
9
9
  ]
10
10
  })
@@ -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 && getv(contextId)) || {},
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
  });