@lobehub/lobehub 2.0.0-next.341 → 2.0.0-next.342
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 +33 -0
- package/changelog/v1.json +5 -0
- package/package.json +1 -1
- package/src/features/PageEditor/Copilot/Toolbar.tsx +1 -1
- package/src/styles/antdOverride.ts +9 -0
- package/src/server/routers/lambda/userMemories/index.ts +0 -13
- package/src/server/routers/lambda/userMemories/reembed.ts +0 -440
- package/src/server/routers/lambda/userMemories/search.ts +0 -117
- package/src/server/routers/lambda/userMemories/shared.ts +0 -63
- package/src/server/routers/lambda/userMemories/tools.ts +0 -410
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.342](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.341...v2.0.0-next.342)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-22**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **userMemories**: Removed un-used code.
|
|
12
|
+
|
|
13
|
+
#### 🐛 Bug Fixes
|
|
14
|
+
|
|
15
|
+
- **copilot**: Pass correct scope when creating new session in PageEditor.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### Code refactoring
|
|
23
|
+
|
|
24
|
+
- **userMemories**: Removed un-used code, closes [#11713](https://github.com/lobehub/lobe-chat/issues/11713) ([89750fc](https://github.com/lobehub/lobe-chat/commit/89750fc))
|
|
25
|
+
|
|
26
|
+
#### What's fixed
|
|
27
|
+
|
|
28
|
+
- **copilot**: Pass correct scope when creating new session in PageEditor, closes [#11714](https://github.com/lobehub/lobe-chat/issues/11714) ([0259270](https://github.com/lobehub/lobe-chat/commit/0259270))
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<div align="right">
|
|
33
|
+
|
|
34
|
+
[](#readme-top)
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
5
38
|
## [Version 2.0.0-next.341](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.340...v2.0.0-next.341)
|
|
6
39
|
|
|
7
40
|
<sup>Released on **2026-01-22**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.342",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -189,7 +189,7 @@ const CopilotToolbar = memo<CopilotToolbarProps>(({ agentId, isHovered }) => {
|
|
|
189
189
|
<div className={cx(styles.fadeContainer, isHovered ? styles.fadeIn : styles.fadeOut)}>
|
|
190
190
|
<ActionIcon
|
|
191
191
|
icon={PlusIcon}
|
|
192
|
-
onClick={() => switchTopic()}
|
|
192
|
+
onClick={() => switchTopic(null, { scope: 'page' })}
|
|
193
193
|
size={DESKTOP_HEADER_ICON_SIZE}
|
|
194
194
|
title={t('actions.addNewTopic')}
|
|
195
195
|
/>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDesktop } from '@lobechat/const';
|
|
1
2
|
import { type Theme, css } from 'antd-style';
|
|
2
3
|
import { rgba } from 'polished';
|
|
3
4
|
|
|
@@ -16,4 +17,12 @@ export default ({ token }: { prefixCls: string; token: Theme }) => css`
|
|
|
16
17
|
background: ${rgba(token.colorBgLayout, 0.5)} !important;
|
|
17
18
|
backdrop-filter: blur(2px);
|
|
18
19
|
}
|
|
20
|
+
|
|
21
|
+
${isDesktop &&
|
|
22
|
+
css`
|
|
23
|
+
.${token.prefixCls}-modal-mask.${token.prefixCls}-modal-mask-blur {
|
|
24
|
+
background: ${rgba(token.colorBgLayout, 0.8)} !important;
|
|
25
|
+
backdrop-filter: none !important;
|
|
26
|
+
}
|
|
27
|
+
`}
|
|
19
28
|
`;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { router } from './shared';
|
|
2
|
-
import { reembedRouter } from './reembed';
|
|
3
|
-
import { searchRouter } from './search';
|
|
4
|
-
import { toolsRouter } from './tools';
|
|
5
|
-
|
|
6
|
-
// Re-export searchUserMemories for potential external use
|
|
7
|
-
export { searchUserMemories } from './search';
|
|
8
|
-
|
|
9
|
-
export const userMemoriesRouter = router({
|
|
10
|
-
...reembedRouter._def.procedures,
|
|
11
|
-
...searchRouter._def.procedures,
|
|
12
|
-
tools: toolsRouter,
|
|
13
|
-
});
|
|
@@ -1,440 +0,0 @@
|
|
|
1
|
-
import { asc, eq, gte, lte } from 'drizzle-orm';
|
|
2
|
-
import pMap from 'p-map';
|
|
3
|
-
import { z } from 'zod';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
userMemories,
|
|
7
|
-
userMemoriesContexts,
|
|
8
|
-
userMemoriesExperiences,
|
|
9
|
-
userMemoriesIdentities,
|
|
10
|
-
userMemoriesPreferences,
|
|
11
|
-
} from '@/database/schemas';
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
EMBEDDING_VECTOR_DIMENSION,
|
|
15
|
-
combineConditions,
|
|
16
|
-
getEmbeddingRuntime,
|
|
17
|
-
memoryProcedure,
|
|
18
|
-
normalizeEmbeddable,
|
|
19
|
-
router,
|
|
20
|
-
} from './shared';
|
|
21
|
-
|
|
22
|
-
const REEMBED_TABLE_KEYS = [
|
|
23
|
-
'userMemories',
|
|
24
|
-
'contexts',
|
|
25
|
-
'preferences',
|
|
26
|
-
'identities',
|
|
27
|
-
'experiences',
|
|
28
|
-
] as const;
|
|
29
|
-
type ReEmbedTableKey = (typeof REEMBED_TABLE_KEYS)[number];
|
|
30
|
-
|
|
31
|
-
const reEmbedInputSchema = z.object({
|
|
32
|
-
concurrency: z.coerce.number().int().min(1).max(50).optional(),
|
|
33
|
-
endDate: z.coerce.date().optional(),
|
|
34
|
-
limit: z.coerce.number().int().min(1).optional(),
|
|
35
|
-
only: z.array(z.enum(REEMBED_TABLE_KEYS)).optional(),
|
|
36
|
-
startDate: z.coerce.date().optional(),
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
interface ReEmbedStats {
|
|
40
|
-
failed: number;
|
|
41
|
-
skipped: number;
|
|
42
|
-
succeeded: number;
|
|
43
|
-
total: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const reembedRouter = router({
|
|
47
|
-
reEmbedMemories: memoryProcedure
|
|
48
|
-
.input(reEmbedInputSchema.optional())
|
|
49
|
-
.mutation(async ({ ctx, input }) => {
|
|
50
|
-
try {
|
|
51
|
-
const options = input ?? {};
|
|
52
|
-
const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
|
|
53
|
-
ctx.serverDB,
|
|
54
|
-
ctx.userId,
|
|
55
|
-
);
|
|
56
|
-
const concurrency = options.concurrency ?? 10;
|
|
57
|
-
const shouldProcess = (key: ReEmbedTableKey) =>
|
|
58
|
-
!options.only || options.only.length === 0 || options.only.includes(key);
|
|
59
|
-
|
|
60
|
-
const embedTexts = async (texts: string[]): Promise<number[][]> => {
|
|
61
|
-
if (texts.length === 0) return [];
|
|
62
|
-
|
|
63
|
-
const response = await agentRuntime.embeddings({
|
|
64
|
-
dimensions: EMBEDDING_VECTOR_DIMENSION,
|
|
65
|
-
input: texts,
|
|
66
|
-
model: embeddingModel,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
if (!response || response.length !== texts.length) {
|
|
70
|
-
throw new Error('Embedding response length mismatch');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return response;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const results: Partial<Record<ReEmbedTableKey, ReEmbedStats>> = {};
|
|
77
|
-
|
|
78
|
-
const run = async (key: ReEmbedTableKey, handler: () => Promise<ReEmbedStats>) => {
|
|
79
|
-
if (!shouldProcess(key)) return;
|
|
80
|
-
results[key] = await handler();
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// Re-embed userMemories
|
|
84
|
-
await run('userMemories', async () => {
|
|
85
|
-
const where = combineConditions([
|
|
86
|
-
eq(userMemories.userId, ctx.userId),
|
|
87
|
-
options.startDate ? gte(userMemories.createdAt, options.startDate) : undefined,
|
|
88
|
-
options.endDate ? lte(userMemories.createdAt, options.endDate) : undefined,
|
|
89
|
-
]);
|
|
90
|
-
|
|
91
|
-
const rows = await ctx.serverDB.query.userMemories.findMany({
|
|
92
|
-
columns: { details: true, id: true, summary: true },
|
|
93
|
-
limit: options.limit,
|
|
94
|
-
orderBy: [asc(userMemories.createdAt)],
|
|
95
|
-
where,
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
let succeeded = 0;
|
|
99
|
-
let failed = 0;
|
|
100
|
-
let skipped = 0;
|
|
101
|
-
|
|
102
|
-
await pMap(
|
|
103
|
-
rows,
|
|
104
|
-
async (row) => {
|
|
105
|
-
const summaryText = normalizeEmbeddable(row.summary);
|
|
106
|
-
const detailsText = normalizeEmbeddable(row.details);
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
if (!summaryText && !detailsText) {
|
|
110
|
-
await ctx.memoryModel.updateUserMemoryVectors(row.id, {
|
|
111
|
-
detailsVector1024: null,
|
|
112
|
-
summaryVector1024: null,
|
|
113
|
-
});
|
|
114
|
-
skipped += 1;
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const inputs: string[] = [];
|
|
119
|
-
if (summaryText) inputs.push(summaryText);
|
|
120
|
-
if (detailsText) inputs.push(detailsText);
|
|
121
|
-
|
|
122
|
-
const embeddings = await embedTexts(inputs);
|
|
123
|
-
let embedIndex = 0;
|
|
124
|
-
|
|
125
|
-
const summaryVector = summaryText ? (embeddings[embedIndex++] ?? null) : null;
|
|
126
|
-
const detailsVector = detailsText ? (embeddings[embedIndex++] ?? null) : null;
|
|
127
|
-
|
|
128
|
-
await ctx.memoryModel.updateUserMemoryVectors(row.id, {
|
|
129
|
-
detailsVector1024: detailsVector,
|
|
130
|
-
summaryVector1024: summaryVector,
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
succeeded += 1;
|
|
134
|
-
} catch (err) {
|
|
135
|
-
failed += 1;
|
|
136
|
-
console.error(
|
|
137
|
-
`[memoryRouter.reEmbed] Failed to re-embed user memory ${row.id}`,
|
|
138
|
-
err,
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
{ concurrency },
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
failed,
|
|
147
|
-
skipped,
|
|
148
|
-
succeeded,
|
|
149
|
-
total: rows.length,
|
|
150
|
-
} satisfies ReEmbedStats;
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// Re-embed contexts
|
|
154
|
-
await run('contexts', async () => {
|
|
155
|
-
const where = combineConditions([
|
|
156
|
-
eq(userMemoriesContexts.userId, ctx.userId),
|
|
157
|
-
options.startDate ? gte(userMemoriesContexts.createdAt, options.startDate) : undefined,
|
|
158
|
-
options.endDate ? lte(userMemoriesContexts.createdAt, options.endDate) : undefined,
|
|
159
|
-
]);
|
|
160
|
-
|
|
161
|
-
const rows = await ctx.serverDB.query.userMemoriesContexts.findMany({
|
|
162
|
-
columns: { description: true, id: true },
|
|
163
|
-
limit: options.limit,
|
|
164
|
-
orderBy: [asc(userMemoriesContexts.createdAt)],
|
|
165
|
-
where,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
let succeeded = 0;
|
|
169
|
-
let failed = 0;
|
|
170
|
-
let skipped = 0;
|
|
171
|
-
|
|
172
|
-
await pMap(
|
|
173
|
-
rows,
|
|
174
|
-
async (row) => {
|
|
175
|
-
const description = normalizeEmbeddable(row.description);
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
if (!description) {
|
|
179
|
-
await ctx.memoryModel.updateContextVectors(row.id, {
|
|
180
|
-
descriptionVector: null,
|
|
181
|
-
});
|
|
182
|
-
skipped += 1;
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const [embedding] = await embedTexts([description]);
|
|
187
|
-
|
|
188
|
-
await ctx.memoryModel.updateContextVectors(row.id, {
|
|
189
|
-
descriptionVector: embedding ?? null,
|
|
190
|
-
});
|
|
191
|
-
succeeded += 1;
|
|
192
|
-
} catch (err) {
|
|
193
|
-
failed += 1;
|
|
194
|
-
console.error(`[memoryRouter.reEmbed] Failed to re-embed context ${row.id}`, err);
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
{ concurrency },
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
failed,
|
|
202
|
-
skipped,
|
|
203
|
-
succeeded,
|
|
204
|
-
total: rows.length,
|
|
205
|
-
} satisfies ReEmbedStats;
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Re-embed preferences
|
|
209
|
-
await run('preferences', async () => {
|
|
210
|
-
const where = combineConditions([
|
|
211
|
-
eq(userMemoriesPreferences.userId, ctx.userId),
|
|
212
|
-
options.startDate
|
|
213
|
-
? gte(userMemoriesPreferences.createdAt, options.startDate)
|
|
214
|
-
: undefined,
|
|
215
|
-
options.endDate ? lte(userMemoriesPreferences.createdAt, options.endDate) : undefined,
|
|
216
|
-
]);
|
|
217
|
-
|
|
218
|
-
const rows = await ctx.serverDB.query.userMemoriesPreferences.findMany({
|
|
219
|
-
columns: { conclusionDirectives: true, id: true },
|
|
220
|
-
limit: options.limit,
|
|
221
|
-
orderBy: [asc(userMemoriesPreferences.createdAt)],
|
|
222
|
-
where,
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
let succeeded = 0;
|
|
226
|
-
let failed = 0;
|
|
227
|
-
let skipped = 0;
|
|
228
|
-
|
|
229
|
-
await pMap(
|
|
230
|
-
rows,
|
|
231
|
-
async (row) => {
|
|
232
|
-
const directives = normalizeEmbeddable(row.conclusionDirectives);
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
if (!directives) {
|
|
236
|
-
await ctx.memoryModel.updatePreferenceVectors(row.id, {
|
|
237
|
-
conclusionDirectivesVector: null,
|
|
238
|
-
});
|
|
239
|
-
skipped += 1;
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const [embedding] = await embedTexts([directives]);
|
|
244
|
-
await ctx.memoryModel.updatePreferenceVectors(row.id, {
|
|
245
|
-
conclusionDirectivesVector: embedding ?? null,
|
|
246
|
-
});
|
|
247
|
-
succeeded += 1;
|
|
248
|
-
} catch (err) {
|
|
249
|
-
failed += 1;
|
|
250
|
-
console.error(
|
|
251
|
-
`[memoryRouter.reEmbed] Failed to re-embed preference ${row.id}`,
|
|
252
|
-
err,
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
},
|
|
256
|
-
{ concurrency },
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
failed,
|
|
261
|
-
skipped,
|
|
262
|
-
succeeded,
|
|
263
|
-
total: rows.length,
|
|
264
|
-
} satisfies ReEmbedStats;
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// Re-embed identities
|
|
268
|
-
await run('identities', async () => {
|
|
269
|
-
const where = combineConditions([
|
|
270
|
-
eq(userMemoriesIdentities.userId, ctx.userId),
|
|
271
|
-
options.startDate
|
|
272
|
-
? gte(userMemoriesIdentities.createdAt, options.startDate)
|
|
273
|
-
: undefined,
|
|
274
|
-
options.endDate ? lte(userMemoriesIdentities.createdAt, options.endDate) : undefined,
|
|
275
|
-
]);
|
|
276
|
-
|
|
277
|
-
const rows = await ctx.serverDB.query.userMemoriesIdentities.findMany({
|
|
278
|
-
columns: { description: true, id: true },
|
|
279
|
-
limit: options.limit,
|
|
280
|
-
orderBy: [asc(userMemoriesIdentities.createdAt)],
|
|
281
|
-
where,
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
let succeeded = 0;
|
|
285
|
-
let failed = 0;
|
|
286
|
-
let skipped = 0;
|
|
287
|
-
|
|
288
|
-
await pMap(
|
|
289
|
-
rows,
|
|
290
|
-
async (row) => {
|
|
291
|
-
const description = normalizeEmbeddable(row.description);
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
if (!description) {
|
|
295
|
-
await ctx.memoryModel.updateIdentityVectors(row.id, {
|
|
296
|
-
descriptionVector: null,
|
|
297
|
-
});
|
|
298
|
-
skipped += 1;
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const [embedding] = await embedTexts([description]);
|
|
303
|
-
await ctx.memoryModel.updateIdentityVectors(row.id, {
|
|
304
|
-
descriptionVector: embedding ?? null,
|
|
305
|
-
});
|
|
306
|
-
succeeded += 1;
|
|
307
|
-
} catch (err) {
|
|
308
|
-
failed += 1;
|
|
309
|
-
console.error(`[memoryRouter.reEmbed] Failed to re-embed identity ${row.id}`, err);
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
{ concurrency },
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
failed,
|
|
317
|
-
skipped,
|
|
318
|
-
succeeded,
|
|
319
|
-
total: rows.length,
|
|
320
|
-
} satisfies ReEmbedStats;
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
// Re-embed experiences
|
|
324
|
-
await run('experiences', async () => {
|
|
325
|
-
const where = combineConditions([
|
|
326
|
-
eq(userMemoriesExperiences.userId, ctx.userId),
|
|
327
|
-
options.startDate
|
|
328
|
-
? gte(userMemoriesExperiences.createdAt, options.startDate)
|
|
329
|
-
: undefined,
|
|
330
|
-
options.endDate ? lte(userMemoriesExperiences.createdAt, options.endDate) : undefined,
|
|
331
|
-
]);
|
|
332
|
-
|
|
333
|
-
const rows = await ctx.serverDB.query.userMemoriesExperiences.findMany({
|
|
334
|
-
columns: { action: true, id: true, keyLearning: true, situation: true },
|
|
335
|
-
limit: options.limit,
|
|
336
|
-
orderBy: [asc(userMemoriesExperiences.createdAt)],
|
|
337
|
-
where,
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
let succeeded = 0;
|
|
341
|
-
let failed = 0;
|
|
342
|
-
let skipped = 0;
|
|
343
|
-
|
|
344
|
-
await pMap(
|
|
345
|
-
rows,
|
|
346
|
-
async (row) => {
|
|
347
|
-
const situation = normalizeEmbeddable(row.situation);
|
|
348
|
-
const action = normalizeEmbeddable(row.action);
|
|
349
|
-
const keyLearning = normalizeEmbeddable(row.keyLearning);
|
|
350
|
-
|
|
351
|
-
try {
|
|
352
|
-
if (!situation && !action && !keyLearning) {
|
|
353
|
-
await ctx.memoryModel.updateExperienceVectors(row.id, {
|
|
354
|
-
actionVector: null,
|
|
355
|
-
keyLearningVector: null,
|
|
356
|
-
situationVector: null,
|
|
357
|
-
});
|
|
358
|
-
skipped += 1;
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const inputs: string[] = [];
|
|
363
|
-
if (situation) inputs.push(situation);
|
|
364
|
-
if (action) inputs.push(action);
|
|
365
|
-
if (keyLearning) inputs.push(keyLearning);
|
|
366
|
-
|
|
367
|
-
const embeddings = await embedTexts(inputs);
|
|
368
|
-
let embedIndex = 0;
|
|
369
|
-
|
|
370
|
-
const situationVector = situation ? (embeddings[embedIndex++] ?? null) : null;
|
|
371
|
-
const actionVector = action ? (embeddings[embedIndex++] ?? null) : null;
|
|
372
|
-
const keyLearningVector = keyLearning ? (embeddings[embedIndex++] ?? null) : null;
|
|
373
|
-
|
|
374
|
-
await ctx.memoryModel.updateExperienceVectors(row.id, {
|
|
375
|
-
actionVector,
|
|
376
|
-
keyLearningVector,
|
|
377
|
-
situationVector,
|
|
378
|
-
});
|
|
379
|
-
succeeded += 1;
|
|
380
|
-
} catch (err) {
|
|
381
|
-
failed += 1;
|
|
382
|
-
console.error(
|
|
383
|
-
`[memoryRouter.reEmbed] Failed to re-embed experience ${row.id}`,
|
|
384
|
-
err,
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
},
|
|
388
|
-
{ concurrency },
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
failed,
|
|
393
|
-
skipped,
|
|
394
|
-
succeeded,
|
|
395
|
-
total: rows.length,
|
|
396
|
-
} satisfies ReEmbedStats;
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
const processedEntries = Object.entries(results) as Array<[ReEmbedTableKey, ReEmbedStats]>;
|
|
400
|
-
|
|
401
|
-
if (processedEntries.length === 0) {
|
|
402
|
-
return {
|
|
403
|
-
message: 'No memory records matched re-embed criteria',
|
|
404
|
-
results,
|
|
405
|
-
success: true,
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const aggregate = processedEntries.reduce(
|
|
410
|
-
(acc, [, stats]) => {
|
|
411
|
-
acc.failed += stats.failed;
|
|
412
|
-
acc.skipped += stats.skipped;
|
|
413
|
-
acc.succeeded += stats.succeeded;
|
|
414
|
-
acc.total += stats.total;
|
|
415
|
-
|
|
416
|
-
return acc;
|
|
417
|
-
},
|
|
418
|
-
{ failed: 0, skipped: 0, succeeded: 0, total: 0 },
|
|
419
|
-
);
|
|
420
|
-
|
|
421
|
-
const message =
|
|
422
|
-
aggregate.total === 0
|
|
423
|
-
? 'No memory records required re-embedding'
|
|
424
|
-
: `Re-embedded ${aggregate.succeeded} of ${aggregate.total} records`;
|
|
425
|
-
|
|
426
|
-
return {
|
|
427
|
-
aggregate,
|
|
428
|
-
message,
|
|
429
|
-
results,
|
|
430
|
-
success: true,
|
|
431
|
-
};
|
|
432
|
-
} catch (error) {
|
|
433
|
-
console.error('Failed to re-embed memories:', error);
|
|
434
|
-
return {
|
|
435
|
-
message: `Failed to re-embed memories: ${(error as Error).message}`,
|
|
436
|
-
success: false,
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
}),
|
|
440
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { type LobeChatDatabase } from '@lobechat/database';
|
|
2
|
-
import { type z } from 'zod';
|
|
3
|
-
|
|
4
|
-
import { DEFAULT_FILE_EMBEDDING_MODEL_ITEM } from '@/const/settings/knowledge';
|
|
5
|
-
import { type UserMemoryModel } from '@/database/models/userMemory';
|
|
6
|
-
import { getServerDefaultFilesConfig } from '@/server/globalConfig';
|
|
7
|
-
import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
|
|
8
|
-
import { type SearchMemoryResult, searchMemorySchema } from '@/types/userMemory';
|
|
9
|
-
|
|
10
|
-
import { EMBEDDING_VECTOR_DIMENSION, memoryProcedure, router } from './shared';
|
|
11
|
-
|
|
12
|
-
type MemorySearchContext = {
|
|
13
|
-
memoryModel: UserMemoryModel;
|
|
14
|
-
serverDB: LobeChatDatabase;
|
|
15
|
-
userId: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type MemorySearchResult = Awaited<ReturnType<UserMemoryModel['searchWithEmbedding']>>;
|
|
19
|
-
|
|
20
|
-
const EMPTY_SEARCH_RESULT: SearchMemoryResult = {
|
|
21
|
-
contexts: [],
|
|
22
|
-
experiences: [],
|
|
23
|
-
preferences: [],
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const mapMemorySearchResult = (layeredResults: MemorySearchResult): SearchMemoryResult => {
|
|
27
|
-
return {
|
|
28
|
-
contexts: layeredResults.contexts.map((context) => ({
|
|
29
|
-
accessedAt: context.accessedAt,
|
|
30
|
-
associatedObjects: context.associatedObjects,
|
|
31
|
-
associatedSubjects: context.associatedSubjects,
|
|
32
|
-
createdAt: context.createdAt,
|
|
33
|
-
currentStatus: context.currentStatus,
|
|
34
|
-
description: context.description,
|
|
35
|
-
id: context.id,
|
|
36
|
-
metadata: context.metadata,
|
|
37
|
-
scoreImpact: context.scoreImpact,
|
|
38
|
-
scoreUrgency: context.scoreUrgency,
|
|
39
|
-
tags: context.tags,
|
|
40
|
-
title: context.title,
|
|
41
|
-
type: context.type,
|
|
42
|
-
updatedAt: context.updatedAt,
|
|
43
|
-
userMemoryIds: Array.isArray(context.userMemoryIds)
|
|
44
|
-
? (context.userMemoryIds as string[])
|
|
45
|
-
: null,
|
|
46
|
-
})),
|
|
47
|
-
experiences: layeredResults.experiences.map((experience) => ({
|
|
48
|
-
accessedAt: experience.accessedAt,
|
|
49
|
-
action: experience.action,
|
|
50
|
-
createdAt: experience.createdAt,
|
|
51
|
-
id: experience.id,
|
|
52
|
-
keyLearning: experience.keyLearning,
|
|
53
|
-
metadata: experience.metadata,
|
|
54
|
-
possibleOutcome: experience.possibleOutcome,
|
|
55
|
-
reasoning: experience.reasoning,
|
|
56
|
-
scoreConfidence: experience.scoreConfidence,
|
|
57
|
-
situation: experience.situation,
|
|
58
|
-
tags: experience.tags,
|
|
59
|
-
type: experience.type,
|
|
60
|
-
updatedAt: experience.updatedAt,
|
|
61
|
-
userMemoryId: experience.userMemoryId,
|
|
62
|
-
})),
|
|
63
|
-
preferences: layeredResults.preferences.map((preference) => ({
|
|
64
|
-
accessedAt: preference.accessedAt,
|
|
65
|
-
conclusionDirectives: preference.conclusionDirectives,
|
|
66
|
-
createdAt: preference.createdAt,
|
|
67
|
-
id: preference.id,
|
|
68
|
-
metadata: preference.metadata,
|
|
69
|
-
scorePriority: preference.scorePriority,
|
|
70
|
-
suggestions: preference.suggestions,
|
|
71
|
-
tags: preference.tags,
|
|
72
|
-
type: preference.type,
|
|
73
|
-
updatedAt: preference.updatedAt,
|
|
74
|
-
userMemoryId: preference.userMemoryId,
|
|
75
|
-
})),
|
|
76
|
-
} satisfies SearchMemoryResult;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const searchUserMemories = async (
|
|
80
|
-
ctx: MemorySearchContext,
|
|
81
|
-
input: z.infer<typeof searchMemorySchema>,
|
|
82
|
-
): Promise<SearchMemoryResult> => {
|
|
83
|
-
const { provider, model: embeddingModel } =
|
|
84
|
-
getServerDefaultFilesConfig().embeddingModel || DEFAULT_FILE_EMBEDDING_MODEL_ITEM;
|
|
85
|
-
// Read user's provider config from database
|
|
86
|
-
const agentRuntime = await initModelRuntimeFromDB(ctx.serverDB, ctx.userId, provider);
|
|
87
|
-
|
|
88
|
-
const queryEmbeddings = await agentRuntime.embeddings({
|
|
89
|
-
dimensions: EMBEDDING_VECTOR_DIMENSION,
|
|
90
|
-
input: input.query,
|
|
91
|
-
model: embeddingModel,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
const limits = {
|
|
95
|
-
contexts: input.topK?.contexts,
|
|
96
|
-
experiences: input.topK?.experiences,
|
|
97
|
-
preferences: input.topK?.preferences,
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const layeredResults = await ctx.memoryModel.searchWithEmbedding({
|
|
101
|
-
embedding: queryEmbeddings?.[0],
|
|
102
|
-
limits,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
return mapMemorySearchResult(layeredResults);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export const searchRouter = router({
|
|
109
|
-
searchMemory: memoryProcedure.input(searchMemorySchema).query(async ({ input, ctx }) => {
|
|
110
|
-
try {
|
|
111
|
-
return await searchUserMemories(ctx, input);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error('Failed to retrieve memories:', error);
|
|
114
|
-
return EMPTY_SEARCH_RESULT;
|
|
115
|
-
}
|
|
116
|
-
}),
|
|
117
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { type LobeChatDatabase } from '@lobechat/database';
|
|
2
|
-
import { type SQL, and } from 'drizzle-orm';
|
|
3
|
-
|
|
4
|
-
import { DEFAULT_FILE_EMBEDDING_MODEL_ITEM } from '@/const/settings/knowledge';
|
|
5
|
-
import { UserMemoryModel } from '@/database/models/userMemory';
|
|
6
|
-
import { authedProcedure } from '@/libs/trpc/lambda';
|
|
7
|
-
import { keyVaults, serverDatabase } from '@/libs/trpc/lambda/middleware';
|
|
8
|
-
import { getServerDefaultFilesConfig } from '@/server/globalConfig';
|
|
9
|
-
import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
|
|
10
|
-
|
|
11
|
-
export const EMBEDDING_VECTOR_DIMENSION = 1024;
|
|
12
|
-
|
|
13
|
-
export const memoryProcedure = authedProcedure
|
|
14
|
-
.use(serverDatabase)
|
|
15
|
-
.use(keyVaults)
|
|
16
|
-
.use(async (opts) => {
|
|
17
|
-
const { ctx } = opts;
|
|
18
|
-
return opts.next({
|
|
19
|
-
ctx: {
|
|
20
|
-
memoryModel: new UserMemoryModel(ctx.serverDB, ctx.userId),
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
export const getEmbeddingRuntime = async (serverDB: LobeChatDatabase, userId: string) => {
|
|
26
|
-
const { provider, model: embeddingModel } =
|
|
27
|
-
getServerDefaultFilesConfig().embeddingModel || DEFAULT_FILE_EMBEDDING_MODEL_ITEM;
|
|
28
|
-
// Read user's provider config from database
|
|
29
|
-
const agentRuntime = await initModelRuntimeFromDB(serverDB, userId, provider);
|
|
30
|
-
|
|
31
|
-
return { agentRuntime, embeddingModel };
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const createEmbedder = (agentRuntime: any, embeddingModel: string) => {
|
|
35
|
-
return async (value?: string | null): Promise<number[] | undefined> => {
|
|
36
|
-
if (!value || value.trim().length === 0) return undefined;
|
|
37
|
-
|
|
38
|
-
const embeddings = await agentRuntime.embeddings({
|
|
39
|
-
dimensions: EMBEDDING_VECTOR_DIMENSION,
|
|
40
|
-
input: value,
|
|
41
|
-
model: embeddingModel,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
return embeddings?.[0];
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const combineConditions = (conditions: Array<SQL | undefined>): SQL | undefined => {
|
|
49
|
-
const filtered = conditions.filter((condition): condition is SQL => condition !== undefined);
|
|
50
|
-
if (filtered.length === 0) return undefined;
|
|
51
|
-
if (filtered.length === 1) return filtered[0];
|
|
52
|
-
|
|
53
|
-
return and(...filtered);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const normalizeEmbeddable = (value?: string | null): string | undefined => {
|
|
57
|
-
if (typeof value !== 'string') return undefined;
|
|
58
|
-
const trimmed = value.trim();
|
|
59
|
-
|
|
60
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export { router } from '@/libs/trpc/lambda';
|
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AddIdentityActionSchema,
|
|
3
|
-
ContextMemoryItemSchema,
|
|
4
|
-
ExperienceMemoryItemSchema,
|
|
5
|
-
PreferenceMemoryItemSchema,
|
|
6
|
-
RemoveIdentityActionSchema,
|
|
7
|
-
UpdateIdentityActionSchema,
|
|
8
|
-
} from '@lobechat/memory-user-memory';
|
|
9
|
-
import { LayersEnum } from '@lobechat/types';
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
type IdentityEntryBasePayload,
|
|
13
|
-
type IdentityEntryPayload,
|
|
14
|
-
UserMemoryModel,
|
|
15
|
-
} from '@/database/models/userMemory';
|
|
16
|
-
import { searchMemorySchema } from '@/types/userMemory';
|
|
17
|
-
|
|
18
|
-
import { searchUserMemories } from './search';
|
|
19
|
-
import { createEmbedder, getEmbeddingRuntime, memoryProcedure, router } from './shared';
|
|
20
|
-
|
|
21
|
-
export const toolsRouter = router({
|
|
22
|
-
addContextMemory: memoryProcedure
|
|
23
|
-
.input(ContextMemoryItemSchema)
|
|
24
|
-
.mutation(async ({ input, ctx }) => {
|
|
25
|
-
try {
|
|
26
|
-
const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
|
|
27
|
-
ctx.serverDB,
|
|
28
|
-
ctx.userId,
|
|
29
|
-
);
|
|
30
|
-
const embed = createEmbedder(agentRuntime, embeddingModel);
|
|
31
|
-
|
|
32
|
-
const summaryEmbedding = await embed(input.summary);
|
|
33
|
-
const detailsEmbedding = await embed(input.details);
|
|
34
|
-
const contextDescriptionEmbedding = await embed(input.withContext.description);
|
|
35
|
-
|
|
36
|
-
const { context, memory } = await ctx.memoryModel.createContextMemory({
|
|
37
|
-
context: {
|
|
38
|
-
associatedObjects:
|
|
39
|
-
UserMemoryModel.parseAssociatedObjects(input.withContext.associatedObjects) ?? null,
|
|
40
|
-
associatedSubjects:
|
|
41
|
-
UserMemoryModel.parseAssociatedSubjects(input.withContext.associatedSubjects) ?? null,
|
|
42
|
-
currentStatus: input.withContext.currentStatus ?? null,
|
|
43
|
-
description: input.withContext.description ?? null,
|
|
44
|
-
descriptionVector: contextDescriptionEmbedding ?? null,
|
|
45
|
-
metadata: {},
|
|
46
|
-
scoreImpact: input.withContext.scoreImpact ?? null,
|
|
47
|
-
scoreUrgency: input.withContext.scoreUrgency ?? null,
|
|
48
|
-
tags: input.tags ?? [],
|
|
49
|
-
title: input.withContext.title ?? null,
|
|
50
|
-
type: input.withContext.type ?? null,
|
|
51
|
-
},
|
|
52
|
-
details: input.details || '',
|
|
53
|
-
detailsEmbedding,
|
|
54
|
-
memoryCategory: input.memoryCategory,
|
|
55
|
-
memoryLayer: LayersEnum.Context,
|
|
56
|
-
memoryType: input.memoryType,
|
|
57
|
-
summary: input.summary,
|
|
58
|
-
summaryEmbedding,
|
|
59
|
-
title: input.title,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
contextId: context.id,
|
|
64
|
-
memoryId: memory.id,
|
|
65
|
-
message: 'Memory saved successfully',
|
|
66
|
-
success: true,
|
|
67
|
-
};
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error('Failed to save memory:', error);
|
|
70
|
-
return {
|
|
71
|
-
message: `Failed to save memory: ${(error as Error).message}`,
|
|
72
|
-
success: false,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
}),
|
|
76
|
-
|
|
77
|
-
addExperienceMemory: memoryProcedure
|
|
78
|
-
.input(ExperienceMemoryItemSchema)
|
|
79
|
-
.mutation(async ({ input, ctx }) => {
|
|
80
|
-
try {
|
|
81
|
-
const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
|
|
82
|
-
ctx.serverDB,
|
|
83
|
-
ctx.userId,
|
|
84
|
-
);
|
|
85
|
-
const embed = createEmbedder(agentRuntime, embeddingModel);
|
|
86
|
-
|
|
87
|
-
const summaryEmbedding = await embed(input.summary);
|
|
88
|
-
const detailsEmbedding = await embed(input.details);
|
|
89
|
-
const situationVector = await embed(input.withExperience.situation);
|
|
90
|
-
const actionVector = await embed(input.withExperience.action);
|
|
91
|
-
const keyLearningVector = await embed(input.withExperience.keyLearning);
|
|
92
|
-
|
|
93
|
-
const { experience, memory } = await ctx.memoryModel.createExperienceMemory({
|
|
94
|
-
details: input.details || '',
|
|
95
|
-
detailsEmbedding,
|
|
96
|
-
experience: {
|
|
97
|
-
action: input.withExperience.action ?? null,
|
|
98
|
-
actionVector: actionVector ?? null,
|
|
99
|
-
keyLearning: input.withExperience.keyLearning ?? null,
|
|
100
|
-
keyLearningVector: keyLearningVector ?? null,
|
|
101
|
-
metadata: {},
|
|
102
|
-
possibleOutcome: input.withExperience.possibleOutcome ?? null,
|
|
103
|
-
reasoning: input.withExperience.reasoning ?? null,
|
|
104
|
-
scoreConfidence: input.withExperience.scoreConfidence ?? null,
|
|
105
|
-
situation: input.withExperience.situation ?? null,
|
|
106
|
-
situationVector: situationVector ?? null,
|
|
107
|
-
tags: input.tags ?? [],
|
|
108
|
-
type: input.memoryType,
|
|
109
|
-
},
|
|
110
|
-
memoryCategory: input.memoryCategory,
|
|
111
|
-
memoryLayer: LayersEnum.Experience,
|
|
112
|
-
memoryType: input.memoryType,
|
|
113
|
-
summary: input.summary,
|
|
114
|
-
summaryEmbedding,
|
|
115
|
-
title: input.title,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
experienceId: experience.id,
|
|
120
|
-
memoryId: memory.id,
|
|
121
|
-
message: 'Memory saved successfully',
|
|
122
|
-
success: true,
|
|
123
|
-
};
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.error('Failed to save memory:', error);
|
|
126
|
-
return {
|
|
127
|
-
message: `Failed to save memory: ${(error as Error).message}`,
|
|
128
|
-
success: false,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
}),
|
|
132
|
-
|
|
133
|
-
addIdentityMemory: memoryProcedure
|
|
134
|
-
.input(AddIdentityActionSchema)
|
|
135
|
-
.mutation(async ({ input, ctx }) => {
|
|
136
|
-
try {
|
|
137
|
-
const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
|
|
138
|
-
ctx.serverDB,
|
|
139
|
-
ctx.userId,
|
|
140
|
-
);
|
|
141
|
-
const embed = createEmbedder(agentRuntime, embeddingModel);
|
|
142
|
-
|
|
143
|
-
const summaryEmbedding = await embed(input.summary);
|
|
144
|
-
const detailsEmbedding = await embed(input.details);
|
|
145
|
-
const descriptionEmbedding = await embed(input.withIdentity.description);
|
|
146
|
-
|
|
147
|
-
const identityMetadata: Record<string, unknown> = {};
|
|
148
|
-
if (
|
|
149
|
-
input.withIdentity.scoreConfidence !== null &&
|
|
150
|
-
input.withIdentity.scoreConfidence !== undefined
|
|
151
|
-
) {
|
|
152
|
-
identityMetadata.scoreConfidence = input.withIdentity.scoreConfidence;
|
|
153
|
-
}
|
|
154
|
-
if (
|
|
155
|
-
input.withIdentity.sourceEvidence !== null &&
|
|
156
|
-
input.withIdentity.sourceEvidence !== undefined
|
|
157
|
-
) {
|
|
158
|
-
identityMetadata.sourceEvidence = input.withIdentity.sourceEvidence;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const { identityId, userMemoryId } = await ctx.memoryModel.addIdentityEntry({
|
|
162
|
-
base: {
|
|
163
|
-
details: input.details,
|
|
164
|
-
detailsVector1024: detailsEmbedding ?? null,
|
|
165
|
-
memoryCategory: input.memoryCategory,
|
|
166
|
-
memoryLayer: LayersEnum.Identity,
|
|
167
|
-
memoryType: input.memoryType,
|
|
168
|
-
metadata: Object.keys(identityMetadata).length > 0 ? identityMetadata : undefined,
|
|
169
|
-
summary: input.summary,
|
|
170
|
-
summaryVector1024: summaryEmbedding ?? null,
|
|
171
|
-
tags: input.tags,
|
|
172
|
-
title: input.title,
|
|
173
|
-
},
|
|
174
|
-
identity: {
|
|
175
|
-
description: input.withIdentity.description,
|
|
176
|
-
descriptionVector: descriptionEmbedding ?? null,
|
|
177
|
-
episodicDate: input.withIdentity.episodicDate,
|
|
178
|
-
metadata: Object.keys(identityMetadata).length > 0 ? identityMetadata : undefined,
|
|
179
|
-
relationship: input.withIdentity.relationship,
|
|
180
|
-
role: input.withIdentity.role,
|
|
181
|
-
tags: input.tags,
|
|
182
|
-
type: input.withIdentity.type,
|
|
183
|
-
},
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
identityId,
|
|
188
|
-
memoryId: userMemoryId,
|
|
189
|
-
message: 'Identity memory saved successfully',
|
|
190
|
-
success: true,
|
|
191
|
-
};
|
|
192
|
-
} catch (error) {
|
|
193
|
-
console.error('Failed to save identity memory:', error);
|
|
194
|
-
return {
|
|
195
|
-
message: `Failed to save identity memory: ${(error as Error).message}`,
|
|
196
|
-
success: false,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
}),
|
|
200
|
-
|
|
201
|
-
addPreferenceMemory: memoryProcedure
|
|
202
|
-
.input(PreferenceMemoryItemSchema)
|
|
203
|
-
.mutation(async ({ input, ctx }) => {
|
|
204
|
-
try {
|
|
205
|
-
const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
|
|
206
|
-
ctx.serverDB,
|
|
207
|
-
ctx.userId,
|
|
208
|
-
);
|
|
209
|
-
const embed = createEmbedder(agentRuntime, embeddingModel);
|
|
210
|
-
|
|
211
|
-
const summaryEmbedding = await embed(input.summary);
|
|
212
|
-
const detailsEmbedding = await embed(input.details);
|
|
213
|
-
const conclusionVector = await embed(input.withPreference.conclusionDirectives);
|
|
214
|
-
|
|
215
|
-
const suggestionsText =
|
|
216
|
-
input.withPreference?.suggestions?.length && input.withPreference?.suggestions?.length > 0
|
|
217
|
-
? input.withPreference?.suggestions?.join('\n')
|
|
218
|
-
: null;
|
|
219
|
-
|
|
220
|
-
const metadata = {
|
|
221
|
-
appContext: input.withPreference.appContext,
|
|
222
|
-
extractedScopes: input.withPreference.extractedScopes,
|
|
223
|
-
originContext: input.withPreference.originContext,
|
|
224
|
-
} satisfies Record<string, unknown>;
|
|
225
|
-
|
|
226
|
-
const { memory, preference } = await ctx.memoryModel.createPreferenceMemory({
|
|
227
|
-
details: input.details || '',
|
|
228
|
-
detailsEmbedding,
|
|
229
|
-
memoryCategory: input.memoryCategory,
|
|
230
|
-
memoryLayer: LayersEnum.Preference,
|
|
231
|
-
memoryType: input.memoryType,
|
|
232
|
-
preference: {
|
|
233
|
-
conclusionDirectives: input.withPreference.conclusionDirectives || '',
|
|
234
|
-
conclusionDirectivesVector: conclusionVector ?? null,
|
|
235
|
-
metadata,
|
|
236
|
-
scorePriority: input.withPreference.scorePriority ?? null,
|
|
237
|
-
suggestions: suggestionsText,
|
|
238
|
-
tags: input.tags,
|
|
239
|
-
type: input.memoryType,
|
|
240
|
-
},
|
|
241
|
-
summary: input.summary,
|
|
242
|
-
summaryEmbedding,
|
|
243
|
-
title: input.title,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
return {
|
|
247
|
-
memoryId: memory.id,
|
|
248
|
-
message: 'Memory saved successfully',
|
|
249
|
-
preferenceId: preference.id,
|
|
250
|
-
success: true,
|
|
251
|
-
};
|
|
252
|
-
} catch (error) {
|
|
253
|
-
console.error('Failed to save memory:', error);
|
|
254
|
-
return {
|
|
255
|
-
message: `Failed to save memory: ${(error as Error).message}`,
|
|
256
|
-
success: false,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
}),
|
|
260
|
-
|
|
261
|
-
removeIdentityMemory: memoryProcedure
|
|
262
|
-
.input(RemoveIdentityActionSchema)
|
|
263
|
-
.mutation(async ({ input, ctx }) => {
|
|
264
|
-
try {
|
|
265
|
-
const removed = await ctx.memoryModel.removeIdentityEntry(input.id);
|
|
266
|
-
|
|
267
|
-
if (!removed) {
|
|
268
|
-
return {
|
|
269
|
-
message: 'Identity memory not found',
|
|
270
|
-
success: false,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
identityId: input.id,
|
|
276
|
-
message: 'Identity memory removed successfully',
|
|
277
|
-
reason: input.reason,
|
|
278
|
-
success: true,
|
|
279
|
-
};
|
|
280
|
-
} catch (error) {
|
|
281
|
-
console.error('Failed to remove identity memory:', error);
|
|
282
|
-
return {
|
|
283
|
-
message: `Failed to remove identity memory: ${(error as Error).message}`,
|
|
284
|
-
success: false,
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
}),
|
|
288
|
-
|
|
289
|
-
searchMemory: memoryProcedure.input(searchMemorySchema).query(async ({ input, ctx }) => {
|
|
290
|
-
try {
|
|
291
|
-
return await searchUserMemories(ctx, input);
|
|
292
|
-
} catch (error) {
|
|
293
|
-
console.error('Failed to retrieve memories:', error);
|
|
294
|
-
return { contexts: [], experiences: [], preferences: [] };
|
|
295
|
-
}
|
|
296
|
-
}),
|
|
297
|
-
|
|
298
|
-
updateIdentityMemory: memoryProcedure
|
|
299
|
-
.input(UpdateIdentityActionSchema)
|
|
300
|
-
.mutation(async ({ input, ctx }) => {
|
|
301
|
-
try {
|
|
302
|
-
const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
|
|
303
|
-
ctx.serverDB,
|
|
304
|
-
ctx.userId,
|
|
305
|
-
);
|
|
306
|
-
const embed = createEmbedder(agentRuntime, embeddingModel);
|
|
307
|
-
|
|
308
|
-
let summaryVector1024: number[] | null | undefined;
|
|
309
|
-
if (input.set.summary !== undefined) {
|
|
310
|
-
const vector = await embed(input.set.summary);
|
|
311
|
-
summaryVector1024 = vector ?? null;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
let detailsVector1024: number[] | null | undefined;
|
|
315
|
-
if (input.set.details !== undefined) {
|
|
316
|
-
const vector = await embed(input.set.details);
|
|
317
|
-
detailsVector1024 = vector ?? null;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
let descriptionVector: number[] | null | undefined;
|
|
321
|
-
if (input.set.withIdentity.description !== undefined) {
|
|
322
|
-
const vector = await embed(input.set.withIdentity.description);
|
|
323
|
-
descriptionVector = vector ?? null;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const metadataUpdates: Record<string, unknown> = {};
|
|
327
|
-
if (Object.hasOwn(input.set.withIdentity, 'scoreConfidence')) {
|
|
328
|
-
metadataUpdates.scoreConfidence = input.set.withIdentity.scoreConfidence ?? null;
|
|
329
|
-
}
|
|
330
|
-
if (Object.hasOwn(input.set.withIdentity, 'sourceEvidence')) {
|
|
331
|
-
metadataUpdates.sourceEvidence = input.set.withIdentity.sourceEvidence ?? null;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const identityPayload: Partial<IdentityEntryPayload> = {};
|
|
335
|
-
if (input.set.withIdentity.description !== undefined) {
|
|
336
|
-
identityPayload.description = input.set.withIdentity.description;
|
|
337
|
-
identityPayload.descriptionVector = descriptionVector;
|
|
338
|
-
}
|
|
339
|
-
if (input.set.withIdentity.episodicDate !== undefined) {
|
|
340
|
-
identityPayload.episodicDate = input.set.withIdentity.episodicDate;
|
|
341
|
-
}
|
|
342
|
-
if (input.set.withIdentity.relationship !== undefined) {
|
|
343
|
-
identityPayload.relationship = input.set.withIdentity.relationship;
|
|
344
|
-
}
|
|
345
|
-
if (input.set.withIdentity.role !== undefined) {
|
|
346
|
-
identityPayload.role = input.set.withIdentity.role;
|
|
347
|
-
}
|
|
348
|
-
if (input.set.tags !== undefined) {
|
|
349
|
-
identityPayload.tags = input.set.tags;
|
|
350
|
-
}
|
|
351
|
-
if (input.set.withIdentity.type !== undefined) {
|
|
352
|
-
identityPayload.type = input.set.withIdentity.type;
|
|
353
|
-
}
|
|
354
|
-
if (Object.keys(metadataUpdates).length > 0) {
|
|
355
|
-
identityPayload.metadata = metadataUpdates;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const basePayload: Partial<IdentityEntryBasePayload> = {};
|
|
359
|
-
if (input.set.details !== undefined) {
|
|
360
|
-
basePayload.details = input.set.details;
|
|
361
|
-
basePayload.detailsVector1024 = detailsVector1024;
|
|
362
|
-
}
|
|
363
|
-
if (input.set.memoryCategory !== undefined) {
|
|
364
|
-
basePayload.memoryCategory = input.set.memoryCategory;
|
|
365
|
-
}
|
|
366
|
-
if (input.set.memoryType !== undefined) {
|
|
367
|
-
basePayload.memoryType = input.set.memoryType;
|
|
368
|
-
}
|
|
369
|
-
if (input.set.summary !== undefined) {
|
|
370
|
-
basePayload.summary = input.set.summary;
|
|
371
|
-
basePayload.summaryVector1024 = summaryVector1024;
|
|
372
|
-
}
|
|
373
|
-
if (input.set.tags !== undefined) {
|
|
374
|
-
basePayload.tags = input.set.tags;
|
|
375
|
-
}
|
|
376
|
-
if (input.set.title !== undefined) {
|
|
377
|
-
basePayload.title = input.set.title;
|
|
378
|
-
}
|
|
379
|
-
if (Object.keys(metadataUpdates).length > 0) {
|
|
380
|
-
basePayload.metadata = metadataUpdates;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
const updated = await ctx.memoryModel.updateIdentityEntry({
|
|
384
|
-
base: Object.keys(basePayload).length > 0 ? basePayload : undefined,
|
|
385
|
-
identity: Object.keys(identityPayload).length > 0 ? identityPayload : undefined,
|
|
386
|
-
identityId: input.id,
|
|
387
|
-
mergeStrategy: input.mergeStrategy,
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
if (!updated) {
|
|
391
|
-
return {
|
|
392
|
-
message: 'Identity memory not found',
|
|
393
|
-
success: false,
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
identityId: input.id,
|
|
399
|
-
message: 'Identity memory updated successfully',
|
|
400
|
-
success: true,
|
|
401
|
-
};
|
|
402
|
-
} catch (error) {
|
|
403
|
-
console.error('Failed to update identity memory:', error);
|
|
404
|
-
return {
|
|
405
|
-
message: `Failed to update identity memory: ${(error as Error).message}`,
|
|
406
|
-
success: false,
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
}),
|
|
410
|
-
});
|