@lobehub/lobehub 2.0.0-next.360 → 2.0.0-next.362
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 +50 -0
- package/Dockerfile +2 -1
- package/changelog/v1.json +14 -0
- package/locales/en-US/chat.json +3 -1
- package/locales/zh-CN/chat.json +2 -0
- package/package.json +1 -1
- package/packages/const/src/userMemory.ts +1 -0
- package/packages/context-engine/src/base/BaseEveryUserContentProvider.ts +204 -0
- package/packages/context-engine/src/base/BaseLastUserContentProvider.ts +1 -8
- package/packages/context-engine/src/base/__tests__/BaseEveryUserContentProvider.test.ts +354 -0
- package/packages/context-engine/src/base/constants.ts +20 -0
- package/packages/context-engine/src/engine/messages/MessagesEngine.ts +27 -23
- package/packages/context-engine/src/engine/messages/__tests__/MessagesEngine.test.ts +364 -0
- package/packages/context-engine/src/providers/PageEditorContextInjector.ts +17 -13
- package/packages/context-engine/src/providers/PageSelectionsInjector.ts +65 -0
- package/packages/context-engine/src/providers/__tests__/PageSelectionsInjector.test.ts +333 -0
- package/packages/context-engine/src/providers/index.ts +3 -1
- 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/prompts/src/agents/index.ts +1 -0
- package/packages/prompts/src/agents/pageSelectionContext.ts +28 -0
- package/packages/types/src/aiChat.ts +4 -0
- package/packages/types/src/message/common/index.ts +1 -0
- package/packages/types/src/message/common/metadata.ts +8 -0
- package/packages/types/src/message/common/pageSelection.ts +36 -0
- package/packages/types/src/message/ui/params.ts +16 -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/scripts/prebuild.mts +1 -0
- package/src/features/ChatInput/Desktop/ContextContainer/ContextList.tsx +1 -1
- package/src/features/Conversation/ChatInput/index.tsx +9 -1
- package/src/features/Conversation/Messages/User/components/MessageContent.tsx +7 -1
- package/src/features/Conversation/Messages/User/components/PageSelections.tsx +62 -0
- package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +5 -1
- package/src/libs/next/proxy/define-config.ts +1 -0
- package/src/locales/default/chat.ts +3 -2
- package/src/server/globalConfig/parseMemoryExtractionConfig.ts +7 -1
- package/src/server/routers/lambda/aiChat.ts +7 -0
- package/src/server/services/memory/userMemory/__tests__/extract.runtime.test.ts +2 -0
- package/src/server/services/memory/userMemory/extract.ts +108 -7
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +5 -19
|
@@ -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;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PageSelection } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Format page selections into a system prompt context
|
|
5
|
+
* Each selection is wrapped in a <selection> tag with metadata
|
|
6
|
+
*/
|
|
7
|
+
export const formatPageSelections = (selections: PageSelection[]): string => {
|
|
8
|
+
if (!selections || selections.length === 0) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const formattedSelections = selections
|
|
13
|
+
.map((sel) => {
|
|
14
|
+
const lineInfo =
|
|
15
|
+
sel.startLine !== undefined
|
|
16
|
+
? ` lines="${sel.startLine}-${sel.endLine ?? sel.startLine}"`
|
|
17
|
+
: '';
|
|
18
|
+
|
|
19
|
+
return `<selection ${lineInfo}>
|
|
20
|
+
${sel.xml}
|
|
21
|
+
</selection>`;
|
|
22
|
+
})
|
|
23
|
+
.join('\n');
|
|
24
|
+
|
|
25
|
+
return `<user_selections count="${selections.length}">
|
|
26
|
+
${formattedSelections}
|
|
27
|
+
</user_selections>`;
|
|
28
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
3
|
import { UIChatMessage } from './message';
|
|
4
|
+
import { PageSelection, PageSelectionSchema } from './message/ui/params';
|
|
4
5
|
import { OpenAIChatMessage } from './openai/chat';
|
|
5
6
|
import { LobeUniformTool, LobeUniformToolSchema } from './tool';
|
|
6
7
|
import { ChatTopic } from './topic';
|
|
@@ -10,6 +11,8 @@ export interface SendNewMessage {
|
|
|
10
11
|
content: string;
|
|
11
12
|
// if message has attached with files, then add files to message and the agent
|
|
12
13
|
files?: string[];
|
|
14
|
+
/** Page selections attached to this message (for Ask AI functionality) */
|
|
15
|
+
pageSelections?: PageSelection[];
|
|
13
16
|
parentId?: string;
|
|
14
17
|
}
|
|
15
18
|
|
|
@@ -83,6 +86,7 @@ export const AiSendMessageServerSchema = z.object({
|
|
|
83
86
|
newUserMessage: z.object({
|
|
84
87
|
content: z.string(),
|
|
85
88
|
files: z.array(z.string()).optional(),
|
|
89
|
+
pageSelections: z.array(PageSelectionSchema).optional(),
|
|
86
90
|
parentId: z.string().optional(),
|
|
87
91
|
}),
|
|
88
92
|
sessionId: z.string().optional(),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
+
import { PageSelection, PageSelectionSchema } from './pageSelection';
|
|
5
|
+
|
|
4
6
|
export interface ModelTokensUsage {
|
|
5
7
|
// Input tokens breakdown
|
|
6
8
|
/**
|
|
@@ -80,6 +82,7 @@ export const MessageMetadataSchema = ModelUsageSchema.merge(ModelPerformanceSche
|
|
|
80
82
|
inspectExpanded: z.boolean().optional(),
|
|
81
83
|
isMultimodal: z.boolean().optional(),
|
|
82
84
|
isSupervisor: z.boolean().optional(),
|
|
85
|
+
pageSelections: z.array(PageSelectionSchema).optional(),
|
|
83
86
|
});
|
|
84
87
|
|
|
85
88
|
export interface ModelUsage extends ModelTokensUsage {
|
|
@@ -147,4 +150,9 @@ export interface MessageMetadata extends ModelUsage, ModelPerformance {
|
|
|
147
150
|
*/
|
|
148
151
|
instruction?: string;
|
|
149
152
|
taskTitle?: string;
|
|
153
|
+
/**
|
|
154
|
+
* Page selections attached to user message
|
|
155
|
+
* Used for Ask AI functionality to persist selection context
|
|
156
|
+
*/
|
|
157
|
+
pageSelections?: PageSelection[];
|
|
150
158
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Page selection represents a user-selected text region in a page/document.
|
|
6
|
+
* Used for Ask AI functionality to persist selection context with user messages.
|
|
7
|
+
*/
|
|
8
|
+
export interface PageSelection {
|
|
9
|
+
/** Selection unique identifier */
|
|
10
|
+
id: string;
|
|
11
|
+
anchor?: {
|
|
12
|
+
startNodeId: string;
|
|
13
|
+
endNodeId: string;
|
|
14
|
+
startOffset: number;
|
|
15
|
+
endOffset: number;
|
|
16
|
+
};
|
|
17
|
+
/** Selected content (plain text or markdown) */
|
|
18
|
+
content: string;
|
|
19
|
+
/** XML structure of the selected content (for positioning edits) */
|
|
20
|
+
xml?: string;
|
|
21
|
+
/** Page ID the selection belongs to */
|
|
22
|
+
pageId: string;
|
|
23
|
+
/** Start line number */
|
|
24
|
+
startLine?: number;
|
|
25
|
+
/** End line number */
|
|
26
|
+
endLine?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const PageSelectionSchema = z.object({
|
|
30
|
+
id: z.string(),
|
|
31
|
+
content: z.string(),
|
|
32
|
+
xml: z.string().optional(),
|
|
33
|
+
pageId: z.string(),
|
|
34
|
+
startLine: z.number().optional(),
|
|
35
|
+
endLine: z.number().optional(),
|
|
36
|
+
});
|
|
@@ -5,6 +5,8 @@ import { ConversationContext } from '../../conversation';
|
|
|
5
5
|
import { UploadFileItem } from '../../files';
|
|
6
6
|
import { MessageSemanticSearchChunk } from '../../rag';
|
|
7
7
|
import { ChatMessageError, ChatMessageErrorSchema } from '../common/base';
|
|
8
|
+
// Import for local use
|
|
9
|
+
import type { PageSelection } from '../common/pageSelection';
|
|
8
10
|
import { ChatPluginPayload, ToolInterventionSchema } from '../common/tools';
|
|
9
11
|
import { UIChatMessage } from './chat';
|
|
10
12
|
import { SemanticSearchChunkSchema } from './rag';
|
|
@@ -78,6 +80,10 @@ export interface ChatContextContent {
|
|
|
78
80
|
*/
|
|
79
81
|
format?: 'xml' | 'text' | 'markdown';
|
|
80
82
|
id: string;
|
|
83
|
+
/**
|
|
84
|
+
* Page ID the selection belongs to (for page editor selections)
|
|
85
|
+
*/
|
|
86
|
+
pageId?: string;
|
|
81
87
|
/**
|
|
82
88
|
* Optional short preview for displaying in UI.
|
|
83
89
|
*/
|
|
@@ -86,6 +92,10 @@ export interface ChatContextContent {
|
|
|
86
92
|
type: 'text';
|
|
87
93
|
}
|
|
88
94
|
|
|
95
|
+
// Re-export PageSelection from common for backwards compatibility
|
|
96
|
+
export type { PageSelection } from '../common/pageSelection';
|
|
97
|
+
export { PageSelectionSchema } from '../common/pageSelection';
|
|
98
|
+
|
|
89
99
|
export interface SendMessageParams {
|
|
90
100
|
/**
|
|
91
101
|
* create a thread
|
|
@@ -119,8 +129,14 @@ export interface SendMessageParams {
|
|
|
119
129
|
parentId?: string;
|
|
120
130
|
/**
|
|
121
131
|
* Additional contextual snippets (e.g., text selections) attached to the request.
|
|
132
|
+
* @deprecated Use pageSelections instead for page editor selections
|
|
122
133
|
*/
|
|
123
134
|
contexts?: ChatContextContent[];
|
|
135
|
+
/**
|
|
136
|
+
* Page selections attached to the message (for Ask AI functionality)
|
|
137
|
+
* These will be persisted to the database and injected via context-engine
|
|
138
|
+
*/
|
|
139
|
+
pageSelections?: PageSelection[];
|
|
124
140
|
}
|
|
125
141
|
|
|
126
142
|
export interface SendGroupMessageParams {
|
|
@@ -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;
|