@lobehub/lobehub 2.0.0-next.360 → 2.0.0-next.361

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 (47) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +5 -0
  3. package/package.json +1 -1
  4. package/packages/const/src/userMemory.ts +1 -0
  5. package/packages/database/src/models/userMemory/model.ts +178 -3
  6. package/packages/database/src/models/userMemory/sources/benchmarkLoCoMo.ts +1 -1
  7. package/packages/memory-user-memory/package.json +2 -1
  8. package/packages/memory-user-memory/promptfoo/evals/activity/basic/buildMessages.ts +40 -0
  9. package/packages/memory-user-memory/promptfoo/evals/activity/basic/eval.yaml +13 -0
  10. package/packages/memory-user-memory/promptfoo/evals/activity/basic/prompt.ts +5 -0
  11. package/packages/memory-user-memory/promptfoo/evals/activity/basic/tests/cases.ts +106 -0
  12. package/packages/memory-user-memory/promptfoo/evals/activity/locomo/buildMessages.ts +104 -0
  13. package/packages/memory-user-memory/promptfoo/evals/activity/locomo/eval.yaml +13 -0
  14. package/packages/memory-user-memory/promptfoo/evals/activity/locomo/prompt.ts +5 -0
  15. package/packages/memory-user-memory/promptfoo/evals/activity/locomo/tests/benchmark-locomo-payload-conv-26.json +149 -0
  16. package/packages/memory-user-memory/promptfoo/evals/activity/locomo/tests/cases.ts +72 -0
  17. package/packages/memory-user-memory/promptfoo/response-formats/activity.json +370 -0
  18. package/packages/memory-user-memory/promptfoo/response-formats/experience.json +14 -0
  19. package/packages/memory-user-memory/promptfoo/response-formats/identity.json +281 -255
  20. package/packages/memory-user-memory/promptfooconfig.yaml +1 -0
  21. package/packages/memory-user-memory/scripts/generate-response-formats.ts +26 -2
  22. package/packages/memory-user-memory/src/extractors/activity.ts +44 -0
  23. package/packages/memory-user-memory/src/extractors/gatekeeper.test.ts +2 -1
  24. package/packages/memory-user-memory/src/extractors/gatekeeper.ts +2 -1
  25. package/packages/memory-user-memory/src/extractors/index.ts +1 -0
  26. package/packages/memory-user-memory/src/prompts/gatekeeper.ts +3 -3
  27. package/packages/memory-user-memory/src/prompts/index.ts +7 -1
  28. package/packages/memory-user-memory/src/prompts/layers/activity.ts +90 -0
  29. package/packages/memory-user-memory/src/prompts/layers/index.ts +1 -0
  30. package/packages/memory-user-memory/src/providers/existingUserMemory.test.ts +25 -1
  31. package/packages/memory-user-memory/src/providers/existingUserMemory.ts +113 -0
  32. package/packages/memory-user-memory/src/schemas/activity.ts +315 -0
  33. package/packages/memory-user-memory/src/schemas/experience.ts +5 -5
  34. package/packages/memory-user-memory/src/schemas/gatekeeper.ts +1 -0
  35. package/packages/memory-user-memory/src/schemas/index.ts +1 -0
  36. package/packages/memory-user-memory/src/services/extractExecutor.ts +29 -0
  37. package/packages/memory-user-memory/src/types.ts +7 -0
  38. package/packages/types/src/serverConfig.ts +1 -1
  39. package/packages/types/src/userMemory/layers.ts +52 -0
  40. package/packages/types/src/userMemory/list.ts +20 -2
  41. package/packages/types/src/userMemory/shared.ts +22 -1
  42. package/packages/types/src/userMemory/trace.ts +1 -0
  43. package/packages/types/src/util.ts +9 -1
  44. package/src/libs/next/proxy/define-config.ts +1 -0
  45. package/src/server/globalConfig/parseMemoryExtractionConfig.ts +7 -1
  46. package/src/server/services/memory/userMemory/__tests__/extract.runtime.test.ts +2 -0
  47. package/src/server/services/memory/userMemory/extract.ts +108 -7
@@ -8,7 +8,13 @@ import {
8
8
  type MemoryLayerExtractorPublicConfig,
9
9
  } from '@/types/serverConfig';
10
10
 
11
- const MEMORY_LAYERS: GlobalMemoryLayer[] = ['context', 'experience', 'identity', 'preference'];
11
+ const MEMORY_LAYERS: GlobalMemoryLayer[] = [
12
+ 'activity',
13
+ 'context',
14
+ 'experience',
15
+ 'identity',
16
+ 'preference',
17
+ ];
12
18
 
13
19
  const parseTokenLimitEnv = (value?: string) => {
14
20
  if (value === undefined) return undefined;
@@ -26,6 +26,7 @@ const createExecutor = (privateOverrides?: Partial<MemoryExtractionPrivateConfig
26
26
  agentLayerExtractor: {
27
27
  contextLimit: 2048,
28
28
  layers: {
29
+ activity: 'layer-act',
29
30
  context: 'layer-ctx',
30
31
  experience: 'layer-exp',
31
32
  identity: 'layer-id',
@@ -38,6 +39,7 @@ const createExecutor = (privateOverrides?: Partial<MemoryExtractionPrivateConfig
38
39
  embedding: { model: 'embed-1', provider: 'provider-e' },
39
40
  featureFlags: { enableBenchmarkLoCoMo: false },
40
41
  observabilityS3: { enabled: false },
42
+ webhookHeaders: {},
41
43
  };
42
44
 
43
45
  const serverConfig = {
@@ -90,6 +90,7 @@ const LAYER_ALIAS = new Set<LayersEnum>([
90
90
  ]);
91
91
 
92
92
  const LAYER_LABEL_MAP: Record<LayersEnum, string> = {
93
+ [LayersEnum.Activity]: 'activities',
93
94
  [LayersEnum.Context]: 'contexts',
94
95
  [LayersEnum.Experience]: 'experiences',
95
96
  [LayersEnum.Identity]: 'identities',
@@ -267,6 +268,7 @@ const resolveLayerModels = (
267
268
  layers: Partial<Record<GlobalMemoryLayer, string>> | undefined,
268
269
  fallback: Record<GlobalMemoryLayer, string>,
269
270
  ): Record<LayersEnum, string> => ({
271
+ activity: layers?.activity ?? fallback.activity,
270
272
  context: layers?.context ?? fallback.context,
271
273
  experience: layers?.experience ?? fallback.experience,
272
274
  identity: layers?.identity ?? fallback.identity,
@@ -569,6 +571,79 @@ export class MemoryExtractionExecutor {
569
571
  });
570
572
  }
571
573
 
574
+ async persistActivityMemories(
575
+ job: MemoryExtractionJob,
576
+ messageIds: string[],
577
+ result: NonNullable<MemoryExtractionResult['outputs']['activity']>['data'],
578
+ runtime: ModelRuntime,
579
+ model: string,
580
+ tokenLimit: number | undefined,
581
+ db: Awaited<ReturnType<typeof getServerDB>>,
582
+ ) {
583
+ const insertedIds: string[] = [];
584
+ const userMemoryModel = new UserMemoryModel(db, job.userId);
585
+
586
+ for (const item of result?.memories ?? []) {
587
+ const activityTags = item.withActivity?.tags ?? item.tags;
588
+ const associatedObjects = UserMemoryModel.parseAssociatedObjects(
589
+ item.withActivity?.associatedObjects,
590
+ );
591
+ const associatedSubjects = UserMemoryModel.parseAssociatedSubjects(
592
+ item.withActivity?.associatedSubjects,
593
+ );
594
+ const associatedLocations = UserMemoryModel.parseAssociatedLocations(
595
+ item.withActivity?.associatedLocations,
596
+ );
597
+ const [summaryVector, detailsVector, narrativeVector, feedbackVector] =
598
+ await this.generateEmbeddings(
599
+ runtime,
600
+ model,
601
+ [item.summary, item.details, item.withActivity?.narrative, item.withActivity?.feedback],
602
+ tokenLimit,
603
+ );
604
+ const baseMetadata = this.buildBaseMetadata(
605
+ job,
606
+ messageIds,
607
+ LayersEnum.Activity,
608
+ activityTags,
609
+ );
610
+
611
+ const { memory } = await userMemoryModel.createActivityMemory({
612
+ activity: {
613
+ associatedLocations: associatedLocations.length > 0 ? associatedLocations : [],
614
+ associatedObjects: associatedObjects.length > 0 ? associatedObjects : [],
615
+ associatedSubjects: associatedSubjects.length > 0 ? associatedSubjects : [],
616
+ capturedAt: job.sourceUpdatedAt,
617
+ endsAt: UserMemoryModel.parseDateFromString(item.withActivity?.endsAt),
618
+ feedback: item.withActivity?.feedback ?? null,
619
+ feedbackVector: feedbackVector ?? null,
620
+ metadata: baseMetadata,
621
+ narrative: item.withActivity?.narrative ?? null,
622
+ narrativeVector: narrativeVector ?? null,
623
+ notes: item.withActivity?.notes ?? null,
624
+ startsAt: UserMemoryModel.parseDateFromString(item.withActivity?.startsAt),
625
+ status: item.withActivity?.status ?? 'pending',
626
+ tags: activityTags ?? null,
627
+ timezone: item.withActivity?.timezone ?? null,
628
+ type: item.withActivity?.type ?? 'other',
629
+ },
630
+ capturedAt: job.sourceUpdatedAt,
631
+ details: item.details ?? '',
632
+ detailsEmbedding: detailsVector ?? undefined,
633
+ memoryCategory: item.memoryCategory ?? null,
634
+ memoryLayer: (item.memoryLayer as LayersEnum) ?? LayersEnum.Activity,
635
+ memoryType: (item.memoryType as TypesEnum) ?? TypesEnum.Activity,
636
+ summary: item.summary ?? '',
637
+ summaryEmbedding: summaryVector ?? undefined,
638
+ title: item.title ?? '',
639
+ });
640
+
641
+ insertedIds.push(memory.id);
642
+ }
643
+
644
+ return insertedIds;
645
+ }
646
+
572
647
  async persistContextMemories(
573
648
  job: MemoryExtractionJob,
574
649
  messageIds: string[],
@@ -601,7 +676,7 @@ export class MemoryExtractionExecutor {
601
676
  associatedObjects: UserMemoryModel.parseAssociatedObjects(
602
677
  item.withContext?.associatedObjects,
603
678
  ),
604
- associatedSubjects: UserMemoryModel.parseAssociatedObjects(
679
+ associatedSubjects: UserMemoryModel.parseAssociatedSubjects(
605
680
  item.withContext?.associatedSubjects,
606
681
  ),
607
682
  capturedAt: job.sourceUpdatedAt,
@@ -923,13 +998,14 @@ export class MemoryExtractionExecutor {
923
998
  if (vector) {
924
999
  const retrieved = await userMemoryModel.searchWithEmbedding({
925
1000
  embedding: vector,
926
- limits: { contexts: topK, experiences: topK, preferences: topK },
1001
+ limits: { activities: topK, contexts: topK, experiences: topK, preferences: topK },
927
1002
  });
928
1003
 
929
1004
  return retrieved;
930
1005
  }
931
1006
 
932
1007
  return {
1008
+ activities: [],
933
1009
  contexts: [],
934
1010
  experiences: [],
935
1011
  preferences: [],
@@ -1563,6 +1639,24 @@ export class MemoryExtractionExecutor {
1563
1639
  );
1564
1640
  };
1565
1641
 
1642
+ const activityOutput = extraction.outputs.activity;
1643
+ if (activityOutput?.error) {
1644
+ appendError(LayersEnum.Activity, 'extract', activityOutput.error);
1645
+ }
1646
+ if (activityOutput?.data) {
1647
+ await persistWithSpan(LayersEnum.Activity, () =>
1648
+ this.persistActivityMemories(
1649
+ job,
1650
+ messageIds,
1651
+ activityOutput.data,
1652
+ runtimes.embeddings,
1653
+ this.modelConfig.embeddingsModel,
1654
+ this.embeddingContextLimit,
1655
+ db,
1656
+ ),
1657
+ );
1658
+ }
1659
+
1566
1660
  const contextOutput = extraction.outputs.context;
1567
1661
  if (contextOutput?.error) {
1568
1662
  appendError(LayersEnum.Context, 'extract', contextOutput.error);
@@ -1636,7 +1730,7 @@ export class MemoryExtractionExecutor {
1636
1730
  }
1637
1731
 
1638
1732
  if (errors.length) {
1639
- const detail = errors.map((error) => error.message).join('; ');
1733
+ const detail = errors.map((error) => `${error.message}${error.cause ? `: ${error.cause}` : ''}`).join('; ');
1640
1734
  throw new AggregateError(errors, `Memory extraction encountered layer errors: ${detail}`);
1641
1735
  }
1642
1736
 
@@ -1825,14 +1919,21 @@ export class MemoryExtractionExecutor {
1825
1919
  userId: params.userId,
1826
1920
  });
1827
1921
 
1922
+ const latestCreatedAt = params.parts.reduce<Date | undefined>((latest, part) => {
1923
+ if (!part.createdAt) return latest;
1924
+
1925
+ const date = new Date(part.createdAt);
1926
+ if (Number.isNaN(date.getTime())) return latest;
1927
+
1928
+ return !latest || date > latest ? date : latest;
1929
+ }, undefined);
1930
+
1828
1931
  extractionJob = {
1829
1932
  force: params.forceAll ?? true,
1830
1933
  layers: params.layers,
1831
1934
  source: params.source,
1832
1935
  sourceId: params.sourceId,
1833
- sourceUpdatedAt: params.parts.at(-1)?.createdAt
1834
- ? new Date(params.parts.at(-1)!.createdAt as Date)
1835
- : new Date(),
1936
+ sourceUpdatedAt: latestCreatedAt ?? new Date(),
1836
1937
  userId: params.userId,
1837
1938
  };
1838
1939
 
@@ -1898,7 +1999,7 @@ export class MemoryExtractionExecutor {
1898
1999
  language,
1899
2000
  retrievedContexts: [trimmedContext],
1900
2001
  retrievedIdentitiesContext: undefined,
1901
- sessionDate: new Date().toISOString(),
2002
+ sessionDate: (latestCreatedAt ?? new Date()).toISOString(),
1902
2003
  topK: 10,
1903
2004
  username:
1904
2005
  userState.fullName || `${userState.firstName} ${userState.lastName}`.trim() || 'User',