@lobehub/lobehub 2.0.0-next.341 → 2.0.0-next.343

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/changelog/v1.json +17 -0
  3. package/package.json +1 -1
  4. package/packages/database/src/models/__tests__/userMemories.test.ts +62 -5
  5. package/packages/database/src/models/agentCronJob.ts +9 -9
  6. package/packages/database/src/models/userMemory/__tests__/identity.test.ts +5 -5
  7. package/packages/database/src/models/userMemory/experience.ts +91 -1
  8. package/packages/database/src/models/userMemory/identity.ts +93 -2
  9. package/packages/database/src/models/userMemory/model.ts +27 -8
  10. package/packages/types/src/userMemory/experience.ts +25 -0
  11. package/packages/types/src/userMemory/identity.ts +27 -0
  12. package/packages/types/src/userMemory/index.ts +1 -0
  13. package/packages/types/src/userMemory/shared.ts +30 -0
  14. package/src/app/[variants]/(main)/group/profile/features/Header/index.tsx +3 -4
  15. package/src/app/[variants]/(main)/home/features/InputArea/SkillInstallBanner.tsx +7 -8
  16. package/src/app/[variants]/(main)/memory/(home)/features/Persona/PersonaDetail.tsx +58 -0
  17. package/src/app/[variants]/(main)/memory/(home)/features/Persona/PersonaHeader.tsx +22 -0
  18. package/src/app/[variants]/(main)/memory/(home)/features/Persona/PersonaSummary.tsx +43 -0
  19. package/src/app/[variants]/(main)/memory/(home)/features/Persona/index.tsx +53 -0
  20. package/src/app/[variants]/(main)/memory/(home)/features/RoleTagCloud/index.tsx +2 -2
  21. package/src/app/[variants]/(main)/memory/(home)/index.tsx +15 -3
  22. package/src/app/[variants]/(main)/memory/experiences/features/List/GridView/ExperienceCard.tsx +3 -3
  23. package/src/app/[variants]/(main)/memory/experiences/features/List/GridView/index.tsx +3 -3
  24. package/src/app/[variants]/(main)/memory/experiences/features/List/TimelineView/ExperienceCard.tsx +3 -3
  25. package/src/app/[variants]/(main)/memory/experiences/features/List/TimelineView/index.tsx +3 -3
  26. package/src/app/[variants]/(main)/memory/features/SourceLink.tsx +2 -11
  27. package/src/app/[variants]/(main)/memory/features/TimeLineView/TimeLineCard.tsx +2 -9
  28. package/src/app/[variants]/(main)/memory/identities/features/IdentityRightPanel.tsx +1 -1
  29. package/src/app/[variants]/(main)/memory/identities/features/List/GridView/IdentityCard.tsx +5 -4
  30. package/src/app/[variants]/(main)/memory/identities/features/List/GridView/index.tsx +3 -3
  31. package/src/app/[variants]/(main)/memory/identities/features/List/TimelineView/IdentityCard.tsx +6 -6
  32. package/src/app/[variants]/(main)/memory/identities/features/List/TimelineView/index.tsx +6 -4
  33. package/src/app/[variants]/(main)/settings/profile/index.tsx +8 -8
  34. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +0 -1
  35. package/src/app/[variants]/(main)/settings/skill/features/Actions.tsx +0 -1
  36. package/src/app/[variants]/(main)/settings/skill/features/KlavisSkillItem.tsx +9 -10
  37. package/src/app/[variants]/(main)/settings/skill/features/LobehubSkillItem.tsx +9 -10
  38. package/src/app/[variants]/(main)/settings/skill/features/McpSkillItem.tsx +4 -5
  39. package/src/app/[variants]/(main)/settings/skill/features/SkillList.tsx +4 -5
  40. package/src/app/[variants]/share/t/[id]/SharedMessageList.tsx +1 -4
  41. package/src/app/[variants]/share/t/[id]/_layout/index.tsx +47 -121
  42. package/src/app/[variants]/share/t/[id]/_layout/style.ts +59 -0
  43. package/src/app/[variants]/share/t/[id]/features/Portal/index.tsx +4 -5
  44. package/src/app/[variants]/share/t/[id]/index.tsx +30 -37
  45. package/src/components/404/index.tsx +15 -9
  46. package/src/components/DragUpload/index.tsx +15 -16
  47. package/src/features/EditorCanvas/DocumentIdMode.tsx +1 -2
  48. package/src/features/IntegrationDetailModal/index.tsx +11 -12
  49. package/src/features/PageEditor/Copilot/Toolbar.tsx +1 -1
  50. package/src/features/ResourceManager/index.tsx +13 -6
  51. package/src/features/ShareModal/ShareImage/Preview.tsx +19 -28
  52. package/src/features/ShareModal/ShareImage/style.ts +4 -2
  53. package/src/features/ShareModal/index.tsx +5 -1
  54. package/src/features/ShareModal/style.ts +1 -0
  55. package/src/features/ShareModal/useContainerStyles.ts +1 -1
  56. package/src/features/SharePopover/index.tsx +16 -9
  57. package/src/features/SharePopover/style.ts +2 -2
  58. package/src/features/SkillStore/CommunityList/Item.tsx +2 -2
  59. package/src/features/SkillStore/LobeHubList/Item.tsx +2 -2
  60. package/src/features/SkillStore/LobeHubList/index.tsx +2 -3
  61. package/src/features/SkillStore/style.ts +4 -4
  62. package/src/layout/GlobalProvider/ServerVersionOutdatedAlert.tsx +28 -20
  63. package/src/server/routers/lambda/userMemories.ts +61 -5
  64. package/src/server/routers/lambda/userMemory.ts +5 -1
  65. package/src/services/chat/index.ts +2 -2
  66. package/src/services/userMemory/index.ts +25 -1
  67. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +0 -1
  68. package/src/store/userMemory/initialState.ts +22 -52
  69. package/src/store/userMemory/slices/context/action.ts +1 -1
  70. package/src/store/userMemory/slices/context/index.ts +1 -0
  71. package/src/store/userMemory/slices/context/initialState.ts +22 -0
  72. package/src/store/userMemory/slices/experience/action.ts +10 -22
  73. package/src/store/userMemory/slices/experience/index.ts +1 -0
  74. package/src/store/userMemory/slices/experience/initialState.ts +22 -0
  75. package/src/store/userMemory/slices/home/action.ts +17 -0
  76. package/src/store/userMemory/slices/identity/action.ts +36 -24
  77. package/src/store/userMemory/slices/identity/initialState.ts +7 -4
  78. package/src/store/userMemory/slices/preference/action.ts +1 -1
  79. package/src/store/userMemory/slices/preference/index.ts +1 -0
  80. package/src/store/userMemory/slices/preference/initialState.ts +22 -0
  81. package/src/styles/antdOverride.ts +9 -0
  82. package/src/server/routers/lambda/userMemories/index.ts +0 -13
  83. package/src/server/routers/lambda/userMemories/reembed.ts +0 -440
  84. package/src/server/routers/lambda/userMemories/search.ts +0 -117
  85. package/src/server/routers/lambda/userMemories/shared.ts +0 -63
  86. package/src/server/routers/lambda/userMemories/tools.ts +0 -410
@@ -1,7 +1,6 @@
1
1
  import {
2
- LayersEnum,
2
+ type IdentityListResult,
3
3
  type NewUserMemoryIdentity,
4
- type TypesEnum,
5
4
  type UpdateUserMemoryIdentity,
6
5
  } from '@lobechat/types';
7
6
  import { uniqBy } from 'es-toolkit/compat';
@@ -21,7 +20,9 @@ export interface IdentityQueryParams {
21
20
  page?: number;
22
21
  pageSize?: number;
23
22
  q?: string;
24
- types?: TypesEnum[];
23
+ relationships?: string[];
24
+ sort?: 'capturedAt' | 'type';
25
+ types?: string[];
25
26
  }
26
27
 
27
28
  export interface IdentityAction {
@@ -30,7 +31,7 @@ export interface IdentityAction {
30
31
  loadMoreIdentities: () => void;
31
32
  resetIdentitiesList: (params?: Omit<IdentityQueryParams, 'page' | 'pageSize'>) => void;
32
33
  updateIdentity: (id: string, data: UpdateUserMemoryIdentity) => Promise<boolean>;
33
- useFetchIdentities: (params: IdentityQueryParams) => SWRResponse<any>;
34
+ useFetchIdentities: (params: IdentityQueryParams) => SWRResponse<IdentityListResult>;
34
35
  }
35
36
 
36
37
  export const createIdentitySlice: StateCreator<
@@ -42,14 +43,24 @@ export const createIdentitySlice: StateCreator<
42
43
  createIdentity: async (data) => {
43
44
  const result = await memoryCRUDService.createIdentity(data);
44
45
  // Reset list to refresh
45
- get().resetIdentitiesList({ q: get().identitiesQuery, types: get().identitiesTypes });
46
+ get().resetIdentitiesList({
47
+ q: get().identitiesQuery,
48
+ relationships: get().identitiesRelationships,
49
+ sort: get().identitiesSort,
50
+ types: get().identitiesTypes,
51
+ });
46
52
  return result;
47
53
  },
48
54
 
49
55
  deleteIdentity: async (id) => {
50
56
  await memoryCRUDService.deleteIdentity(id);
51
57
  // Reset list to refresh
52
- get().resetIdentitiesList({ q: get().identitiesQuery, types: get().identitiesTypes });
58
+ get().resetIdentitiesList({
59
+ q: get().identitiesQuery,
60
+ relationships: get().identitiesRelationships,
61
+ sort: get().identitiesSort,
62
+ types: get().identitiesTypes,
63
+ });
53
64
  },
54
65
 
55
66
  loadMoreIdentities: () => {
@@ -71,7 +82,9 @@ export const createIdentitySlice: StateCreator<
71
82
  draft.identities = [];
72
83
  draft.identitiesPage = 1;
73
84
  draft.identitiesQuery = params?.q;
85
+ draft.identitiesRelationships = params?.relationships;
74
86
  draft.identitiesSearchLoading = true;
87
+ draft.identitiesSort = params?.sort;
75
88
  draft.identitiesTypes = params?.types;
76
89
  }),
77
90
  false,
@@ -82,7 +95,12 @@ export const createIdentitySlice: StateCreator<
82
95
  updateIdentity: async (id, data) => {
83
96
  const result = await memoryCRUDService.updateIdentity(id, data);
84
97
  // Reset list to refresh
85
- get().resetIdentitiesList({ q: get().identitiesQuery, types: get().identitiesTypes });
98
+ get().resetIdentitiesList({
99
+ q: get().identitiesQuery,
100
+ relationships: get().identitiesRelationships,
101
+ sort: get().identitiesSort,
102
+ types: get().identitiesTypes,
103
+ });
86
104
  return result;
87
105
  },
88
106
 
@@ -92,6 +110,8 @@ export const createIdentitySlice: StateCreator<
92
110
  params.page,
93
111
  params.pageSize,
94
112
  params.q,
113
+ params.relationships?.join(','),
114
+ params.sort,
95
115
  params.types?.join(','),
96
116
  ];
97
117
  const swrKey = swrKeyParts
@@ -102,42 +122,34 @@ export const createIdentitySlice: StateCreator<
102
122
  return useSWR(
103
123
  swrKey,
104
124
  async () => {
105
- return await userMemoryService.queryMemories({
106
- layer: LayersEnum.Identity,
125
+ // Use the new dedicated queryIdentities API
126
+ return userMemoryService.queryIdentities({
107
127
  page: params.page,
108
128
  pageSize: params.pageSize,
109
129
  q: params.q,
130
+ relationships: params.relationships,
131
+ sort: params.sort,
110
132
  types: params.types,
111
133
  });
112
134
  },
113
135
  {
114
- onSuccess(data: any) {
136
+ onSuccess(data: IdentityListResult) {
115
137
  set(
116
138
  produce((draft) => {
117
139
  draft.identitiesSearchLoading = false;
140
+ draft.identitiesTotal = data.total;
118
141
 
119
- // 设置基础信息
120
142
  if (!draft.identitiesInit) {
121
143
  draft.identitiesInit = true;
122
- draft.identitiesTotal = data.total;
123
144
  }
124
145
 
125
- // 转换数据结构
126
- const transformedItems = data.items.map((item: any) => ({
127
- ...item.memory,
128
- ...item.identity,
129
- }));
130
-
131
- // 累积数据逻辑
146
+ // Backend now returns flat structure directly, no transformation needed
132
147
  if (page === 1) {
133
- // 第一页,直接设置
134
- draft.identities = uniqBy(transformedItems, 'id');
148
+ draft.identities = uniqBy(data.items, 'id');
135
149
  } else {
136
- // 后续页面,累积数据
137
- draft.identities = uniqBy([...draft.identities, ...transformedItems], 'id');
150
+ draft.identities = uniqBy([...draft.identities, ...data.items], 'id');
138
151
  }
139
152
 
140
- // 更新 hasMore
141
153
  draft.identitiesHasMore = data.items.length >= (params.pageSize || 20);
142
154
  }),
143
155
  false,
@@ -1,5 +1,4 @@
1
- import { type DisplayIdentityMemory } from '@/database/repositories/userMemory';
2
- import type { TypesEnum } from '@/types/userMemory';
1
+ import type { IdentityListItem, IdentityListSort } from '@lobechat/types';
3
2
 
4
3
  import type { IdentityForInjection } from '../../types';
5
4
 
@@ -10,14 +9,16 @@ export interface IdentitySliceState {
10
9
  globalIdentitiesFetchedAt?: number;
11
10
  /** Whether global identities have been initialized */
12
11
  globalIdentitiesInit: boolean;
13
- identities: DisplayIdentityMemory[];
12
+ identities: IdentityListItem[];
14
13
  identitiesHasMore: boolean;
15
14
  identitiesInit: boolean;
16
15
  identitiesPage: number;
17
16
  identitiesQuery?: string;
17
+ identitiesRelationships?: string[];
18
18
  identitiesSearchLoading?: boolean;
19
+ identitiesSort?: IdentityListSort;
19
20
  identitiesTotal: number;
20
- identitiesTypes?: TypesEnum[];
21
+ identitiesTypes?: string[];
21
22
  }
22
23
 
23
24
  export const identityInitialState: IdentitySliceState = {
@@ -29,6 +30,8 @@ export const identityInitialState: IdentitySliceState = {
29
30
  identitiesInit: false,
30
31
  identitiesPage: 1,
31
32
  identitiesQuery: undefined,
33
+ identitiesRelationships: undefined,
34
+ identitiesSort: undefined,
32
35
  identitiesTotal: 0,
33
36
  identitiesTypes: undefined,
34
37
  };
@@ -16,7 +16,7 @@ export interface PreferenceQueryParams {
16
16
  page?: number;
17
17
  pageSize?: number;
18
18
  q?: string;
19
- sort?: 'scorePriority';
19
+ sort?: 'capturedAt' | 'scorePriority';
20
20
  }
21
21
 
22
22
  export interface PreferenceAction {
@@ -1 +1,2 @@
1
1
  export { createPreferenceSlice, type PreferenceAction } from './action';
2
+ export { preferenceInitialState, type PreferenceSliceState } from './initialState';
@@ -0,0 +1,22 @@
1
+ import { type DisplayPreferenceMemory } from '@/database/repositories/userMemory';
2
+
3
+ export interface PreferenceSliceState {
4
+ preferences: DisplayPreferenceMemory[];
5
+ preferencesHasMore: boolean;
6
+ preferencesInit: boolean;
7
+ preferencesPage: number;
8
+ preferencesQuery?: string;
9
+ preferencesSearchLoading?: boolean;
10
+ preferencesSort?: 'capturedAt' | 'scorePriority';
11
+ preferencesTotal: number;
12
+ }
13
+
14
+ export const preferenceInitialState: PreferenceSliceState = {
15
+ preferences: [],
16
+ preferencesHasMore: true,
17
+ preferencesInit: false,
18
+ preferencesPage: 1,
19
+ preferencesQuery: undefined,
20
+ preferencesSort: undefined,
21
+ preferencesTotal: 0,
22
+ };
@@ -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
- });