@lobehub/chat 1.140.0 → 1.141.1

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 (125) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/changelog/v1.json +24 -0
  3. package/locales/ar/chat.json +13 -0
  4. package/locales/ar/common.json +1 -0
  5. package/locales/ar/components.json +4 -0
  6. package/locales/ar/file.json +2 -2
  7. package/locales/bg-BG/chat.json +13 -0
  8. package/locales/bg-BG/common.json +1 -0
  9. package/locales/bg-BG/components.json +4 -0
  10. package/locales/bg-BG/file.json +2 -2
  11. package/locales/de-DE/chat.json +13 -0
  12. package/locales/de-DE/common.json +1 -0
  13. package/locales/de-DE/components.json +4 -0
  14. package/locales/de-DE/file.json +2 -2
  15. package/locales/en-US/chat.json +13 -0
  16. package/locales/en-US/common.json +1 -0
  17. package/locales/en-US/components.json +4 -0
  18. package/locales/en-US/file.json +2 -2
  19. package/locales/es-ES/chat.json +13 -0
  20. package/locales/es-ES/common.json +1 -0
  21. package/locales/es-ES/components.json +4 -0
  22. package/locales/es-ES/file.json +2 -2
  23. package/locales/fa-IR/chat.json +13 -0
  24. package/locales/fa-IR/common.json +1 -0
  25. package/locales/fa-IR/components.json +4 -0
  26. package/locales/fa-IR/file.json +2 -2
  27. package/locales/fr-FR/chat.json +13 -0
  28. package/locales/fr-FR/common.json +1 -0
  29. package/locales/fr-FR/components.json +4 -0
  30. package/locales/fr-FR/file.json +2 -2
  31. package/locales/it-IT/chat.json +13 -0
  32. package/locales/it-IT/common.json +1 -0
  33. package/locales/it-IT/components.json +4 -0
  34. package/locales/it-IT/file.json +2 -2
  35. package/locales/ja-JP/chat.json +13 -0
  36. package/locales/ja-JP/common.json +1 -0
  37. package/locales/ja-JP/components.json +4 -0
  38. package/locales/ja-JP/file.json +2 -2
  39. package/locales/ko-KR/chat.json +13 -0
  40. package/locales/ko-KR/common.json +1 -0
  41. package/locales/ko-KR/components.json +4 -0
  42. package/locales/ko-KR/file.json +2 -2
  43. package/locales/nl-NL/chat.json +13 -0
  44. package/locales/nl-NL/common.json +1 -0
  45. package/locales/nl-NL/components.json +4 -0
  46. package/locales/nl-NL/file.json +2 -2
  47. package/locales/pl-PL/chat.json +13 -0
  48. package/locales/pl-PL/common.json +1 -0
  49. package/locales/pl-PL/components.json +4 -0
  50. package/locales/pl-PL/file.json +2 -2
  51. package/locales/pt-BR/chat.json +13 -0
  52. package/locales/pt-BR/common.json +1 -0
  53. package/locales/pt-BR/components.json +4 -0
  54. package/locales/pt-BR/file.json +2 -2
  55. package/locales/ru-RU/chat.json +13 -0
  56. package/locales/ru-RU/common.json +1 -0
  57. package/locales/ru-RU/components.json +4 -0
  58. package/locales/ru-RU/file.json +2 -2
  59. package/locales/tr-TR/chat.json +13 -0
  60. package/locales/tr-TR/common.json +1 -0
  61. package/locales/tr-TR/components.json +4 -0
  62. package/locales/tr-TR/file.json +2 -2
  63. package/locales/vi-VN/chat.json +13 -0
  64. package/locales/vi-VN/common.json +1 -0
  65. package/locales/vi-VN/components.json +4 -0
  66. package/locales/vi-VN/file.json +2 -2
  67. package/locales/zh-CN/chat.json +13 -0
  68. package/locales/zh-CN/common.json +1 -0
  69. package/locales/zh-CN/components.json +4 -0
  70. package/locales/zh-CN/file.json +2 -2
  71. package/locales/zh-TW/chat.json +13 -0
  72. package/locales/zh-TW/common.json +1 -0
  73. package/locales/zh-TW/components.json +4 -0
  74. package/locales/zh-TW/file.json +2 -2
  75. package/next.config.ts +5 -6
  76. package/package.json +8 -2
  77. package/packages/context-engine/src/__tests__/pipeline.test.ts +7 -27
  78. package/packages/context-engine/src/pipeline.ts +5 -21
  79. package/packages/context-engine/src/types.ts +2 -2
  80. package/packages/database/src/models/__tests__/message.test.ts +200 -2
  81. package/packages/database/src/models/message.ts +13 -0
  82. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +313 -0
  83. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +21 -5
  84. package/packages/model-runtime/src/providers/azureai/index.test.ts +12 -2
  85. package/packages/model-runtime/src/providers/groq/index.test.ts +449 -0
  86. package/packages/model-runtime/src/providers/groq/index.ts +46 -0
  87. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +3 -2
  88. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags/index.tsx +1 -1
  89. package/src/app/[variants]/(main)/files/(content)/@menu/features/KnowledgeBase/Item/index.tsx +10 -2
  90. package/src/features/ChatInput/InputEditor/index.tsx +2 -0
  91. package/src/features/Conversation/Messages/User/index.tsx +7 -17
  92. package/src/features/Conversation/components/ChatItem/ShareMessageModal/SharePdf/PdfPreview.tsx +361 -0
  93. package/src/features/Conversation/components/ChatItem/ShareMessageModal/SharePdf/index.tsx +119 -0
  94. package/src/features/Conversation/components/ChatItem/ShareMessageModal/SharePdf/style.ts +63 -0
  95. package/src/features/Conversation/components/ChatItem/ShareMessageModal/SharePdf/template.ts +24 -0
  96. package/src/features/Conversation/components/ChatItem/ShareMessageModal/SharePdf/usePdfGeneration.ts +93 -0
  97. package/src/features/Conversation/components/ShareMessageModal/ShareImage/Preview.tsx +1 -1
  98. package/src/features/Conversation/components/ShareMessageModal/index.tsx +39 -14
  99. package/src/features/FileManager/FileList/MasonryFileItem/MasonryItemWrapper.tsx +44 -0
  100. package/src/features/FileManager/FileList/MasonryFileItem/index.tsx +553 -0
  101. package/src/features/FileManager/FileList/MasonrySkeleton.tsx +57 -0
  102. package/src/features/FileManager/FileList/ToolBar/ViewSwitcher.tsx +45 -0
  103. package/src/features/FileManager/FileList/ToolBar/index.tsx +9 -1
  104. package/src/features/FileManager/FileList/index.tsx +83 -13
  105. package/src/features/FileManager/Header/FilesSearchBar.tsx +7 -2
  106. package/src/features/ShareModal/ShareImage/Preview.tsx +1 -1
  107. package/src/features/ShareModal/SharePdf/PdfPreview.tsx +361 -0
  108. package/src/features/ShareModal/SharePdf/index.tsx +194 -0
  109. package/src/features/ShareModal/SharePdf/usePdfGeneration.ts +90 -0
  110. package/src/features/ShareModal/index.tsx +40 -14
  111. package/src/features/ShareModal/style.ts +8 -5
  112. package/src/helpers/toolEngineering/index.ts +7 -1
  113. package/src/helpers/toolFilters.ts +35 -0
  114. package/src/libs/trpc/client/lambda.ts +7 -1
  115. package/src/locales/default/chat.ts +13 -0
  116. package/src/locales/default/common.ts +1 -0
  117. package/src/locales/default/components.ts +4 -0
  118. package/src/locales/default/file.ts +2 -2
  119. package/src/server/globalConfig/parseSystemAgent.ts +4 -2
  120. package/src/server/routers/lambda/exporter.ts +173 -3
  121. package/src/server/routers/lambda/message.ts +11 -0
  122. package/src/services/chat/contextEngineering.ts +1 -9
  123. package/src/store/agent/slices/chat/selectors/agent.ts +16 -6
  124. package/src/store/global/initialState.ts +2 -0
  125. package/src/store/tool/slices/builtin/selectors.ts +15 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.140.0",
3
+ "version": "1.141.1",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot 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",
@@ -164,7 +164,7 @@
164
164
  "@lobehub/charts": "^2.1.2",
165
165
  "@lobehub/chat-plugin-sdk": "^1.32.4",
166
166
  "@lobehub/chat-plugins-gateway": "^1.9.0",
167
- "@lobehub/editor": "^1.16.1",
167
+ "@lobehub/editor": "^1.20.2",
168
168
  "@lobehub/icons": "^2.42.0",
169
169
  "@lobehub/market-sdk": "^0.22.7",
170
170
  "@lobehub/tts": "^2.0.1",
@@ -174,6 +174,7 @@
174
174
  "@next/third-parties": "^15.5.4",
175
175
  "@opentelemetry/exporter-jaeger": "^2.1.0",
176
176
  "@opentelemetry/winston-transport": "^0.17.0",
177
+ "@react-pdf/renderer": "^4.3.0",
177
178
  "@react-spring/web": "^9.7.5",
178
179
  "@saintno/comfyui-sdk": "^0.2.48",
179
180
  "@serwist/next": "^9.2.1",
@@ -187,6 +188,7 @@
187
188
  "@vercel/edge-config": "^1.4.0",
188
189
  "@vercel/functions": "^3.1.3",
189
190
  "@vercel/speed-insights": "^1.2.0",
191
+ "@virtuoso.dev/masonry": "^1.3.5",
190
192
  "@xterm/xterm": "^5.5.0",
191
193
  "ahooks": "^3.9.5",
192
194
  "antd": "^5.27.4",
@@ -224,6 +226,7 @@
224
226
  "lucide-react": "^0.544.0",
225
227
  "mammoth": "^1.11.0",
226
228
  "markdown-to-txt": "^2.0.1",
229
+ "marked": "^16.3.0",
227
230
  "mdast-util-to-markdown": "^2.1.2",
228
231
  "model-bank": "workspace:*",
229
232
  "modern-screenshot": "^4.6.6",
@@ -244,6 +247,7 @@
244
247
  "path-browserify-esm": "^1.0.6",
245
248
  "pdf-parse": "^1.1.1",
246
249
  "pdfjs-dist": "4.8.69",
250
+ "pdfkit": "^0.17.2",
247
251
  "pg": "^8.16.3",
248
252
  "pino": "^9.13.1",
249
253
  "plaiceholder": "^3.0.0",
@@ -320,9 +324,11 @@
320
324
  "@types/json-schema": "^7.0.15",
321
325
  "@types/lodash": "^4.17.20",
322
326
  "@types/lodash-es": "^4.17.12",
327
+ "@types/marked": "^6.0.0",
323
328
  "@types/node": "^22.18.9",
324
329
  "@types/numeral": "^2.0.5",
325
330
  "@types/oidc-provider": "^9.5.0",
331
+ "@types/pdfkit": "^0.17.3",
326
332
  "@types/pg": "^8.15.5",
327
333
  "@types/react": "^19.2.2",
328
334
  "@types/react-dom": "^19.2.1",
@@ -36,19 +36,9 @@ describe('ContextEngine', () => {
36
36
  });
37
37
 
38
38
  const createInitialContext = (): {
39
- initialState: any;
40
- maxTokens: number;
41
39
  messages: any[];
42
- model: string;
43
40
  } => ({
44
- initialState: {
45
- messages: [],
46
- model: 'test-model',
47
- provider: 'test-provider',
48
- },
49
- maxTokens: 4000,
50
41
  messages: [{ content: 'test', role: 'user' }],
51
- model: 'test-model',
52
42
  });
53
43
 
54
44
  describe('constructor', () => {
@@ -206,8 +196,7 @@ describe('ContextEngine', () => {
206
196
  const processor = createMockProcessor('p1');
207
197
  const engine = new ContextEngine({ pipeline: [processor] });
208
198
 
209
- const input = { ...createInitialContext(), messages: undefined };
210
- const result = await engine.process(input);
199
+ const result = await engine.process({ messages: [] });
211
200
 
212
201
  expect(result.messages).toEqual([]);
213
202
  });
@@ -216,19 +205,14 @@ describe('ContextEngine', () => {
216
205
  const processor: ContextProcessor = {
217
206
  name: 'test',
218
207
  process: vi.fn(async (context) => {
219
- expect(context.metadata.maxTokens).toBe(4000);
220
- expect(context.metadata.model).toBe('test-model');
221
- expect(context.metadata.customKey).toBe('customValue');
208
+ expect(context.metadata).toBeDefined();
222
209
  return context;
223
210
  }),
224
211
  };
225
212
 
226
213
  const engine = new ContextEngine({ pipeline: [processor] });
227
214
 
228
- await engine.process({
229
- ...createInitialContext(),
230
- metadata: { customKey: 'customValue' },
231
- });
215
+ await engine.process(createInitialContext());
232
216
  });
233
217
 
234
218
  it('should track execution stats', async () => {
@@ -278,12 +262,7 @@ describe('ContextEngine', () => {
278
262
  pipeline: [processor1, processor2],
279
263
  });
280
264
 
281
- const input = {
282
- ...createInitialContext(),
283
- };
284
- input.initialState = { ...input.initialState, messages: [] };
285
-
286
- const result = await engine.process(input);
265
+ const result = await engine.process(createInitialContext());
287
266
 
288
267
  expect(result.isAborted).toBe(true);
289
268
  expect(result.stats.processedCount).toBe(1);
@@ -333,16 +312,17 @@ describe('ContextEngine', () => {
333
312
  });
334
313
 
335
314
  it('should preserve initial state', async () => {
315
+ const testContext = createInitialContext();
336
316
  const processor: ContextProcessor = {
337
317
  name: 'test',
338
318
  process: vi.fn(async (context) => {
339
- expect(context.initialState).toEqual(createInitialContext().initialState);
319
+ expect(context.initialState.messages).toEqual(testContext.messages);
340
320
  return context;
341
321
  }),
342
322
  };
343
323
 
344
324
  const engine = new ContextEngine({ pipeline: [processor] });
345
- await engine.process(createInitialContext());
325
+ await engine.process(testContext);
346
326
  });
347
327
  });
348
328
 
@@ -1,12 +1,6 @@
1
1
  import debug from 'debug';
2
2
 
3
- import type {
4
- AgentState,
5
- ContextProcessor,
6
- PipelineContext,
7
- PipelineResult,
8
- ProcessorOptions,
9
- } from './types';
3
+ import type { ContextProcessor, PipelineContext, PipelineResult, ProcessorOptions } from './types';
10
4
  import { PipelineError } from './types';
11
5
 
12
6
  const log = debug('context-engine:ContextEngine');
@@ -70,26 +64,16 @@ export class ContextEngine {
70
64
  /**
71
65
  * Execute pipeline processing
72
66
  */
73
- async process(input: {
74
- initialState: AgentState;
75
- maxTokens: number;
76
- messages?: Array<any>;
77
- metadata?: Record<string, any>;
78
- model: string;
79
- }): Promise<PipelineResult> {
67
+ async process(input: { messages: Array<any> }): Promise<PipelineResult> {
80
68
  const startTime = Date.now();
81
69
  const processorDurations: Record<string, number> = {};
82
70
 
83
71
  // Create initial pipeline context
84
72
  let context: PipelineContext = {
85
- initialState: input.initialState,
73
+ initialState: { messages: input.messages },
86
74
  isAborted: false,
87
- messages: Array.isArray(input.messages) ? [...input.messages] : [],
88
- metadata: {
89
- maxTokens: input.maxTokens,
90
- model: input.model,
91
- ...input.metadata,
92
- },
75
+ messages: [...input.messages],
76
+ metadata: {},
93
77
  };
94
78
 
95
79
  log('Starting pipeline processing');
@@ -60,9 +60,9 @@ export interface PipelineContext {
60
60
  /** 当前 token 估算值 */
61
61
  currentTokenCount?: number;
62
62
  /** 最大 token 限制 */
63
- maxTokens: number;
63
+ maxTokens?: number;
64
64
  /** 模型标识 */
65
- model: string;
65
+ model?: string;
66
66
  };
67
67
  }
68
68
 
@@ -7,10 +7,10 @@ import { uuid } from '@/utils/uuid';
7
7
 
8
8
  import { getTestDB } from '../../models/__tests__/_util';
9
9
  import {
10
- chunks,
11
- embeddings,
12
10
  agents,
13
11
  chatGroups,
12
+ chunks,
13
+ embeddings,
14
14
  fileChunks,
15
15
  files,
16
16
  messagePlugins,
@@ -1290,6 +1290,204 @@ describe('MessageModel', () => {
1290
1290
  });
1291
1291
  });
1292
1292
 
1293
+ describe('updateMetadata', () => {
1294
+ it('should update metadata for an existing message', async () => {
1295
+ // 创建测试数据
1296
+ await serverDB.insert(messages).values({
1297
+ id: 'msg-with-metadata',
1298
+ userId,
1299
+ role: 'user',
1300
+ content: 'test message',
1301
+ metadata: { existingKey: 'existingValue' },
1302
+ });
1303
+
1304
+ // 调用 updateMetadata 方法
1305
+ await messageModel.updateMetadata('msg-with-metadata', { newKey: 'newValue' });
1306
+
1307
+ // 断言结果
1308
+ const result = await serverDB
1309
+ .select()
1310
+ .from(messages)
1311
+ .where(eq(messages.id, 'msg-with-metadata'));
1312
+
1313
+ expect(result[0].metadata).toEqual({
1314
+ existingKey: 'existingValue',
1315
+ newKey: 'newValue',
1316
+ });
1317
+ });
1318
+
1319
+ it('should merge new metadata with existing metadata using lodash merge behavior', async () => {
1320
+ // 创建测试数据
1321
+ await serverDB.insert(messages).values({
1322
+ id: 'msg-merge-metadata',
1323
+ userId,
1324
+ role: 'assistant',
1325
+ content: 'test message',
1326
+ metadata: {
1327
+ level1: {
1328
+ level2a: 'original',
1329
+ level2b: { level3: 'deep' },
1330
+ },
1331
+ array: [1, 2, 3],
1332
+ },
1333
+ });
1334
+
1335
+ // 调用 updateMetadata 方法
1336
+ await messageModel.updateMetadata('msg-merge-metadata', {
1337
+ level1: {
1338
+ level2a: 'updated',
1339
+ level2c: 'new',
1340
+ },
1341
+ newTopLevel: 'value',
1342
+ });
1343
+
1344
+ // 断言结果 - 应该使用 lodash merge 行为
1345
+ const result = await serverDB
1346
+ .select()
1347
+ .from(messages)
1348
+ .where(eq(messages.id, 'msg-merge-metadata'));
1349
+
1350
+ expect(result[0].metadata).toEqual({
1351
+ level1: {
1352
+ level2a: 'updated',
1353
+ level2b: { level3: 'deep' },
1354
+ level2c: 'new',
1355
+ },
1356
+ array: [1, 2, 3],
1357
+ newTopLevel: 'value',
1358
+ });
1359
+ });
1360
+
1361
+ it('should handle non-existent message IDs', async () => {
1362
+ // 调用 updateMetadata 方法,尝试更新不存在的消息
1363
+ const result = await messageModel.updateMetadata('non-existent-id', { key: 'value' });
1364
+
1365
+ // 断言结果 - 应该返回 undefined
1366
+ expect(result).toBeUndefined();
1367
+ });
1368
+
1369
+ it('should handle empty metadata updates', async () => {
1370
+ // 创建测试数据
1371
+ await serverDB.insert(messages).values({
1372
+ id: 'msg-empty-metadata',
1373
+ userId,
1374
+ role: 'user',
1375
+ content: 'test message',
1376
+ metadata: { originalKey: 'originalValue' },
1377
+ });
1378
+
1379
+ // 调用 updateMetadata 方法,传递空对象
1380
+ await messageModel.updateMetadata('msg-empty-metadata', {});
1381
+
1382
+ // 断言结果 - 原始 metadata 应该保持不变
1383
+ const result = await serverDB
1384
+ .select()
1385
+ .from(messages)
1386
+ .where(eq(messages.id, 'msg-empty-metadata'));
1387
+
1388
+ expect(result[0].metadata).toEqual({ originalKey: 'originalValue' });
1389
+ });
1390
+
1391
+ it('should handle message with null metadata', async () => {
1392
+ // 创建测试数据
1393
+ await serverDB.insert(messages).values({
1394
+ id: 'msg-null-metadata',
1395
+ userId,
1396
+ role: 'user',
1397
+ content: 'test message',
1398
+ metadata: null,
1399
+ });
1400
+
1401
+ // 调用 updateMetadata 方法
1402
+ await messageModel.updateMetadata('msg-null-metadata', { key: 'value' });
1403
+
1404
+ // 断言结果 - 应该创建新的 metadata
1405
+ const result = await serverDB
1406
+ .select()
1407
+ .from(messages)
1408
+ .where(eq(messages.id, 'msg-null-metadata'));
1409
+
1410
+ expect(result[0].metadata).toEqual({ key: 'value' });
1411
+ });
1412
+
1413
+ it('should only update messages belonging to the current user', async () => {
1414
+ // 创建测试数据 - 其他用户的消息
1415
+ await serverDB.insert(messages).values({
1416
+ id: 'msg-other-user',
1417
+ userId: '456',
1418
+ role: 'user',
1419
+ content: 'test message',
1420
+ metadata: { originalKey: 'originalValue' },
1421
+ });
1422
+
1423
+ // 调用 updateMetadata 方法
1424
+ const result = await messageModel.updateMetadata('msg-other-user', {
1425
+ hackedKey: 'hackedValue',
1426
+ });
1427
+
1428
+ // 断言结果 - 应该返回 undefined
1429
+ expect(result).toBeUndefined();
1430
+
1431
+ // 验证原始 metadata 未被修改
1432
+ const dbResult = await serverDB
1433
+ .select()
1434
+ .from(messages)
1435
+ .where(eq(messages.id, 'msg-other-user'));
1436
+
1437
+ expect(dbResult[0].metadata).toEqual({ originalKey: 'originalValue' });
1438
+ });
1439
+
1440
+ it('should handle complex nested metadata updates', async () => {
1441
+ // 创建测试数据
1442
+ await serverDB.insert(messages).values({
1443
+ id: 'msg-complex-metadata',
1444
+ userId,
1445
+ role: 'assistant',
1446
+ content: 'test message',
1447
+ metadata: {
1448
+ config: {
1449
+ settings: {
1450
+ enabled: true,
1451
+ options: ['a', 'b'],
1452
+ },
1453
+ version: 1,
1454
+ },
1455
+ },
1456
+ });
1457
+
1458
+ // 调用 updateMetadata 方法
1459
+ await messageModel.updateMetadata('msg-complex-metadata', {
1460
+ config: {
1461
+ settings: {
1462
+ enabled: false,
1463
+ timeout: 5000,
1464
+ },
1465
+ newField: 'value',
1466
+ },
1467
+ stats: { count: 10 },
1468
+ });
1469
+
1470
+ // 断言结果
1471
+ const result = await serverDB
1472
+ .select()
1473
+ .from(messages)
1474
+ .where(eq(messages.id, 'msg-complex-metadata'));
1475
+
1476
+ expect(result[0].metadata).toEqual({
1477
+ config: {
1478
+ settings: {
1479
+ enabled: false,
1480
+ options: ['a', 'b'],
1481
+ timeout: 5000,
1482
+ },
1483
+ version: 1,
1484
+ newField: 'value',
1485
+ },
1486
+ stats: { count: 10 },
1487
+ });
1488
+ });
1489
+ });
1490
+
1293
1491
  describe('updateTranslate', () => {
1294
1492
  it('should insert a new record if message does not exist in messageTranslates table', async () => {
1295
1493
  // 创建测试数据
@@ -570,6 +570,19 @@ export class MessageModel {
570
570
  });
571
571
  };
572
572
 
573
+ updateMetadata = async (id: string, metadata: Record<string, any>) => {
574
+ const item = await this.db.query.messages.findFirst({
575
+ where: and(eq(messages.id, id), eq(messages.userId, this.userId)),
576
+ });
577
+
578
+ if (!item) return;
579
+
580
+ return this.db
581
+ .update(messages)
582
+ .set({ metadata: merge(item.metadata || {}, metadata) })
583
+ .where(and(eq(messages.userId, this.userId), eq(messages.id, id)));
584
+ };
585
+
573
586
  updatePluginState = async (id: string, state: Record<string, any>) => {
574
587
  const item = await this.db.query.messagePlugins.findFirst({
575
588
  where: eq(messagePlugins.id, id),