@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.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +5 -0
- package/package.json +1 -1
- package/packages/const/src/userMemory.ts +1 -0
- package/packages/database/src/models/userMemory/model.ts +178 -3
- package/packages/database/src/models/userMemory/sources/benchmarkLoCoMo.ts +1 -1
- package/packages/memory-user-memory/package.json +2 -1
- package/packages/memory-user-memory/promptfoo/evals/activity/basic/buildMessages.ts +40 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/basic/eval.yaml +13 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/basic/prompt.ts +5 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/basic/tests/cases.ts +106 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/buildMessages.ts +104 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/eval.yaml +13 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/prompt.ts +5 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/tests/benchmark-locomo-payload-conv-26.json +149 -0
- package/packages/memory-user-memory/promptfoo/evals/activity/locomo/tests/cases.ts +72 -0
- package/packages/memory-user-memory/promptfoo/response-formats/activity.json +370 -0
- package/packages/memory-user-memory/promptfoo/response-formats/experience.json +14 -0
- package/packages/memory-user-memory/promptfoo/response-formats/identity.json +281 -255
- package/packages/memory-user-memory/promptfooconfig.yaml +1 -0
- package/packages/memory-user-memory/scripts/generate-response-formats.ts +26 -2
- package/packages/memory-user-memory/src/extractors/activity.ts +44 -0
- package/packages/memory-user-memory/src/extractors/gatekeeper.test.ts +2 -1
- package/packages/memory-user-memory/src/extractors/gatekeeper.ts +2 -1
- package/packages/memory-user-memory/src/extractors/index.ts +1 -0
- package/packages/memory-user-memory/src/prompts/gatekeeper.ts +3 -3
- package/packages/memory-user-memory/src/prompts/index.ts +7 -1
- package/packages/memory-user-memory/src/prompts/layers/activity.ts +90 -0
- package/packages/memory-user-memory/src/prompts/layers/index.ts +1 -0
- package/packages/memory-user-memory/src/providers/existingUserMemory.test.ts +25 -1
- package/packages/memory-user-memory/src/providers/existingUserMemory.ts +113 -0
- package/packages/memory-user-memory/src/schemas/activity.ts +315 -0
- package/packages/memory-user-memory/src/schemas/experience.ts +5 -5
- package/packages/memory-user-memory/src/schemas/gatekeeper.ts +1 -0
- package/packages/memory-user-memory/src/schemas/index.ts +1 -0
- package/packages/memory-user-memory/src/services/extractExecutor.ts +29 -0
- package/packages/memory-user-memory/src/types.ts +7 -0
- package/packages/types/src/serverConfig.ts +1 -1
- package/packages/types/src/userMemory/layers.ts +52 -0
- package/packages/types/src/userMemory/list.ts +20 -2
- package/packages/types/src/userMemory/shared.ts +22 -1
- package/packages/types/src/userMemory/trace.ts +1 -0
- package/packages/types/src/util.ts +9 -1
- package/src/libs/next/proxy/define-config.ts +1 -0
- package/src/server/globalConfig/parseMemoryExtractionConfig.ts +7 -1
- package/src/server/services/memory/userMemory/__tests__/extract.runtime.test.ts +2 -0
- package/src/server/services/memory/userMemory/extract.ts +108 -7
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import type { GenerateObjectSchema } from '@lobechat/model-runtime';
|
|
2
|
+
import { ActivityTypeEnum, LayersEnum, TypesEnum } from '@lobechat/types';
|
|
3
|
+
import { type JSONSchema7 } from 'json-schema';
|
|
4
|
+
|
|
5
|
+
export interface WithActivity {
|
|
6
|
+
associatedLocations?: {
|
|
7
|
+
address?: string;
|
|
8
|
+
extra?: string | null;
|
|
9
|
+
name?: string;
|
|
10
|
+
tags?: string[];
|
|
11
|
+
type?: string;
|
|
12
|
+
}[];
|
|
13
|
+
associatedObjects?: {
|
|
14
|
+
extra?: string | null;
|
|
15
|
+
name?: string;
|
|
16
|
+
type?: string;
|
|
17
|
+
}[];
|
|
18
|
+
associatedSubjects?: {
|
|
19
|
+
extra?: string | null;
|
|
20
|
+
name?: string;
|
|
21
|
+
type?: string;
|
|
22
|
+
}[];
|
|
23
|
+
endsAt?: string;
|
|
24
|
+
feedback?: string;
|
|
25
|
+
metadata?: Record<string, unknown>;
|
|
26
|
+
narrative: string;
|
|
27
|
+
notes?: string;
|
|
28
|
+
startsAt?: string;
|
|
29
|
+
status?: string;
|
|
30
|
+
tags?: string[];
|
|
31
|
+
timezone?: string;
|
|
32
|
+
type: ActivityTypeEnum | string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ActivityMemoryItem {
|
|
36
|
+
details: string;
|
|
37
|
+
memoryCategory: string;
|
|
38
|
+
memoryLayer: LayersEnum.Activity;
|
|
39
|
+
memoryType: TypesEnum.Activity;
|
|
40
|
+
summary: string;
|
|
41
|
+
tags: string[];
|
|
42
|
+
title: string;
|
|
43
|
+
withActivity: WithActivity;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ActivityMemory {
|
|
47
|
+
memories: ActivityMemoryItem[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const ActivityMemorySchema: GenerateObjectSchema = {
|
|
51
|
+
description:
|
|
52
|
+
'Extract episodic activities with clear timelines, participants, objects, subjects, locations, and feelings. Temporal and associated fields are optional—omit when missing rather than guessing.',
|
|
53
|
+
name: 'activity_extraction',
|
|
54
|
+
schema: {
|
|
55
|
+
additionalProperties: false,
|
|
56
|
+
properties: {
|
|
57
|
+
memories: {
|
|
58
|
+
description:
|
|
59
|
+
'Array of extracted activity memories. Use an empty array when no activity should be captured.',
|
|
60
|
+
items: {
|
|
61
|
+
additionalProperties: false,
|
|
62
|
+
description:
|
|
63
|
+
'Self-contained activity memory describing what happened, when, where, with whom, and how it felt.',
|
|
64
|
+
examples: [
|
|
65
|
+
{
|
|
66
|
+
details:
|
|
67
|
+
'Talked through renewal scope, confirmed timeline flexibility, and captured follow-ups.',
|
|
68
|
+
memoryCategory: 'work',
|
|
69
|
+
memoryType: 'activity',
|
|
70
|
+
summary: 'Client Q2 renewal meeting with Alice (ACME)',
|
|
71
|
+
tags: ['meeting', 'client', 'renewal'],
|
|
72
|
+
title: 'ACME Q2 renewal meeting',
|
|
73
|
+
withActivity: {
|
|
74
|
+
associatedLocations: [
|
|
75
|
+
{
|
|
76
|
+
address: '123 Main St, New York, NY',
|
|
77
|
+
name: 'ACME HQ',
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
associatedSubjects: [
|
|
81
|
+
{ name: 'Alice Smith', type: 'person' },
|
|
82
|
+
],
|
|
83
|
+
endsAt: '2024-05-03T15:00:00-04:00',
|
|
84
|
+
feedback: 'Positive momentum; Alice felt heard and open to renewal.',
|
|
85
|
+
narrative:
|
|
86
|
+
'Alice and User reviewed Q2 renewal scope, aligned on reduced deliverables, and agreed to share revised pricing next week.',
|
|
87
|
+
notes: 'Agenda: renewal scope, pricing, next steps.',
|
|
88
|
+
startsAt: '2024-05-03T14:00:00-04:00',
|
|
89
|
+
status: 'completed',
|
|
90
|
+
timezone: 'America/New_York',
|
|
91
|
+
type: 'meeting',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
details: 'Routine check-up; discussed migraines and sleep habits.',
|
|
96
|
+
memoryCategory: 'health',
|
|
97
|
+
memoryType: 'activity',
|
|
98
|
+
summary: 'Doctor appointment with Dr. Kim about migraines',
|
|
99
|
+
tags: ['appointment', 'health'],
|
|
100
|
+
title: 'Neurology follow-up',
|
|
101
|
+
withActivity: {
|
|
102
|
+
associatedLocations: [
|
|
103
|
+
{
|
|
104
|
+
name: 'City Neurology Clinic',
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
associatedSubjects: [
|
|
108
|
+
{ name: 'Dr. Kim', type: 'person' },
|
|
109
|
+
],
|
|
110
|
+
feedback: 'Felt reassured; plan seems manageable.',
|
|
111
|
+
narrative:
|
|
112
|
+
'User saw Dr. Kim to review migraine frequency; decided to track sleep, hydration, and start a low-dose preventive.',
|
|
113
|
+
notes: 'Discussed triggers, hydration, and medication side effects.',
|
|
114
|
+
status: 'completed',
|
|
115
|
+
type: 'appointment',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
properties: {
|
|
120
|
+
details: {
|
|
121
|
+
description:
|
|
122
|
+
'Optional detailed information or longer notes supporting the summary and narrative.',
|
|
123
|
+
type: 'string',
|
|
124
|
+
},
|
|
125
|
+
memoryCategory: {
|
|
126
|
+
description:
|
|
127
|
+
'Memory category best matching the activity (e.g., work, health, travel, relationships).',
|
|
128
|
+
type: 'string',
|
|
129
|
+
},
|
|
130
|
+
memoryType: {
|
|
131
|
+
const: TypesEnum.Activity,
|
|
132
|
+
description: 'Memory type; always activity.',
|
|
133
|
+
type: 'string',
|
|
134
|
+
},
|
|
135
|
+
summary: {
|
|
136
|
+
description: 'Concise overview of this activity.',
|
|
137
|
+
type: 'string',
|
|
138
|
+
},
|
|
139
|
+
tags: {
|
|
140
|
+
description: 'Model-generated tags summarizing key facets of the activity.',
|
|
141
|
+
items: { type: 'string' },
|
|
142
|
+
type: 'array',
|
|
143
|
+
},
|
|
144
|
+
title: {
|
|
145
|
+
description:
|
|
146
|
+
'Brief descriptive title for the activity, e.g., "Dinner with friends at Marina".',
|
|
147
|
+
type: 'string',
|
|
148
|
+
},
|
|
149
|
+
withActivity: {
|
|
150
|
+
additionalProperties: false,
|
|
151
|
+
description:
|
|
152
|
+
'Structured activity fields. Temporal and association values are optional—include only when the user mentioned them.',
|
|
153
|
+
properties: {
|
|
154
|
+
associatedLocations: {
|
|
155
|
+
description:
|
|
156
|
+
'Places linked to this activity. Capture any mentioned venue, address, or setting.',
|
|
157
|
+
items: {
|
|
158
|
+
additionalProperties: false,
|
|
159
|
+
properties: {
|
|
160
|
+
address: {
|
|
161
|
+
description: 'Free-form address or directions if provided.',
|
|
162
|
+
type: ['string', 'null'],
|
|
163
|
+
},
|
|
164
|
+
extra: {
|
|
165
|
+
description: 'Optional key-value metadata related to the location.',
|
|
166
|
+
type: ['string', 'null'],
|
|
167
|
+
},
|
|
168
|
+
name: {
|
|
169
|
+
description: 'Place name or venue label.',
|
|
170
|
+
type: 'string',
|
|
171
|
+
},
|
|
172
|
+
tags: {
|
|
173
|
+
description: 'Place-related tags (e.g., indoor, outdoor, virtual).',
|
|
174
|
+
items: { type: 'string' },
|
|
175
|
+
type: ['array', 'null'],
|
|
176
|
+
},
|
|
177
|
+
type: {
|
|
178
|
+
description: 'Place type or category (office, clinic, restaurant, virtual).',
|
|
179
|
+
type: 'string',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
required: ['type', 'name', 'address', 'tags', 'extra'],
|
|
183
|
+
type: 'object',
|
|
184
|
+
},
|
|
185
|
+
type: 'array',
|
|
186
|
+
},
|
|
187
|
+
associatedObjects: {
|
|
188
|
+
description:
|
|
189
|
+
'Non-living entities or items tied to the activity (e.g., transportation for trips, devices, tools).',
|
|
190
|
+
items: {
|
|
191
|
+
additionalProperties: false,
|
|
192
|
+
properties: {
|
|
193
|
+
extra: {
|
|
194
|
+
description: 'Optional key-value metadata related to the object.',
|
|
195
|
+
type: ['string', 'null'],
|
|
196
|
+
},
|
|
197
|
+
name: {
|
|
198
|
+
description: 'Name or label of the object (e.g., “MacBook”, “flight UA123”).',
|
|
199
|
+
type: 'string',
|
|
200
|
+
},
|
|
201
|
+
type: {
|
|
202
|
+
description: 'Object category (e.g., transportation, device, document).',
|
|
203
|
+
enum: ['application', 'item', 'knowledge', 'other', 'person', 'place'],
|
|
204
|
+
type: 'string',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
required: ['type', 'name', 'extra'],
|
|
208
|
+
type: 'object',
|
|
209
|
+
},
|
|
210
|
+
type: 'array',
|
|
211
|
+
},
|
|
212
|
+
associatedSubjects: {
|
|
213
|
+
description:
|
|
214
|
+
'Living beings involved (people, pets, groups). Use when the subject lacks a known identity ID.',
|
|
215
|
+
items: {
|
|
216
|
+
additionalProperties: false,
|
|
217
|
+
properties: {
|
|
218
|
+
extra: {
|
|
219
|
+
description: 'Optional key-value metadata related to the subject.',
|
|
220
|
+
type: ['string', 'null'],
|
|
221
|
+
},
|
|
222
|
+
name: {
|
|
223
|
+
description: 'Name or short label of the subject.',
|
|
224
|
+
type: 'string',
|
|
225
|
+
},
|
|
226
|
+
type: {
|
|
227
|
+
description: 'Subject category (e.g., person, pet, group).',
|
|
228
|
+
enum: ['person', 'pet', 'group', 'other'],
|
|
229
|
+
type: 'string',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
required: ['type', 'name', 'extra'],
|
|
233
|
+
type: 'object',
|
|
234
|
+
},
|
|
235
|
+
type: 'array',
|
|
236
|
+
},
|
|
237
|
+
endsAt: {
|
|
238
|
+
description:
|
|
239
|
+
'ISO 8601 end time for the activity when specified. Omit if not explicitly provided.',
|
|
240
|
+
format: 'date-time',
|
|
241
|
+
type: ['string', 'null'],
|
|
242
|
+
},
|
|
243
|
+
feedback: {
|
|
244
|
+
description:
|
|
245
|
+
'Subjective feelings or evaluation of how the activity went (mood, satisfaction, effort).',
|
|
246
|
+
type: ['string', 'null'],
|
|
247
|
+
},
|
|
248
|
+
metadata: {
|
|
249
|
+
additionalProperties: false,
|
|
250
|
+
description:
|
|
251
|
+
'Additional structured metadata to keep raw hints (JSON object). Use sparingly.',
|
|
252
|
+
type: ['object', 'null'],
|
|
253
|
+
},
|
|
254
|
+
narrative: {
|
|
255
|
+
description:
|
|
256
|
+
'Factual story of what happened (chronology, participants, outcomes). Required for recall.',
|
|
257
|
+
type: 'string',
|
|
258
|
+
},
|
|
259
|
+
notes: {
|
|
260
|
+
description:
|
|
261
|
+
'Short annotations such as agenda, preparation, or quick bullets distinct from narrative.',
|
|
262
|
+
type: ['string', 'null'],
|
|
263
|
+
},
|
|
264
|
+
startsAt: {
|
|
265
|
+
description:
|
|
266
|
+
'ISO 8601 start time for the activity when specified. Omit if not explicitly provided.',
|
|
267
|
+
format: 'date-time',
|
|
268
|
+
type: ['string', 'null'],
|
|
269
|
+
},
|
|
270
|
+
status: {
|
|
271
|
+
description:
|
|
272
|
+
'Lifecycle status when mentioned. Use planned/completed/cancelled/ongoing/on_hold/pending. Omit if unclear.',
|
|
273
|
+
enum: ['planned', 'completed', 'cancelled', 'ongoing', 'on_hold', 'pending'],
|
|
274
|
+
type: ['string', 'null'],
|
|
275
|
+
},
|
|
276
|
+
tags: {
|
|
277
|
+
description: 'Optional activity-specific tags or facets.',
|
|
278
|
+
items: { type: 'string' },
|
|
279
|
+
type: ['array', 'null'],
|
|
280
|
+
},
|
|
281
|
+
timezone: {
|
|
282
|
+
description:
|
|
283
|
+
'IANA timezone string for the start/end times when provided (e.g., "America/New_York").',
|
|
284
|
+
type: ['string', 'null'],
|
|
285
|
+
},
|
|
286
|
+
type: {
|
|
287
|
+
description:
|
|
288
|
+
'Activity type enum. Choose the closest match; fall back to "other" when unclear.',
|
|
289
|
+
enum: Object.values(ActivityTypeEnum),
|
|
290
|
+
type: 'string',
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
required: ['type', 'narrative', 'feedback', 'notes', 'associatedLocations', 'associatedSubjects', 'associatedObjects', 'startsAt', 'endsAt', 'status', 'tags', 'timezone', 'metadata'],
|
|
294
|
+
type: 'object',
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
required: [
|
|
298
|
+
'title',
|
|
299
|
+
'summary',
|
|
300
|
+
'details',
|
|
301
|
+
'memoryType',
|
|
302
|
+
'memoryCategory',
|
|
303
|
+
'tags',
|
|
304
|
+
'withActivity',
|
|
305
|
+
],
|
|
306
|
+
type: 'object',
|
|
307
|
+
},
|
|
308
|
+
type: 'array',
|
|
309
|
+
},
|
|
310
|
+
} satisfies JSONSchema7['properties'],
|
|
311
|
+
required: ['memories'],
|
|
312
|
+
type: 'object',
|
|
313
|
+
},
|
|
314
|
+
strict: true,
|
|
315
|
+
};
|
|
@@ -8,6 +8,11 @@ import { MemoryTypeSchema } from './common';
|
|
|
8
8
|
export const WithExperienceSchema = z.object({
|
|
9
9
|
action: z.string().describe('Narrative describing actions taken or behaviors exhibited'),
|
|
10
10
|
keyLearning: z.string().describe('Narrative describing key insights or lessons learned'),
|
|
11
|
+
knowledgeValueScore: z
|
|
12
|
+
.number()
|
|
13
|
+
.min(0)
|
|
14
|
+
.max(1)
|
|
15
|
+
.describe('Numeric score (0-1) describing how reusable and shareable this experience is'),
|
|
11
16
|
labels: z.array(z.string()).describe('Model generated tags that summarize the experience facets'),
|
|
12
17
|
possibleOutcome: z.string().describe('Narrative describing potential outcomes or learnings'),
|
|
13
18
|
problemSolvingScore: z
|
|
@@ -15,11 +20,6 @@ export const WithExperienceSchema = z.object({
|
|
|
15
20
|
.min(0)
|
|
16
21
|
.max(1)
|
|
17
22
|
.describe('Numeric score (0-1) describing how effectively the problem was solved'),
|
|
18
|
-
knowledgeValueScore: z
|
|
19
|
-
.number()
|
|
20
|
-
.min(0)
|
|
21
|
-
.max(1)
|
|
22
|
-
.describe('Numeric score (0-1) describing how reusable and shareable this experience is'),
|
|
23
23
|
reasoning: z.string().describe('Narrative describing the thought process or motivations'),
|
|
24
24
|
scoreConfidence: z
|
|
25
25
|
.number()
|
|
@@ -14,6 +14,7 @@ export type LayerDecision = z.infer<typeof LayerDecisionSchema>;
|
|
|
14
14
|
* Gatekeeper result schema for memory layers
|
|
15
15
|
*/
|
|
16
16
|
export const GatekeeperResultSchema = z.object({
|
|
17
|
+
activity: LayerDecisionSchema,
|
|
17
18
|
context: LayerDecisionSchema,
|
|
18
19
|
experience: LayerDecisionSchema,
|
|
19
20
|
identity: LayerDecisionSchema,
|
|
@@ -12,6 +12,7 @@ import { attributesCommon } from '@lobechat/observability-otel/node';
|
|
|
12
12
|
import { LayersEnum } from '@lobechat/types';
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
|
+
ActivityExtractor,
|
|
15
16
|
ContextExtractor,
|
|
16
17
|
ExperienceExtractor,
|
|
17
18
|
IdentityExtractor,
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
} from '../types';
|
|
33
34
|
|
|
34
35
|
const LAYER_ORDER: LayersEnum[] = [
|
|
36
|
+
'activity' as LayersEnum,
|
|
35
37
|
'identity' as LayersEnum,
|
|
36
38
|
'context' as LayersEnum,
|
|
37
39
|
'preference' as LayersEnum,
|
|
@@ -39,6 +41,7 @@ const LAYER_ORDER: LayersEnum[] = [
|
|
|
39
41
|
];
|
|
40
42
|
|
|
41
43
|
const LAYER_LABEL_MAP: Record<LayersEnum, string> = {
|
|
44
|
+
activity: 'activities',
|
|
42
45
|
context: 'contexts',
|
|
43
46
|
experience: 'experiences',
|
|
44
47
|
identity: 'identities',
|
|
@@ -61,6 +64,7 @@ export interface MemoryExtractionServiceOptions {
|
|
|
61
64
|
|
|
62
65
|
export interface MemoryExtractionLayerOutputTypes {
|
|
63
66
|
[LayersEnum.Context]: Awaited<ReturnType<ContextExtractor['structuredCall']>>;
|
|
67
|
+
[LayersEnum.Activity]: Awaited<ReturnType<ActivityExtractor['structuredCall']>>;
|
|
64
68
|
[LayersEnum.Experience]: Awaited<ReturnType<ExperienceExtractor['structuredCall']>>;
|
|
65
69
|
[LayersEnum.Preference]: Awaited<ReturnType<PreferenceExtractor['structuredCall']>>;
|
|
66
70
|
[LayersEnum.Identity]: Awaited<ReturnType<IdentityExtractor['structuredCall']>>;
|
|
@@ -79,6 +83,7 @@ export class MemoryExtractionService<RO> {
|
|
|
79
83
|
private readonly contextExtractor: ContextExtractor;
|
|
80
84
|
private readonly experienceExtractor: ExperienceExtractor;
|
|
81
85
|
private readonly preferenceExtractor: PreferenceExtractor;
|
|
86
|
+
private readonly activityExtractor: ActivityExtractor;
|
|
82
87
|
|
|
83
88
|
private readonly gatekeeperRuntime: ModelRuntime;
|
|
84
89
|
private readonly layerRuntime: ModelRuntime;
|
|
@@ -115,6 +120,9 @@ export class MemoryExtractionService<RO> {
|
|
|
115
120
|
this.identityExtractor = new IdentityExtractor(
|
|
116
121
|
buildExtractorConfig(LayersEnum.Identity, 'layer-identity'),
|
|
117
122
|
);
|
|
123
|
+
this.activityExtractor = new ActivityExtractor(
|
|
124
|
+
buildExtractorConfig(LayersEnum.Activity, 'layer-activity'),
|
|
125
|
+
);
|
|
118
126
|
this.contextExtractor = new ContextExtractor(
|
|
119
127
|
buildExtractorConfig(LayersEnum.Context, 'layer-context'),
|
|
120
128
|
);
|
|
@@ -214,6 +222,7 @@ export class MemoryExtractionService<RO> {
|
|
|
214
222
|
const outputs = await this.runLayers(job, layersToExtract, { ...options });
|
|
215
223
|
|
|
216
224
|
const processedLayersCount = {
|
|
225
|
+
activity: outputs.activity?.data ? outputs.activity?.data?.memories?.length : 0,
|
|
217
226
|
context: outputs.context?.data ? outputs.context?.data?.memories?.length : 0,
|
|
218
227
|
experience: outputs.experience?.data ? outputs.experience?.data?.memories?.length : 0,
|
|
219
228
|
identity: outputs.identity?.data
|
|
@@ -224,6 +233,7 @@ export class MemoryExtractionService<RO> {
|
|
|
224
233
|
preference: outputs.preference?.data ? outputs.preference?.data?.memories?.length : 0,
|
|
225
234
|
};
|
|
226
235
|
const processedErrorsCount = {
|
|
236
|
+
activity: outputs.activity?.error ? 1 : 0,
|
|
227
237
|
context: outputs.context?.error ? 1 : 0,
|
|
228
238
|
experience: outputs.experience?.error ? 1 : 0,
|
|
229
239
|
identity: outputs.identity?.error ? 1 : 0,
|
|
@@ -281,6 +291,15 @@ export class MemoryExtractionService<RO> {
|
|
|
281
291
|
);
|
|
282
292
|
}
|
|
283
293
|
|
|
294
|
+
private async runActivityLayer(job: MemoryExtractionJob, options: ExtractorOptions) {
|
|
295
|
+
return this.runLayerExtractor(job, LayersEnum.Activity, () =>
|
|
296
|
+
this.activityExtractor.structuredCall({
|
|
297
|
+
...options,
|
|
298
|
+
language: options.language ?? 'English',
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
284
303
|
private async runExperienceLayer(job: MemoryExtractionJob, options: ExtractorOptions) {
|
|
285
304
|
return this.runLayerExtractor(job, LayersEnum.Experience, () =>
|
|
286
305
|
this.experienceExtractor.structuredCall({
|
|
@@ -347,6 +366,12 @@ export class MemoryExtractionService<RO> {
|
|
|
347
366
|
| { error: unknown };
|
|
348
367
|
break;
|
|
349
368
|
}
|
|
369
|
+
case LayersEnum.Activity: {
|
|
370
|
+
outputs.activity = result as
|
|
371
|
+
| { data: MemoryExtractionLayerOutputTypes[typeof layer] }
|
|
372
|
+
| { error: unknown };
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
350
375
|
case LayersEnum.Experience: {
|
|
351
376
|
outputs.experience = result as
|
|
352
377
|
| { data: MemoryExtractionLayerOutputTypes[typeof layer] }
|
|
@@ -374,6 +399,7 @@ export class MemoryExtractionService<RO> {
|
|
|
374
399
|
await Promise.all(
|
|
375
400
|
(
|
|
376
401
|
[
|
|
402
|
+
LayersEnum.Activity,
|
|
377
403
|
LayersEnum.Context,
|
|
378
404
|
LayersEnum.Experience,
|
|
379
405
|
LayersEnum.Preference,
|
|
@@ -407,6 +433,9 @@ export class MemoryExtractionService<RO> {
|
|
|
407
433
|
case LayersEnum.Context: {
|
|
408
434
|
return await this.runContextLayer(job, options);
|
|
409
435
|
}
|
|
436
|
+
case LayersEnum.Activity: {
|
|
437
|
+
return await this.runActivityLayer(job, options);
|
|
438
|
+
}
|
|
410
439
|
case LayersEnum.Experience: {
|
|
411
440
|
return await this.runExperienceLayer(job, options);
|
|
412
441
|
}
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
import type { LayersEnum, MemorySourceType } from '@lobechat/types';
|
|
7
7
|
|
|
8
8
|
import type {
|
|
9
|
+
ActivityExtractor,
|
|
9
10
|
ContextExtractor,
|
|
10
11
|
ExperienceExtractor,
|
|
11
12
|
IdentityExtractor,
|
|
@@ -14,6 +15,7 @@ import type {
|
|
|
14
15
|
|
|
15
16
|
export type MemoryExtractionAgent =
|
|
16
17
|
| 'gatekeeper'
|
|
18
|
+
| 'layer-activity'
|
|
17
19
|
| 'layer-context'
|
|
18
20
|
| 'layer-experience'
|
|
19
21
|
| 'layer-identity'
|
|
@@ -124,6 +126,10 @@ export interface PersistedMemoryResult {
|
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
export type MemoryExtractionLayerOutputs = Partial<{
|
|
129
|
+
activity: {
|
|
130
|
+
data?: Awaited<ReturnType<ActivityExtractor['structuredCall']>>;
|
|
131
|
+
error?: unknown;
|
|
132
|
+
};
|
|
127
133
|
context: {
|
|
128
134
|
data?: Awaited<ReturnType<ContextExtractor['structuredCall']>>;
|
|
129
135
|
error?: unknown;
|
|
@@ -143,6 +149,7 @@ export type MemoryExtractionLayerOutputs = Partial<{
|
|
|
143
149
|
}>;
|
|
144
150
|
|
|
145
151
|
export interface GatekeeperDecision {
|
|
152
|
+
activity: MemoryLayerDecision;
|
|
146
153
|
context: MemoryLayerDecision;
|
|
147
154
|
experience: MemoryLayerDecision;
|
|
148
155
|
identity: MemoryLayerDecision;
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
UserSystemAgentConfig,
|
|
11
11
|
} from './user/settings';
|
|
12
12
|
|
|
13
|
-
export type GlobalMemoryLayer = 'context' | 'experience' | 'identity' | 'preference';
|
|
13
|
+
export type GlobalMemoryLayer = 'activity' | 'context' | 'experience' | 'identity' | 'preference';
|
|
14
14
|
|
|
15
15
|
export interface MemoryAgentPublicConfig {
|
|
16
16
|
baseURL?: string;
|
|
@@ -22,6 +22,26 @@ export enum UserMemoryContextSubjectType {
|
|
|
22
22
|
}
|
|
23
23
|
export const CONTEXT_SUBJECT_TYPES = Object.values(UserMemoryContextSubjectType);
|
|
24
24
|
|
|
25
|
+
export interface UserMemoryAssociatedLocation {
|
|
26
|
+
address?: string | null;
|
|
27
|
+
extra?: Record<string, unknown> | null;
|
|
28
|
+
name?: string | null;
|
|
29
|
+
tags?: string[] | null;
|
|
30
|
+
type?: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UserMemoryAssociatedObject {
|
|
34
|
+
extra?: Record<string, unknown> | null;
|
|
35
|
+
name?: string;
|
|
36
|
+
type?: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface UserMemoryAssociatedSubject {
|
|
40
|
+
extra?: Record<string, unknown> | null;
|
|
41
|
+
name?: string;
|
|
42
|
+
type?: string | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
25
45
|
export interface UserMemoryContext extends UserMemoryTimestamps {
|
|
26
46
|
associatedObjects:
|
|
27
47
|
| {
|
|
@@ -112,3 +132,35 @@ export type UserMemoryPreferenceWithoutVectors = Omit<
|
|
|
112
132
|
>;
|
|
113
133
|
|
|
114
134
|
export type UserMemoryPreferencesListItem = Omit<UserMemoryPreferenceWithoutVectors, 'suggestions'>;
|
|
135
|
+
|
|
136
|
+
export interface UserMemoryActivity extends UserMemoryTimestamps {
|
|
137
|
+
associatedLocations: UserMemoryAssociatedLocation[] | null;
|
|
138
|
+
associatedObjects: UserMemoryAssociatedObject[] | null;
|
|
139
|
+
associatedSubjects: UserMemoryAssociatedSubject[] | null;
|
|
140
|
+
endsAt: Date | null;
|
|
141
|
+
feedback: string | null;
|
|
142
|
+
feedbackVector: number[] | null;
|
|
143
|
+
id: string;
|
|
144
|
+
metadata: Record<string, unknown> | null;
|
|
145
|
+
narrative: string | null;
|
|
146
|
+
narrativeVector: number[] | null;
|
|
147
|
+
notes: string | null;
|
|
148
|
+
startsAt: Date | null;
|
|
149
|
+
status: string | null;
|
|
150
|
+
tags: string[] | null;
|
|
151
|
+
timezone: string | null;
|
|
152
|
+
type: string | null;
|
|
153
|
+
userId: string | null;
|
|
154
|
+
userMemoryId: string | null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
158
|
+
export type UserMemoryActivityWithoutVectors = Omit<
|
|
159
|
+
UserMemoryActivity,
|
|
160
|
+
'narrativeVector' | 'feedbackVector'
|
|
161
|
+
>;
|
|
162
|
+
|
|
163
|
+
export type UserMemoryActivitiesListItem = Omit<
|
|
164
|
+
UserMemoryActivityWithoutVectors,
|
|
165
|
+
'feedback' | 'narrative' | 'notes'
|
|
166
|
+
>;
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
UserMemoryContextsListItem,
|
|
6
6
|
UserMemoryExperiencesListItem,
|
|
7
7
|
UserMemoryPreferencesListItem,
|
|
8
|
+
UserMemoryActivitiesListItem,
|
|
9
|
+
UserMemoryActivityWithoutVectors,
|
|
8
10
|
} from './layers'
|
|
9
11
|
import {
|
|
10
12
|
UserMemoryIdentityWithoutVectors,
|
|
@@ -87,14 +89,30 @@ export interface IdentityMemoryDetail {
|
|
|
87
89
|
sourceType?: MemorySourceType;
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
export interface ActivityMemorySimple {
|
|
93
|
+
activity: UserMemoryActivitiesListItem;
|
|
94
|
+
layer: LayersEnum.Activity;
|
|
95
|
+
memory: UserMemoryListItem;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ActivityMemoryDetail {
|
|
99
|
+
activity: UserMemoryActivityWithoutVectors;
|
|
100
|
+
layer: LayersEnum.Activity;
|
|
101
|
+
memory: UserMemoryWithoutVectors;
|
|
102
|
+
source?: MemorySource;
|
|
103
|
+
sourceType?: MemorySourceType;
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
export type UserMemoryItemSimple =
|
|
91
107
|
| ContextMemorySimple
|
|
92
108
|
| ExperienceMemorySimple
|
|
93
109
|
| IdentityMemorySimple
|
|
94
|
-
| PreferenceMemorySimple
|
|
110
|
+
| PreferenceMemorySimple
|
|
111
|
+
| ActivityMemorySimple;
|
|
95
112
|
|
|
96
113
|
export type UserMemoryDetail =
|
|
97
114
|
| ContextMemoryDetail
|
|
98
115
|
| ExperienceMemoryDetail
|
|
99
116
|
| IdentityMemoryDetail
|
|
100
|
-
| PreferenceMemoryDetail
|
|
117
|
+
| PreferenceMemoryDetail
|
|
118
|
+
| ActivityMemoryDetail;
|
|
@@ -46,6 +46,7 @@ export enum IdentityTypeEnum {
|
|
|
46
46
|
export const IDENTITY_TYPES = Object.values(IdentityTypeEnum);
|
|
47
47
|
|
|
48
48
|
export enum LayersEnum {
|
|
49
|
+
Activity = 'activity',
|
|
49
50
|
Context = 'context',
|
|
50
51
|
Experience = 'experience',
|
|
51
52
|
Identity = 'identity',
|
|
@@ -80,7 +81,6 @@ export const CONTEXT_STATUS = Object.values(ContextStatusEnum);
|
|
|
80
81
|
/**
|
|
81
82
|
* Shared types for memory list queries
|
|
82
83
|
*/
|
|
83
|
-
|
|
84
84
|
export interface BaseListParams {
|
|
85
85
|
order?: 'asc' | 'desc';
|
|
86
86
|
page?: number;
|
|
@@ -106,3 +106,24 @@ export interface BaseListItem {
|
|
|
106
106
|
type: string | null;
|
|
107
107
|
updatedAt: Date;
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
export enum ActivityTypeEnum {
|
|
111
|
+
Appointment = 'appointment',
|
|
112
|
+
Call = 'call',
|
|
113
|
+
Celebration = 'celebration',
|
|
114
|
+
Class = 'class',
|
|
115
|
+
Conference = 'conference',
|
|
116
|
+
Errand = 'errand',
|
|
117
|
+
Event = 'event',
|
|
118
|
+
Exercise = 'exercise',
|
|
119
|
+
Meal = 'meal',
|
|
120
|
+
Meeting = 'meeting',
|
|
121
|
+
Other = 'other',
|
|
122
|
+
ProjectSession = 'project-session',
|
|
123
|
+
Social = 'social',
|
|
124
|
+
Task = 'task',
|
|
125
|
+
Trip = 'trip',
|
|
126
|
+
Workshop = 'workshop',
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const ACTIVITY_TYPES = Object.values(ActivityTypeEnum);
|
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
export type Primitive = number | string | boolean | undefined | null | any[];
|
|
2
|
-
|
|
2
|
+
type OptionalKeys<T> = { [P in keyof T]-?: undefined extends T[P] ? P : never }[keyof T];
|
|
3
|
+
type OptionalField<T, P extends keyof T, K extends keyof T> = T[P] extends Primitive
|
|
4
|
+
? T[P]
|
|
5
|
+
: Optional<T[P], keyof T[P] & K>;
|
|
6
|
+
|
|
7
|
+
export type Optional<T, K extends keyof T> = T extends null | undefined
|
|
8
|
+
? T
|
|
9
|
+
: Partial<Pick<T, K | OptionalKeys<T>>> &
|
|
10
|
+
{ [P in Exclude<keyof T, K | OptionalKeys<T>>]: OptionalField<T, P, K> };
|