@aj-archipelago/cortex 1.4.24 → 1.4.26

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-archipelago/cortex",
3
- "version": "1.4.24",
3
+ "version": "1.4.26",
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": {
@@ -26,6 +26,10 @@ export default {
26
26
  timeout: 300,
27
27
  executePathway: async ({args, resolver}) => {
28
28
  try {
29
+ // Skip if memory is disabled
30
+ if (args.useMemory === false) {
31
+ return "";
32
+ }
29
33
 
30
34
  args = { ...args, ...config.get('entityConstants') };
31
35
  let parsedMemory;
@@ -60,6 +60,7 @@ export default {
60
60
  useInputChunking: false,
61
61
  enableDuplicateRequests: false,
62
62
  useSingleTokenStream: false,
63
+ manageTokenLength: false, // Agentic models handle context management themselves
63
64
  inputParameters: {
64
65
  privateData: false,
65
66
  chatHistory: [{role: '', content: []}],
@@ -452,7 +453,11 @@ export default {
452
453
 
453
454
  const entityConfig = loadEntityConfig(entityId);
454
455
  const { entityTools, entityToolsOpenAiFormat } = getToolsForEntity(entityConfig);
455
- const { useMemory: entityUseMemory = true, name: entityName, instructions: entityInstructions } = entityConfig || {};
456
+ const { name: entityName, instructions: entityInstructions } = entityConfig || {};
457
+
458
+ // Determine useMemory: entityConfig.useMemory === false is a hard disable (entity can't use memory)
459
+ // Otherwise args.useMemory can disable it, default true
460
+ args.useMemory = entityConfig?.useMemory === false ? false : (args.useMemory ?? true);
456
461
 
457
462
  // Initialize chat history if needed
458
463
  if (!args.chatHistory || args.chatHistory.length === 0) {
@@ -488,7 +493,7 @@ export default {
488
493
 
489
494
  // Kick off the memory lookup required pathway in parallel - this takes like 500ms so we want to start it early
490
495
  let memoryLookupRequiredPromise = null;
491
- if (entityUseMemory) {
496
+ if (args.useMemory) {
492
497
  const chatHistoryLastTurn = args.chatHistory.slice(-2);
493
498
  const chatHistorySizeOk = (JSON.stringify(chatHistoryLastTurn).length < 5000);
494
499
  if (chatHistorySizeOk) {
@@ -512,7 +517,6 @@ export default {
512
517
  entityId,
513
518
  entityTools,
514
519
  entityToolsOpenAiFormat,
515
- entityUseMemory,
516
520
  entityInstructions,
517
521
  voiceResponse,
518
522
  aiMemorySelfModify,
@@ -524,7 +528,7 @@ export default {
524
528
 
525
529
  const promptPrefix = '';
526
530
 
527
- const memoryTemplates = entityUseMemory ?
531
+ const memoryTemplates = args.useMemory ?
528
532
  `{{renderTemplate AI_MEMORY_INSTRUCTIONS}}\n\n{{renderTemplate AI_MEMORY}}\n\n{{renderTemplate AI_MEMORY_CONTEXT}}\n\n` : '';
529
533
 
530
534
  const instructionTemplates = entityInstructions ? (entityInstructions + '\n\n') : `{{renderTemplate AI_COMMON_INSTRUCTIONS}}\n\n{{renderTemplate AI_EXPERTISE}}\n\n`;
@@ -560,7 +564,7 @@ export default {
560
564
  const truncatedChatHistory = resolver.modelExecutor.plugin.truncateMessagesToTargetLength(args.chatHistory, null, 1000);
561
565
 
562
566
  // Asynchronously manage memory for this context
563
- if (args.aiMemorySelfModify && entityUseMemory) {
567
+ if (args.aiMemorySelfModify && args.useMemory) {
564
568
  callPathway('sys_memory_manager', { ...args, chatHistory: truncatedChatHistory, stream: false })
565
569
  .catch(error => logger.error(error?.message || "Error in sys_memory_manager pathway"));
566
570
  }
@@ -52,8 +52,7 @@ export default {
52
52
 
53
53
  executePathway: async ({args, runAllPrompts, resolver}) => {
54
54
  // Check if memory is enabled for this entity
55
- const useMemory = args.entityUseMemory !== undefined ? args.entityUseMemory : args.useMemory;
56
- if (useMemory === false) {
55
+ if (args.useMemory === false) {
57
56
  return JSON.stringify({
58
57
  error: 'Memory storage is disabled for this entity. Cannot store memories when useMemory is false.'
59
58
  });
@@ -404,38 +404,45 @@ class PathwayResolver {
404
404
  }
405
405
 
406
406
  // Get saved context from contextId or change contextId if needed
407
- const { contextId } = args;
407
+ const { contextId, useMemory } = args;
408
408
  this.savedContextId = contextId ? contextId : uuidv4();
409
409
 
410
+ // Check if memory is enabled (default true for backward compatibility)
411
+ const memoryEnabled = useMemory !== false;
412
+
410
413
  const loadMemory = async () => {
411
414
  try {
412
- // Load saved context and core memory if it exists
413
- const [savedContext, memorySelf, memoryDirectives, memoryTopics, memoryUser, memoryContext] = await Promise.all([
414
- (getvWithDoubleDecryption && await getvWithDoubleDecryption(this.savedContextId, this.args?.contextKey)) || {},
415
- callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memorySelf', priority: 1, stripMetadata: true, contextKey: this.args?.contextKey }),
416
- callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryDirectives', priority: 1, stripMetadata: true, contextKey: this.args?.contextKey }),
417
- callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryTopics', priority: 0, numResults: 10, contextKey: this.args?.contextKey }),
418
- callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryUser', priority: 1, stripMetadata: true, contextKey: this.args?.contextKey }),
419
- callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryContext', priority: 0, contextKey: this.args?.contextKey }),
420
- ]).catch(error => {
421
- this.logError(`Failed to load memory: ${error.message}`);
422
- return [{},'','','','',''];
423
- });
415
+ // Always load savedContext (legacy feature)
416
+ this.savedContext = (getvWithDoubleDecryption && await getvWithDoubleDecryption(this.savedContextId, this.args?.contextKey)) || {};
417
+ this.initialState = { savedContext: this.savedContext };
418
+
419
+ // Only load memory* sections if memory is enabled
420
+ if (memoryEnabled) {
421
+ const [memorySelf, memoryDirectives, memoryTopics, memoryUser, memoryContext] = await Promise.all([
422
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memorySelf', priority: 1, stripMetadata: true, contextKey: this.args?.contextKey }),
423
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryDirectives', priority: 1, stripMetadata: true, contextKey: this.args?.contextKey }),
424
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryTopics', priority: 0, numResults: 10, contextKey: this.args?.contextKey }),
425
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryUser', priority: 1, stripMetadata: true, contextKey: this.args?.contextKey }),
426
+ callPathway('sys_read_memory', { contextId: this.savedContextId, section: 'memoryContext', priority: 0, contextKey: this.args?.contextKey }),
427
+ ]).catch(error => {
428
+ this.logError(`Failed to load memory: ${error.message}`);
429
+ return ['','','','',''];
430
+ });
424
431
 
425
- this.savedContext = savedContext;
426
- this.memorySelf = memorySelf || '';
427
- this.memoryDirectives = memoryDirectives || '';
428
- this.memoryTopics = memoryTopics || '';
429
- this.memoryUser = memoryUser || '';
430
- this.memoryContext = memoryContext || '';
431
-
432
- // Store initial state for comparison
433
- this.initialState = {
434
- savedContext: this.savedContext,
435
- };
432
+ this.memorySelf = memorySelf || '';
433
+ this.memoryDirectives = memoryDirectives || '';
434
+ this.memoryTopics = memoryTopics || '';
435
+ this.memoryUser = memoryUser || '';
436
+ this.memoryContext = memoryContext || '';
437
+ } else {
438
+ this.memorySelf = '';
439
+ this.memoryDirectives = '';
440
+ this.memoryTopics = '';
441
+ this.memoryUser = '';
442
+ this.memoryContext = '';
443
+ }
436
444
  } catch (error) {
437
445
  this.logError(`Error in loadMemory: ${error.message}`);
438
- // Set default values in case of error
439
446
  this.savedContext = {};
440
447
  this.memorySelf = '';
441
448
  this.memoryDirectives = '';
@@ -447,6 +454,7 @@ class PathwayResolver {
447
454
  };
448
455
 
449
456
  const saveChangedMemory = async () => {
457
+ // Always save savedContext (legacy feature, not governed by useMemory)
450
458
  this.savedContextId = this.savedContextId || uuidv4();
451
459
 
452
460
  const currentState = {
@@ -576,9 +576,16 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
576
576
  let totalLength = 0;
577
577
  let totalUnits;
578
578
  messages.forEach((message, index) => {
579
- const content = Array.isArray(message.content)
580
- ? message.content.map((item) => JSON.stringify(sanitizeBase64(item))).join(", ")
581
- : message.content;
579
+ let content;
580
+ if (Array.isArray(message.content)) {
581
+ // Only stringify objects, not strings (which may already be JSON strings)
582
+ content = message.content.map((item) => {
583
+ const sanitized = sanitizeBase64(item);
584
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
585
+ }).join(", ");
586
+ } else {
587
+ content = message.content;
588
+ }
582
589
  const { length, units } = this.getLength(content);
583
590
  const preview = this.shortenContent(content);
584
591
 
@@ -593,9 +600,15 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
593
600
  logger.info(`[chat request contained ${totalLength} ${totalUnits}]`);
594
601
  } else {
595
602
  const message = messages[0];
596
- const content = Array.isArray(message.content)
597
- ? message.content.map((item) => JSON.stringify(item)).join(", ")
598
- : message.content;
603
+ let content;
604
+ if (Array.isArray(message.content)) {
605
+ // Only stringify objects, not strings (which may already be JSON strings)
606
+ content = message.content.map((item) => {
607
+ return typeof item === 'string' ? item : JSON.stringify(item);
608
+ }).join(", ");
609
+ } else {
610
+ content = message.content;
611
+ }
599
612
  const { length, units } = this.getLength(content);
600
613
  logger.info(`[request sent containing ${length} ${units}]`);
601
614
  logger.verbose(`${this.shortenContent(content)}`);
@@ -474,14 +474,19 @@ class Claude4VertexPlugin extends Claude3VertexPlugin {
474
474
  let totalLength = 0;
475
475
  let totalUnits;
476
476
  messages.forEach((message, index) => {
477
- const content = Array.isArray(message.content)
478
- ? message.content.map((item) => {
479
- if (item.type === 'document') {
480
- return `{type: document, source: ${JSON.stringify(sanitizeBase64(item.source))}}`;
481
- }
482
- return JSON.stringify(sanitizeBase64(item));
483
- }).join(", ")
484
- : message.content;
477
+ let content;
478
+ if (Array.isArray(message.content)) {
479
+ // Only stringify objects, not strings (which may already be JSON strings)
480
+ content = message.content.map((item) => {
481
+ if (item.type === 'document') {
482
+ return `{type: document, source: ${JSON.stringify(sanitizeBase64(item.source))}}`;
483
+ }
484
+ const sanitized = sanitizeBase64(item);
485
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
486
+ }).join(", ");
487
+ } else {
488
+ content = message.content;
489
+ }
485
490
  const { length, units } = this.getLength(content);
486
491
  const preview = this.shortenContent(content);
487
492
 
@@ -496,14 +501,19 @@ class Claude4VertexPlugin extends Claude3VertexPlugin {
496
501
  logger.info(`[chat request contained ${totalLength} ${totalUnits}]`);
497
502
  } else {
498
503
  const message = messages[0];
499
- const content = Array.isArray(message.content)
500
- ? message.content.map((item) => {
501
- if (item.type === 'document') {
502
- return `{type: document, source: ${JSON.stringify(sanitizeBase64(item.source))}}`;
503
- }
504
- return JSON.stringify(sanitizeBase64(item));
505
- }).join(", ")
506
- : message.content;
504
+ let content;
505
+ if (Array.isArray(message.content)) {
506
+ // Only stringify objects, not strings (which may already be JSON strings)
507
+ content = message.content.map((item) => {
508
+ if (item.type === 'document') {
509
+ return `{type: document, source: ${JSON.stringify(sanitizeBase64(item.source))}}`;
510
+ }
511
+ const sanitized = sanitizeBase64(item);
512
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
513
+ }).join(", ");
514
+ } else {
515
+ content = message.content;
516
+ }
507
517
  const { length, units } = this.getLength(content);
508
518
  logger.info(`[request sent containing ${length} ${units}]`);
509
519
  logger.verbose(`${this.shortenContent(content)}`);
@@ -37,7 +37,18 @@ class GrokResponsesPlugin extends OpenAIVisionPlugin {
37
37
  let totalLength = 0;
38
38
  let totalUnits;
39
39
  messages.forEach((message, index) => {
40
- const content = message.content === undefined ? JSON.stringify(sanitizeBase64(message)) : (Array.isArray(message.content) ? message.content.map(item => JSON.stringify(sanitizeBase64(item))).join(', ') : message.content);
40
+ let content;
41
+ if (message.content === undefined) {
42
+ content = JSON.stringify(sanitizeBase64(message));
43
+ } else if (Array.isArray(message.content)) {
44
+ // Only stringify objects, not strings (which may already be JSON strings)
45
+ content = message.content.map(item => {
46
+ const sanitized = sanitizeBase64(item);
47
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
48
+ }).join(', ');
49
+ } else {
50
+ content = message.content;
51
+ }
41
52
  const { length, units } = this.getLength(content);
42
53
  const displayContent = this.shortenContent(content);
43
54
 
@@ -54,7 +65,16 @@ class GrokResponsesPlugin extends OpenAIVisionPlugin {
54
65
  logger.info(`[grok responses request contained ${totalLength} ${totalUnits}]`);
55
66
  } else if (messages && messages.length === 1) {
56
67
  const message = messages[0];
57
- const content = Array.isArray(message.content) ? message.content.map(item => JSON.stringify(sanitizeBase64(item))).join(', ') : message.content;
68
+ let content;
69
+ if (Array.isArray(message.content)) {
70
+ // Only stringify objects, not strings (which may already be JSON strings)
71
+ content = message.content.map(item => {
72
+ const sanitized = sanitizeBase64(item);
73
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
74
+ }).join(', ');
75
+ } else {
76
+ content = message.content;
77
+ }
58
78
  const { length, units } = this.getLength(content);
59
79
  logger.info(`[grok responses request sent containing ${length} ${units}]`);
60
80
  logger.verbose(`${this.shortenContent(content)}`);
@@ -29,7 +29,18 @@ class GrokVisionPlugin extends OpenAIVisionPlugin {
29
29
  let totalUnits;
30
30
  messages.forEach((message, index) => {
31
31
  //message.content string or array
32
- const content = message.content === undefined ? JSON.stringify(sanitizeBase64(message)) : (Array.isArray(message.content) ? message.content.map(item => JSON.stringify(sanitizeBase64(item))).join(', ') : message.content);
32
+ let content;
33
+ if (message.content === undefined) {
34
+ content = JSON.stringify(sanitizeBase64(message));
35
+ } else if (Array.isArray(message.content)) {
36
+ // Only stringify objects, not strings (which may already be JSON strings)
37
+ content = message.content.map(item => {
38
+ const sanitized = sanitizeBase64(item);
39
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
40
+ }).join(', ');
41
+ } else {
42
+ content = message.content;
43
+ }
33
44
  const { length, units } = this.getLength(content);
34
45
  const displayContent = this.shortenContent(content);
35
46
 
@@ -47,7 +58,16 @@ class GrokVisionPlugin extends OpenAIVisionPlugin {
47
58
  logger.info(`[grok request contained ${totalLength} ${totalUnits}]`);
48
59
  } else {
49
60
  const message = messages[0];
50
- const content = Array.isArray(message.content) ? message.content.map(item => JSON.stringify(sanitizeBase64(item))).join(', ') : message.content;
61
+ let content;
62
+ if (Array.isArray(message.content)) {
63
+ // Only stringify objects, not strings (which may already be JSON strings)
64
+ content = message.content.map(item => {
65
+ const sanitized = sanitizeBase64(item);
66
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
67
+ }).join(', ');
68
+ } else {
69
+ content = message.content;
70
+ }
51
71
  const { length, units } = this.getLength(content);
52
72
  logger.info(`[grok request sent containing ${length} ${units}]`);
53
73
  logger.verbose(`${this.shortenContent(content)}`);
@@ -159,7 +159,18 @@ class OpenAIVisionPlugin extends OpenAIChatPlugin {
159
159
  let totalUnits;
160
160
  messages.forEach((message, index) => {
161
161
  //message.content string or array
162
- const content = message.content === undefined ? JSON.stringify(sanitizeBase64(message)) : (Array.isArray(message.content) ? message.content.map(item => JSON.stringify(sanitizeBase64(item))).join(', ') : message.content);
162
+ let content;
163
+ if (message.content === undefined) {
164
+ content = JSON.stringify(sanitizeBase64(message));
165
+ } else if (Array.isArray(message.content)) {
166
+ // Only stringify objects, not strings (which may already be JSON strings)
167
+ content = message.content.map(item => {
168
+ const sanitized = sanitizeBase64(item);
169
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
170
+ }).join(', ');
171
+ } else {
172
+ content = message.content;
173
+ }
163
174
  const { length, units } = this.getLength(content);
164
175
  const displayContent = this.shortenContent(content);
165
176
 
@@ -177,7 +188,16 @@ class OpenAIVisionPlugin extends OpenAIChatPlugin {
177
188
  logger.info(`[chat request contained ${totalLength} ${totalUnits}]`);
178
189
  } else {
179
190
  const message = messages[0];
180
- const content = Array.isArray(message.content) ? message.content.map(item => JSON.stringify(sanitizeBase64(item))).join(', ') : message.content;
191
+ let content;
192
+ if (Array.isArray(message.content)) {
193
+ // Only stringify objects, not strings (which may already be JSON strings)
194
+ content = message.content.map(item => {
195
+ const sanitized = sanitizeBase64(item);
196
+ return typeof sanitized === 'string' ? sanitized : JSON.stringify(sanitized);
197
+ }).join(', ');
198
+ } else {
199
+ content = message.content;
200
+ }
181
201
  const { length, units } = this.getLength(content);
182
202
  logger.info(`[request sent containing ${length} ${units}]`);
183
203
  logger.verbose(`${this.shortenContent(content)}`);