@lobehub/lobehub 2.0.0-next.201 → 2.0.0-next.203

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 (110) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/chat.json +2 -0
  4. package/locales/ar/models.json +104 -6
  5. package/locales/ar/plugin.json +2 -1
  6. package/locales/bg-BG/chat.json +2 -0
  7. package/locales/bg-BG/models.json +46 -4
  8. package/locales/bg-BG/plugin.json +2 -1
  9. package/locales/de-DE/chat.json +2 -0
  10. package/locales/de-DE/models.json +74 -4
  11. package/locales/de-DE/plugin.json +2 -1
  12. package/locales/en-US/chat.json +2 -0
  13. package/locales/en-US/plugin.json +2 -1
  14. package/locales/es-ES/chat.json +2 -0
  15. package/locales/es-ES/models.json +129 -4
  16. package/locales/es-ES/plugin.json +2 -1
  17. package/locales/fa-IR/chat.json +2 -0
  18. package/locales/fa-IR/models.json +80 -4
  19. package/locales/fa-IR/plugin.json +2 -1
  20. package/locales/fr-FR/chat.json +2 -0
  21. package/locales/fr-FR/models.json +67 -7
  22. package/locales/fr-FR/plugin.json +2 -1
  23. package/locales/it-IT/chat.json +2 -0
  24. package/locales/it-IT/models.json +39 -6
  25. package/locales/it-IT/plugin.json +2 -1
  26. package/locales/ja-JP/chat.json +2 -0
  27. package/locales/ja-JP/models.json +123 -7
  28. package/locales/ja-JP/plugin.json +2 -1
  29. package/locales/ko-KR/chat.json +2 -0
  30. package/locales/ko-KR/models.json +104 -5
  31. package/locales/ko-KR/plugin.json +2 -1
  32. package/locales/nl-NL/chat.json +2 -0
  33. package/locales/nl-NL/models.json +62 -5
  34. package/locales/nl-NL/plugin.json +2 -1
  35. package/locales/pl-PL/chat.json +2 -0
  36. package/locales/pl-PL/models.json +110 -0
  37. package/locales/pl-PL/plugin.json +2 -1
  38. package/locales/pt-BR/chat.json +2 -0
  39. package/locales/pt-BR/models.json +81 -5
  40. package/locales/pt-BR/plugin.json +2 -1
  41. package/locales/ru-RU/chat.json +2 -0
  42. package/locales/ru-RU/models.json +33 -6
  43. package/locales/ru-RU/plugin.json +2 -1
  44. package/locales/tr-TR/chat.json +2 -0
  45. package/locales/tr-TR/models.json +26 -7
  46. package/locales/tr-TR/plugin.json +2 -1
  47. package/locales/vi-VN/chat.json +2 -0
  48. package/locales/vi-VN/models.json +59 -4
  49. package/locales/vi-VN/plugin.json +2 -1
  50. package/locales/zh-CN/chat.json +2 -0
  51. package/locales/zh-CN/models.json +141 -5
  52. package/locales/zh-CN/plugin.json +2 -1
  53. package/locales/zh-TW/chat.json +2 -0
  54. package/locales/zh-TW/models.json +96 -7
  55. package/locales/zh-TW/plugin.json +2 -1
  56. package/package.json +1 -1
  57. package/packages/builtin-tool-gtd/src/client/Inspector/ExecTask/index.tsx +30 -15
  58. package/packages/builtin-tool-gtd/src/manifest.ts +1 -1
  59. package/packages/model-runtime/src/core/ModelRuntime.test.ts +44 -86
  60. package/packages/types/src/aiChat.ts +0 -1
  61. package/packages/types/src/message/ui/chat.ts +1 -1
  62. package/src/app/(backend)/middleware/auth/index.ts +16 -2
  63. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +30 -15
  64. package/src/app/(backend)/webapi/chat/[provider]/route.ts +44 -40
  65. package/src/app/(backend)/webapi/models/[provider]/pull/route.ts +4 -3
  66. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +36 -13
  67. package/src/app/(backend)/webapi/models/[provider]/route.ts +4 -11
  68. package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/index.tsx +21 -23
  69. package/src/features/Conversation/Messages/AssistantGroup/components/ContentBlock.tsx +16 -3
  70. package/src/features/Conversation/Messages/Task/TaskDetailPanel/index.tsx +17 -20
  71. package/src/features/Conversation/Messages/Tasks/shared/ErrorState.tsx +16 -11
  72. package/src/features/Conversation/Messages/Tasks/shared/InitializingState.tsx +6 -20
  73. package/src/features/Conversation/Messages/Tasks/shared/ProcessingState.tsx +10 -20
  74. package/src/features/User/DataStatistics.tsx +4 -4
  75. package/src/hooks/useQueryParam.ts +0 -2
  76. package/src/libs/trpc/async/asyncAuth.ts +0 -2
  77. package/src/libs/trpc/async/context.ts +3 -11
  78. package/src/locales/default/chat.ts +2 -0
  79. package/src/locales/default/plugin.ts +2 -1
  80. package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +6 -6
  81. package/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts +3 -3
  82. package/src/server/modules/AgentRuntime/factory.ts +39 -20
  83. package/src/server/modules/ModelRuntime/index.ts +138 -1
  84. package/src/server/routers/async/__tests__/caller.test.ts +22 -27
  85. package/src/server/routers/async/caller.ts +4 -6
  86. package/src/server/routers/async/file.ts +10 -5
  87. package/src/server/routers/async/image.ts +5 -4
  88. package/src/server/routers/async/ragEval.ts +7 -5
  89. package/src/server/routers/lambda/__tests__/aiChat.test.ts +8 -37
  90. package/src/server/routers/lambda/aiChat.ts +5 -21
  91. package/src/server/routers/lambda/chunk.ts +9 -28
  92. package/src/server/routers/lambda/image.ts +1 -7
  93. package/src/server/routers/lambda/ragEval.ts +1 -1
  94. package/src/server/routers/lambda/userMemories/reembed.ts +4 -1
  95. package/src/server/routers/lambda/userMemories/search.ts +7 -7
  96. package/src/server/routers/lambda/userMemories/shared.ts +8 -10
  97. package/src/server/routers/lambda/userMemories/tools.ts +140 -118
  98. package/src/server/routers/lambda/userMemories.test.ts +3 -7
  99. package/src/server/routers/lambda/userMemories.ts +44 -29
  100. package/src/server/services/agentRuntime/AgentRuntimeService.test.ts +87 -0
  101. package/src/server/services/agentRuntime/AgentRuntimeService.ts +53 -2
  102. package/src/server/services/agentRuntime/__tests__/executeSync.test.ts +2 -6
  103. package/src/server/services/agentRuntime/__tests__/stepLifecycleCallbacks.test.ts +1 -1
  104. package/src/server/services/chunk/index.ts +6 -5
  105. package/src/server/services/toolExecution/types.ts +1 -2
  106. package/src/services/__tests__/_url.test.ts +0 -1
  107. package/src/services/_url.ts +0 -3
  108. package/src/services/aiChat.ts +5 -12
  109. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +0 -2
  110. package/src/app/(backend)/webapi/text-to-image/[provider]/route.ts +0 -74
@@ -2,7 +2,6 @@ import { DEFAULT_FILE_EMBEDDING_MODEL_ITEM } from '@lobechat/const';
2
2
  import {
3
3
  type ChatSemanticSearchChunk,
4
4
  type FileSearchResult,
5
- type ProviderConfig,
6
5
  SemanticSearchSchema,
7
6
  } from '@lobechat/types';
8
7
  import { TRPCError } from '@trpc/server';
@@ -17,13 +16,11 @@ import { DocumentModel } from '@/database/models/document';
17
16
  import { EmbeddingModel } from '@/database/models/embedding';
18
17
  import { FileModel } from '@/database/models/file';
19
18
  import { MessageModel } from '@/database/models/message';
20
- import { AiInfraRepos } from '@/database/repositories/aiInfra';
21
19
  import { knowledgeBaseFiles } from '@/database/schemas';
22
20
  import { authedProcedure, router } from '@/libs/trpc/lambda';
23
21
  import { keyVaults, serverDatabase } from '@/libs/trpc/lambda/middleware';
24
- import { getServerDefaultFilesConfig, getServerGlobalConfig } from '@/server/globalConfig';
25
- import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
26
- import { initModelRuntimeWithUserPayload } from '@/server/modules/ModelRuntime';
22
+ import { getServerDefaultFilesConfig } from '@/server/globalConfig';
23
+ import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
27
24
  import { ChunkService } from '@/server/services/chunk';
28
25
  import { DocumentService } from '@/server/services/document';
29
26
 
@@ -32,15 +29,9 @@ const chunkProcedure = authedProcedure
32
29
  .use(keyVaults)
33
30
  .use(async (opts) => {
34
31
  const { ctx } = opts;
35
- const { aiProvider } = await getServerGlobalConfig();
36
32
 
37
33
  return opts.next({
38
34
  ctx: {
39
- aiInfraRepos: new AiInfraRepos(
40
- ctx.serverDB,
41
- ctx.userId,
42
- aiProvider as Record<string, ProviderConfig>,
43
- ),
44
35
  asyncTaskModel: new AsyncTaskModel(ctx.serverDB, ctx.userId),
45
36
  chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
46
37
  chunkService: new ChunkService(ctx.serverDB, ctx.userId),
@@ -105,7 +96,7 @@ export const chunkRouter = router({
105
96
  }),
106
97
  )
107
98
  .mutation(async ({ ctx, input }) => {
108
- const asyncTaskId = await ctx.chunkService.asyncEmbeddingFileChunks(input.id, ctx.jwtPayload);
99
+ const asyncTaskId = await ctx.chunkService.asyncEmbeddingFileChunks(input.id);
109
100
 
110
101
  return { id: asyncTaskId, success: true };
111
102
  }),
@@ -118,11 +109,7 @@ export const chunkRouter = router({
118
109
  }),
119
110
  )
120
111
  .mutation(async ({ ctx, input }) => {
121
- const asyncTaskId = await ctx.chunkService.asyncParseFileToChunks(
122
- input.id,
123
- ctx.jwtPayload,
124
- input.skipExist,
125
- );
112
+ const asyncTaskId = await ctx.chunkService.asyncParseFileToChunks(input.id, input.skipExist);
126
113
 
127
114
  return { id: asyncTaskId, success: true };
128
115
  }),
@@ -223,7 +210,7 @@ export const chunkRouter = router({
223
210
  }
224
211
 
225
212
  // 2. create a new asyncTask for chunking
226
- const asyncTaskId = await ctx.chunkService.asyncParseFileToChunks(input.id, ctx.jwtPayload);
213
+ const asyncTaskId = await ctx.chunkService.asyncParseFileToChunks(input.id);
227
214
 
228
215
  return { id: asyncTaskId, success: true };
229
216
  }),
@@ -239,7 +226,8 @@ export const chunkRouter = router({
239
226
  .mutation(async ({ ctx, input }) => {
240
227
  const { model, provider } =
241
228
  getServerDefaultFilesConfig().embeddingModel || DEFAULT_FILE_EMBEDDING_MODEL_ITEM;
242
- const agentRuntime = await initModelRuntimeWithUserPayload(provider, ctx.jwtPayload);
229
+ // Read user's provider config from database
230
+ const agentRuntime = await initModelRuntimeFromDB(ctx.serverDB, ctx.userId, provider);
243
231
 
244
232
  const embeddings = await agentRuntime.embeddings({
245
233
  dimensions: 1024,
@@ -262,15 +250,8 @@ export const chunkRouter = router({
262
250
  getServerDefaultFilesConfig().embeddingModel || DEFAULT_FILE_EMBEDDING_MODEL_ITEM;
263
251
  let embedding: number[];
264
252
 
265
- const providerDetail = await ctx.aiInfraRepos.getAiProviderDetail(
266
- provider,
267
- KeyVaultsGateKeeper.getUserKeyVaults,
268
- );
269
-
270
- const modelRuntime = initModelRuntimeWithUserPayload(
271
- provider,
272
- providerDetail.keyVaults || {},
273
- );
253
+ // Read user's provider config from database
254
+ const modelRuntime = await initModelRuntimeFromDB(ctx.serverDB, ctx.userId, provider);
274
255
 
275
256
  // slice content to make sure in the context window limit
276
257
  const query = input.query.length > 8000 ? input.query.slice(0, 8000) : input.query;
@@ -228,15 +228,9 @@ export const imageRouter = router({
228
228
 
229
229
  try {
230
230
  log('Creating unified async caller for userId: %s', userId);
231
- log(
232
- 'Lambda context - userId: %s, jwtPayload keys: %O',
233
- ctx.userId,
234
- Object.keys(ctx.jwtPayload || {}),
235
- );
236
231
 
237
- // 使用统一的 caller 工厂创建 caller
232
+ // Async router will read keyVaults from DB, no need to pass jwtPayload
238
233
  const asyncCaller = await createAsyncCaller({
239
- jwtPayload: ctx.jwtPayload,
240
234
  userId: ctx.userId,
241
235
  });
242
236
 
@@ -201,9 +201,9 @@ export const ragEvalRouter = router({
201
201
  })),
202
202
  );
203
203
 
204
+ // Async router will read keyVaults from DB, no need to pass jwtPayload
204
205
  const asyncCaller = await createAsyncCaller({
205
206
  userId: ctx.userId,
206
- jwtPayload: ctx.jwtPayload,
207
207
  });
208
208
 
209
209
  await ctx.evaluationModel.update(input.id, { status: EvalEvaluationStatus.Processing });
@@ -49,7 +49,10 @@ export const reembedRouter = router({
49
49
  .mutation(async ({ ctx, input }) => {
50
50
  try {
51
51
  const options = input ?? {};
52
- const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(ctx.jwtPayload);
52
+ const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
53
+ ctx.serverDB,
54
+ ctx.userId,
55
+ );
53
56
  const concurrency = options.concurrency ?? 10;
54
57
  const shouldProcess = (key: ReEmbedTableKey) =>
55
58
  !options.only || options.only.length === 0 || options.only.includes(key);
@@ -1,18 +1,18 @@
1
- import { ModelProvider } from 'model-bank';
1
+ import { type LobeChatDatabase } from '@lobechat/database';
2
2
  import { type z } from 'zod';
3
3
 
4
4
  import { DEFAULT_FILE_EMBEDDING_MODEL_ITEM } from '@/const/settings/knowledge';
5
5
  import { type UserMemoryModel } from '@/database/models/userMemory';
6
6
  import { getServerDefaultFilesConfig } from '@/server/globalConfig';
7
- import { initModelRuntimeWithUserPayload } from '@/server/modules/ModelRuntime';
8
- import { type ClientSecretPayload } from '@/types/auth';
7
+ import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
9
8
  import { type SearchMemoryResult, searchMemorySchema } from '@/types/userMemory';
10
9
 
11
10
  import { EMBEDDING_VECTOR_DIMENSION, memoryProcedure, router } from './shared';
12
11
 
13
12
  type MemorySearchContext = {
14
- jwtPayload: ClientSecretPayload;
15
13
  memoryModel: UserMemoryModel;
14
+ serverDB: LobeChatDatabase;
15
+ userId: string;
16
16
  };
17
17
 
18
18
  type MemorySearchResult = Awaited<ReturnType<UserMemoryModel['searchWithEmbedding']>>;
@@ -80,10 +80,10 @@ export const searchUserMemories = async (
80
80
  ctx: MemorySearchContext,
81
81
  input: z.infer<typeof searchMemorySchema>,
82
82
  ): Promise<SearchMemoryResult> => {
83
- const agentRuntime = await initModelRuntimeWithUserPayload(ModelProvider.OpenAI, ctx.jwtPayload);
84
-
85
- const { model: embeddingModel } =
83
+ const { provider, model: embeddingModel } =
86
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
87
 
88
88
  const queryEmbeddings = await agentRuntime.embeddings({
89
89
  dimensions: EMBEDDING_VECTOR_DIMENSION,
@@ -1,15 +1,12 @@
1
+ import { type LobeChatDatabase } from '@lobechat/database';
1
2
  import { type SQL, and } from 'drizzle-orm';
2
- import { ModelProvider } from 'model-bank';
3
3
 
4
4
  import { DEFAULT_FILE_EMBEDDING_MODEL_ITEM } from '@/const/settings/knowledge';
5
5
  import { UserMemoryModel } from '@/database/models/userMemory';
6
- import { authedProcedure, } from '@/libs/trpc/lambda';
6
+ import { authedProcedure } from '@/libs/trpc/lambda';
7
7
  import { keyVaults, serverDatabase } from '@/libs/trpc/lambda/middleware';
8
8
  import { getServerDefaultFilesConfig } from '@/server/globalConfig';
9
- import { initModelRuntimeWithUserPayload } from '@/server/modules/ModelRuntime';
10
- import { type ClientSecretPayload } from '@/types/auth';
11
-
12
-
9
+ import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
13
10
 
14
11
  export const EMBEDDING_VECTOR_DIMENSION = 1024;
15
12
 
@@ -25,10 +22,11 @@ export const memoryProcedure = authedProcedure
25
22
  });
26
23
  });
27
24
 
28
- export const getEmbeddingRuntime = async (jwtPayload: ClientSecretPayload) => {
29
- const agentRuntime = await initModelRuntimeWithUserPayload(ModelProvider.OpenAI, jwtPayload);
30
- const { model: embeddingModel } =
25
+ export const getEmbeddingRuntime = async (serverDB: LobeChatDatabase, userId: string) => {
26
+ const { provider, model: embeddingModel } =
31
27
  getServerDefaultFilesConfig().embeddingModel || DEFAULT_FILE_EMBEDDING_MODEL_ITEM;
28
+ // Read user's provider config from database
29
+ const agentRuntime = await initModelRuntimeFromDB(serverDB, userId, provider);
32
30
 
33
31
  return { agentRuntime, embeddingModel };
34
32
  };
@@ -62,4 +60,4 @@ export const normalizeEmbeddable = (value?: string | null): string | undefined =
62
60
  return trimmed.length > 0 ? trimmed : undefined;
63
61
  };
64
62
 
65
- export {router} from '@/libs/trpc/lambda';
63
+ export { router } from '@/libs/trpc/lambda';
@@ -1,13 +1,18 @@
1
- import { type IdentityEntryBasePayload, type IdentityEntryPayload, UserMemoryModel } from '@/database/models/userMemory';
2
- import { searchMemorySchema } from '@/types/userMemory';
3
1
  import {
2
+ AddIdentityActionSchema,
4
3
  ContextMemoryItemSchema,
5
4
  ExperienceMemoryItemSchema,
6
5
  PreferenceMemoryItemSchema,
7
- AddIdentityActionSchema,
8
- UpdateIdentityActionSchema,
9
6
  RemoveIdentityActionSchema,
10
- } from '@lobechat/memory-user-memory'
7
+ UpdateIdentityActionSchema,
8
+ } from '@lobechat/memory-user-memory';
9
+
10
+ import {
11
+ type IdentityEntryBasePayload,
12
+ type IdentityEntryPayload,
13
+ UserMemoryModel,
14
+ } from '@/database/models/userMemory';
15
+ import { searchMemorySchema } from '@/types/userMemory';
11
16
 
12
17
  import { searchUserMemories } from './search';
13
18
  import { createEmbedder, getEmbeddingRuntime, memoryProcedure, router } from './shared';
@@ -17,7 +22,10 @@ export const toolsRouter = router({
17
22
  .input(ContextMemoryItemSchema)
18
23
  .mutation(async ({ input, ctx }) => {
19
24
  try {
20
- const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(ctx.jwtPayload);
25
+ const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
26
+ ctx.serverDB,
27
+ ctx.userId,
28
+ );
21
29
  const embed = createEmbedder(agentRuntime, embeddingModel);
22
30
 
23
31
  const summaryEmbedding = await embed(input.summary);
@@ -26,8 +34,10 @@ export const toolsRouter = router({
26
34
 
27
35
  const { context, memory } = await ctx.memoryModel.createContextMemory({
28
36
  context: {
29
- associatedObjects: UserMemoryModel.parseAssociatedObjects(input.withContext.associatedObjects) ?? null,
30
- associatedSubjects: UserMemoryModel.parseAssociatedSubjects(input.withContext.associatedSubjects) ?? null,
37
+ associatedObjects:
38
+ UserMemoryModel.parseAssociatedObjects(input.withContext.associatedObjects) ?? null,
39
+ associatedSubjects:
40
+ UserMemoryModel.parseAssociatedSubjects(input.withContext.associatedSubjects) ?? null,
31
41
  currentStatus: input.withContext.currentStatus ?? null,
32
42
  description: input.withContext.description ?? null,
33
43
  descriptionVector: contextDescriptionEmbedding ?? null,
@@ -67,7 +77,10 @@ export const toolsRouter = router({
67
77
  .input(ExperienceMemoryItemSchema)
68
78
  .mutation(async ({ input, ctx }) => {
69
79
  try {
70
- const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(ctx.jwtPayload);
80
+ const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
81
+ ctx.serverDB,
82
+ ctx.userId,
83
+ );
71
84
  const embed = createEmbedder(agentRuntime, embeddingModel);
72
85
 
73
86
  const summaryEmbedding = await embed(input.summary);
@@ -120,7 +133,10 @@ export const toolsRouter = router({
120
133
  .input(AddIdentityActionSchema)
121
134
  .mutation(async ({ input, ctx }) => {
122
135
  try {
123
- const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(ctx.jwtPayload);
136
+ const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
137
+ ctx.serverDB,
138
+ ctx.userId,
139
+ );
124
140
  const embed = createEmbedder(agentRuntime, embeddingModel);
125
141
 
126
142
  const summaryEmbedding = await embed(input.summary);
@@ -185,7 +201,10 @@ export const toolsRouter = router({
185
201
  .input(PreferenceMemoryItemSchema)
186
202
  .mutation(async ({ input, ctx }) => {
187
203
  try {
188
- const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(ctx.jwtPayload);
204
+ const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
205
+ ctx.serverDB,
206
+ ctx.userId,
207
+ );
189
208
  const embed = createEmbedder(agentRuntime, embeddingModel);
190
209
 
191
210
  const summaryEmbedding = await embed(input.summary);
@@ -277,111 +296,114 @@ export const toolsRouter = router({
277
296
 
278
297
  updateIdentityMemory: memoryProcedure
279
298
  .input(UpdateIdentityActionSchema)
280
- .mutation(async ({ input, ctx }) => {
281
- try {
282
- const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(ctx.jwtPayload);
283
- const embed = createEmbedder(agentRuntime, embeddingModel);
284
-
285
- let summaryVector1024: number[] | null | undefined;
286
- if (input.set.summary !== undefined) {
287
- const vector = await embed(input.set.summary);
288
- summaryVector1024 = vector ?? null;
289
- }
290
-
291
- let detailsVector1024: number[] | null | undefined;
292
- if (input.set.details !== undefined) {
293
- const vector = await embed(input.set.details);
294
- detailsVector1024 = vector ?? null;
295
- }
296
-
297
- let descriptionVector: number[] | null | undefined;
298
- if (input.set.withIdentity.description !== undefined) {
299
- const vector = await embed(input.set.withIdentity.description);
300
- descriptionVector = vector ?? null;
301
- }
302
-
303
- const metadataUpdates: Record<string, unknown> = {};
304
- if (Object.hasOwn(input.set.withIdentity, 'scoreConfidence')) {
305
- metadataUpdates.scoreConfidence = input.set.withIdentity.scoreConfidence ?? null;
306
- }
307
- if (Object.hasOwn(input.set.withIdentity, 'sourceEvidence')) {
308
- metadataUpdates.sourceEvidence = input.set.withIdentity.sourceEvidence ?? null;
309
- }
310
-
311
- const identityPayload: Partial<IdentityEntryPayload> = {};
312
- if (input.set.withIdentity.description !== undefined) {
313
- identityPayload.description = input.set.withIdentity.description;
314
- identityPayload.descriptionVector = descriptionVector;
315
- }
316
- if (input.set.withIdentity.episodicDate !== undefined) {
317
- identityPayload.episodicDate = input.set.withIdentity.episodicDate;
318
- }
319
- if (input.set.withIdentity.relationship !== undefined) {
320
- identityPayload.relationship = input.set.withIdentity.relationship;
321
- }
322
- if (input.set.withIdentity.role !== undefined) {
323
- identityPayload.role = input.set.withIdentity.role;
324
- }
325
- if (input.set.tags !== undefined) {
326
- identityPayload.tags = input.set.tags;
327
- }
328
- if (input.set.withIdentity.type !== undefined) {
329
- identityPayload.type = input.set.withIdentity.type;
330
- }
331
- if (Object.keys(metadataUpdates).length > 0) {
332
- identityPayload.metadata = metadataUpdates;
333
- }
334
-
335
- const basePayload: Partial<IdentityEntryBasePayload> = {};
336
- if (input.set.details !== undefined) {
337
- basePayload.details = input.set.details;
338
- basePayload.detailsVector1024 = detailsVector1024;
339
- }
340
- if (input.set.memoryCategory !== undefined) {
341
- basePayload.memoryCategory = input.set.memoryCategory;
342
- }
343
- if (input.set.memoryType !== undefined) {
344
- basePayload.memoryType = input.set.memoryType;
345
- }
346
- if (input.set.summary !== undefined) {
347
- basePayload.summary = input.set.summary;
348
- basePayload.summaryVector1024 = summaryVector1024;
349
- }
350
- if (input.set.tags !== undefined) {
351
- basePayload.tags = input.set.tags;
352
- }
353
- if (input.set.title !== undefined) {
354
- basePayload.title = input.set.title;
355
- }
356
- if (Object.keys(metadataUpdates).length > 0) {
357
- basePayload.metadata = metadataUpdates;
358
- }
359
-
360
- const updated = await ctx.memoryModel.updateIdentityEntry({
361
- base: Object.keys(basePayload).length > 0 ? basePayload : undefined,
362
- identity: Object.keys(identityPayload).length > 0 ? identityPayload : undefined,
363
- identityId: input.id,
364
- mergeStrategy: input.mergeStrategy,
365
- });
366
-
367
- if (!updated) {
368
- return {
369
- message: 'Identity memory not found',
370
- success: false,
371
- };
372
- }
373
-
374
- return {
375
- identityId: input.id,
376
- message: 'Identity memory updated successfully',
377
- success: true,
378
- };
379
- } catch (error) {
380
- console.error('Failed to update identity memory:', error);
381
- return {
382
- message: `Failed to update identity memory: ${(error as Error).message}`,
383
- success: false,
384
- };
385
- }
386
- }),
299
+ .mutation(async ({ input, ctx }) => {
300
+ try {
301
+ const { agentRuntime, embeddingModel } = await getEmbeddingRuntime(
302
+ ctx.serverDB,
303
+ ctx.userId,
304
+ );
305
+ const embed = createEmbedder(agentRuntime, embeddingModel);
306
+
307
+ let summaryVector1024: number[] | null | undefined;
308
+ if (input.set.summary !== undefined) {
309
+ const vector = await embed(input.set.summary);
310
+ summaryVector1024 = vector ?? null;
311
+ }
312
+
313
+ let detailsVector1024: number[] | null | undefined;
314
+ if (input.set.details !== undefined) {
315
+ const vector = await embed(input.set.details);
316
+ detailsVector1024 = vector ?? null;
317
+ }
318
+
319
+ let descriptionVector: number[] | null | undefined;
320
+ if (input.set.withIdentity.description !== undefined) {
321
+ const vector = await embed(input.set.withIdentity.description);
322
+ descriptionVector = vector ?? null;
323
+ }
324
+
325
+ const metadataUpdates: Record<string, unknown> = {};
326
+ if (Object.hasOwn(input.set.withIdentity, 'scoreConfidence')) {
327
+ metadataUpdates.scoreConfidence = input.set.withIdentity.scoreConfidence ?? null;
328
+ }
329
+ if (Object.hasOwn(input.set.withIdentity, 'sourceEvidence')) {
330
+ metadataUpdates.sourceEvidence = input.set.withIdentity.sourceEvidence ?? null;
331
+ }
332
+
333
+ const identityPayload: Partial<IdentityEntryPayload> = {};
334
+ if (input.set.withIdentity.description !== undefined) {
335
+ identityPayload.description = input.set.withIdentity.description;
336
+ identityPayload.descriptionVector = descriptionVector;
337
+ }
338
+ if (input.set.withIdentity.episodicDate !== undefined) {
339
+ identityPayload.episodicDate = input.set.withIdentity.episodicDate;
340
+ }
341
+ if (input.set.withIdentity.relationship !== undefined) {
342
+ identityPayload.relationship = input.set.withIdentity.relationship;
343
+ }
344
+ if (input.set.withIdentity.role !== undefined) {
345
+ identityPayload.role = input.set.withIdentity.role;
346
+ }
347
+ if (input.set.tags !== undefined) {
348
+ identityPayload.tags = input.set.tags;
349
+ }
350
+ if (input.set.withIdentity.type !== undefined) {
351
+ identityPayload.type = input.set.withIdentity.type;
352
+ }
353
+ if (Object.keys(metadataUpdates).length > 0) {
354
+ identityPayload.metadata = metadataUpdates;
355
+ }
356
+
357
+ const basePayload: Partial<IdentityEntryBasePayload> = {};
358
+ if (input.set.details !== undefined) {
359
+ basePayload.details = input.set.details;
360
+ basePayload.detailsVector1024 = detailsVector1024;
361
+ }
362
+ if (input.set.memoryCategory !== undefined) {
363
+ basePayload.memoryCategory = input.set.memoryCategory;
364
+ }
365
+ if (input.set.memoryType !== undefined) {
366
+ basePayload.memoryType = input.set.memoryType;
367
+ }
368
+ if (input.set.summary !== undefined) {
369
+ basePayload.summary = input.set.summary;
370
+ basePayload.summaryVector1024 = summaryVector1024;
371
+ }
372
+ if (input.set.tags !== undefined) {
373
+ basePayload.tags = input.set.tags;
374
+ }
375
+ if (input.set.title !== undefined) {
376
+ basePayload.title = input.set.title;
377
+ }
378
+ if (Object.keys(metadataUpdates).length > 0) {
379
+ basePayload.metadata = metadataUpdates;
380
+ }
381
+
382
+ const updated = await ctx.memoryModel.updateIdentityEntry({
383
+ base: Object.keys(basePayload).length > 0 ? basePayload : undefined,
384
+ identity: Object.keys(identityPayload).length > 0 ? identityPayload : undefined,
385
+ identityId: input.id,
386
+ mergeStrategy: input.mergeStrategy,
387
+ });
388
+
389
+ if (!updated) {
390
+ return {
391
+ message: 'Identity memory not found',
392
+ success: false,
393
+ };
394
+ }
395
+
396
+ return {
397
+ identityId: input.id,
398
+ message: 'Identity memory updated successfully',
399
+ success: true,
400
+ };
401
+ } catch (error) {
402
+ console.error('Failed to update identity memory:', error);
403
+ return {
404
+ message: `Failed to update identity memory: ${(error as Error).message}`,
405
+ success: false,
406
+ };
407
+ }
408
+ }),
387
409
  });
@@ -4,7 +4,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
4
 
5
5
  import { getServerDB } from '@/database/core/db-adaptor';
6
6
  import { UserMemoryModel } from '@/database/models/userMemory';
7
- import { initModelRuntimeWithUserPayload } from '@/server/modules/ModelRuntime';
7
+ import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
8
8
 
9
9
  import { userMemoriesRouter } from './userMemories';
10
10
 
@@ -19,11 +19,7 @@ vi.mock('@/server/globalConfig', () => ({
19
19
  }));
20
20
 
21
21
  vi.mock('@/server/modules/ModelRuntime', () => ({
22
- initModelRuntimeWithUserPayload: vi.fn(),
23
- }));
24
-
25
- vi.mock('@lobechat/utils/server', () => ({
26
- getXorPayload: vi.fn().mockReturnValue({ userId: 'test-user' }),
22
+ initModelRuntimeFromDB: vi.fn(),
27
23
  }));
28
24
 
29
25
  vi.mock('@/database/models/userMemory', async (importOriginal) => {
@@ -47,7 +43,7 @@ beforeEach(() => {
47
43
  return items.map((_, index) => [index + 1]);
48
44
  });
49
45
 
50
- vi.mocked(initModelRuntimeWithUserPayload).mockResolvedValue({
46
+ vi.mocked(initModelRuntimeFromDB).mockResolvedValue({
51
47
  embeddings: embeddingsMock,
52
48
  } as any);
53
49
  });