@lobehub/lobehub 2.0.0-next.97 → 2.0.0-next.99

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 (150) hide show
  1. package/.console-log-whitelist.json +14 -0
  2. package/.github/workflows/check-console-log.yml +117 -0
  3. package/.github/workflows/desktop-pr-build.yml +4 -4
  4. package/.github/workflows/release-desktop-beta.yml +4 -4
  5. package/.github/workflows/release.yml +1 -1
  6. package/.github/workflows/test.yml +5 -5
  7. package/CHANGELOG.md +58 -0
  8. package/apps/desktop/src/main/services/__tests__/fileSrv.test.ts +603 -0
  9. package/changelog/v1.json +21 -0
  10. package/codecov.yml +1 -0
  11. package/docs/development/database-schema.dbml +1 -0
  12. package/e2e/package.json +1 -1
  13. package/locales/ar/file.json +9 -11
  14. package/locales/ar/plugin.json +34 -22
  15. package/locales/ar/tool.json +8 -0
  16. package/locales/bg-BG/file.json +8 -10
  17. package/locales/bg-BG/plugin.json +34 -22
  18. package/locales/bg-BG/tool.json +8 -0
  19. package/locales/de-DE/file.json +9 -11
  20. package/locales/de-DE/plugin.json +34 -22
  21. package/locales/de-DE/tool.json +8 -0
  22. package/locales/en-US/file.json +12 -14
  23. package/locales/en-US/plugin.json +34 -22
  24. package/locales/en-US/tool.json +8 -0
  25. package/locales/es-ES/file.json +7 -9
  26. package/locales/es-ES/plugin.json +34 -22
  27. package/locales/es-ES/tool.json +8 -0
  28. package/locales/fa-IR/file.json +9 -11
  29. package/locales/fa-IR/plugin.json +34 -22
  30. package/locales/fa-IR/tool.json +8 -0
  31. package/locales/fr-FR/file.json +6 -8
  32. package/locales/fr-FR/plugin.json +34 -22
  33. package/locales/fr-FR/tool.json +8 -0
  34. package/locales/it-IT/file.json +8 -10
  35. package/locales/it-IT/plugin.json +34 -22
  36. package/locales/it-IT/tool.json +8 -0
  37. package/locales/ja-JP/file.json +10 -12
  38. package/locales/ja-JP/plugin.json +34 -22
  39. package/locales/ja-JP/tool.json +8 -0
  40. package/locales/ko-KR/file.json +8 -10
  41. package/locales/ko-KR/plugin.json +34 -22
  42. package/locales/ko-KR/tool.json +8 -0
  43. package/locales/nl-NL/file.json +8 -10
  44. package/locales/nl-NL/plugin.json +34 -22
  45. package/locales/nl-NL/tool.json +8 -0
  46. package/locales/pl-PL/file.json +7 -9
  47. package/locales/pl-PL/plugin.json +34 -22
  48. package/locales/pl-PL/tool.json +8 -0
  49. package/locales/pt-BR/file.json +7 -9
  50. package/locales/pt-BR/plugin.json +34 -22
  51. package/locales/pt-BR/tool.json +8 -0
  52. package/locales/ru-RU/file.json +9 -11
  53. package/locales/ru-RU/plugin.json +34 -22
  54. package/locales/ru-RU/tool.json +8 -0
  55. package/locales/tr-TR/file.json +8 -10
  56. package/locales/tr-TR/plugin.json +34 -22
  57. package/locales/tr-TR/tool.json +8 -0
  58. package/locales/vi-VN/file.json +9 -11
  59. package/locales/vi-VN/plugin.json +34 -22
  60. package/locales/vi-VN/tool.json +8 -0
  61. package/locales/zh-CN/file.json +10 -12
  62. package/locales/zh-CN/plugin.json +34 -22
  63. package/locales/zh-CN/tool.json +8 -0
  64. package/locales/zh-TW/file.json +10 -12
  65. package/locales/zh-TW/plugin.json +34 -22
  66. package/locales/zh-TW/tool.json +8 -0
  67. package/package.json +3 -2
  68. package/packages/database/migrations/0047_add_slug_document.sql +6 -0
  69. package/packages/database/migrations/meta/0047_snapshot.json +7891 -0
  70. package/packages/database/migrations/meta/_journal.json +7 -0
  71. package/packages/database/src/core/migrations.json +16 -7
  72. package/packages/database/src/models/__tests__/document.test.ts +149 -0
  73. package/packages/database/src/models/chunk.ts +3 -1
  74. package/packages/database/src/models/document.ts +10 -4
  75. package/packages/database/src/schemas/file.ts +7 -1
  76. package/packages/model-bank/src/aiModels/qwen.ts +5 -3
  77. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +21 -21
  78. package/packages/obervability-otel/package.json +2 -2
  79. package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatFileContents.test.ts.snap +75 -0
  80. package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatNoSearchResults.test.ts.snap +45 -0
  81. package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatSearchResults.test.ts.snap +82 -0
  82. package/packages/prompts/src/prompts/knowledgeBaseQA/formatFileContents.test.ts +118 -0
  83. package/packages/prompts/src/prompts/knowledgeBaseQA/formatFileContents.ts +31 -0
  84. package/packages/prompts/src/prompts/knowledgeBaseQA/formatNoSearchResults.test.ts +25 -0
  85. package/packages/prompts/src/prompts/knowledgeBaseQA/formatNoSearchResults.ts +13 -0
  86. package/packages/prompts/src/prompts/knowledgeBaseQA/formatSearchResults.test.ts +191 -0
  87. package/packages/prompts/src/prompts/knowledgeBaseQA/formatSearchResults.ts +50 -0
  88. package/packages/prompts/src/prompts/knowledgeBaseQA/index.ts +6 -0
  89. package/packages/types/src/rag.ts +13 -4
  90. package/scripts/checkConsoleLog.mts +148 -0
  91. package/scripts/prebuild.mts +5 -5
  92. package/src/app/[variants]/(main)/changelog/index.tsx +1 -1
  93. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +20 -16
  94. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +6 -3
  95. package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +3 -2
  96. package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +14 -4
  97. package/src/app/[variants]/desktopRouter.config.tsx +23 -0
  98. package/src/app/[variants]/mobileRouter.config.tsx +23 -0
  99. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +2 -2
  100. package/src/features/ChatInput/ActionBar/Token/TokenTagForGroupChat.tsx +2 -2
  101. package/src/features/ChatList/Messages/Group/Tool/Inspector/ToolTitle.tsx +5 -23
  102. package/src/features/ChatList/Messages/Tool/Inspector/ToolTitle.tsx +5 -25
  103. package/src/features/KnowledgeManager/DocumentExplorer/NoteEditorModal.tsx +0 -20
  104. package/src/features/KnowledgeManager/DocumentExplorer/index.tsx +3 -3
  105. package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/index.tsx +0 -20
  106. package/src/features/KnowledgeManager/Header/AddButton.tsx +0 -1
  107. package/src/features/KnowledgeManager/Header/NewNoteButton.tsx +1 -1
  108. package/src/features/KnowledgeManager/Header/TogglePanelButton.tsx +2 -2
  109. package/src/features/KnowledgeManager/Home/UploadEntries.tsx +2 -2
  110. package/src/features/KnowledgeManager/Home/index.tsx +4 -4
  111. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -1
  112. package/src/features/User/UserPanel/useMenu.tsx +7 -3
  113. package/src/helpers/toolEngineering/index.test.ts +3 -3
  114. package/src/helpers/toolEngineering/index.ts +17 -4
  115. package/src/libs/trpc/client/lambda.ts +0 -6
  116. package/src/locales/default/file.ts +11 -13
  117. package/src/locales/default/plugin.ts +34 -22
  118. package/src/locales/default/tool.ts +13 -5
  119. package/src/server/routers/lambda/chunk.ts +168 -41
  120. package/src/services/chat/chat.test.ts +3 -3
  121. package/src/services/chat/index.ts +2 -2
  122. package/src/services/rag.ts +6 -2
  123. package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +1 -4
  124. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +11 -0
  125. package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +0 -87
  126. package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +2 -69
  127. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +0 -2
  128. package/src/store/chat/slices/aiChat/actions/rag.ts +0 -47
  129. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +8 -69
  130. package/src/store/chat/slices/builtinTool/actions/index.ts +4 -1
  131. package/src/store/chat/slices/builtinTool/actions/knowledgeBase.ts +174 -0
  132. package/src/store/chat/slices/operation/types.ts +1 -0
  133. package/src/store/chat/slices/thread/action.test.ts +0 -1
  134. package/src/store/chat/slices/thread/action.ts +0 -1
  135. package/src/tools/executionRuntimes.ts +3 -0
  136. package/src/tools/identifiers.ts +13 -0
  137. package/src/tools/index.ts +7 -0
  138. package/src/tools/knowledge-base/ExecutionRuntime/index.ts +96 -0
  139. package/src/tools/knowledge-base/Render/ReadKnowledge/FileCard.tsx +135 -0
  140. package/src/tools/knowledge-base/Render/ReadKnowledge/index.tsx +27 -0
  141. package/src/tools/knowledge-base/Render/SearchKnowledgeBase/Item/index.tsx +54 -0
  142. package/src/tools/knowledge-base/Render/SearchKnowledgeBase/Item/style.ts +51 -0
  143. package/src/tools/knowledge-base/Render/SearchKnowledgeBase/index.tsx +23 -0
  144. package/src/tools/knowledge-base/Render/index.ts +7 -0
  145. package/src/tools/knowledge-base/index.ts +64 -0
  146. package/src/tools/knowledge-base/systemRole.ts +102 -0
  147. package/src/tools/knowledge-base/type.ts +25 -0
  148. package/src/tools/local-system/Intervention/WriteFile/index.tsx +1 -1
  149. package/src/tools/renders.ts +4 -0
  150. package/src/store/chat/agents/createToolEngine.ts +0 -22
@@ -329,6 +329,13 @@
329
329
  "when": 1763453175961,
330
330
  "tag": "0046_add_parent_id",
331
331
  "breakpoints": true
332
+ },
333
+ {
334
+ "idx": 47,
335
+ "version": "7",
336
+ "when": 1763535087148,
337
+ "tag": "0047_add_slug_document",
338
+ "breakpoints": true
332
339
  }
333
340
  ],
334
341
  "version": "6"
@@ -778,15 +778,24 @@
778
778
  {
779
779
  "sql": [
780
780
  "ALTER TABLE \"documents\" ALTER COLUMN \"id\" SET DATA TYPE varchar(255);",
781
- "\nALTER TABLE \"documents\" ADD COLUMN \"parent_id\" varchar(255);",
782
- "\nALTER TABLE \"files\" ADD COLUMN \"parent_id\" varchar(255);",
783
- "\nALTER TABLE \"documents\" ADD CONSTRAINT \"documents_parent_id_documents_id_fk\" FOREIGN KEY (\"parent_id\") REFERENCES \"public\".\"documents\"(\"id\") ON DELETE set null ON UPDATE no action;",
784
- "\nALTER TABLE \"files\" ADD CONSTRAINT \"files_parent_id_documents_id_fk\" FOREIGN KEY (\"parent_id\") REFERENCES \"public\".\"documents\"(\"id\") ON DELETE set null ON UPDATE no action;",
785
- "\nCREATE INDEX \"documents_parent_id_idx\" ON \"documents\" USING btree (\"parent_id\");",
786
- "\nCREATE INDEX \"files_parent_id_idx\" ON \"files\" USING btree (\"parent_id\");"
781
+ "\nALTER TABLE \"documents\" ADD COLUMN IF NOT EXISTS \"parent_id\" varchar(255);",
782
+ "\nALTER TABLE \"files\" ADD COLUMN IF NOT EXISTS \"parent_id\" varchar(255);",
783
+ "\nDO $$ BEGIN\n ALTER TABLE \"documents\" ADD CONSTRAINT \"documents_parent_id_documents_id_fk\" FOREIGN KEY (\"parent_id\") REFERENCES \"public\".\"documents\"(\"id\") ON DELETE set null ON UPDATE no action;\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;",
784
+ "\nDO $$ BEGIN\n ALTER TABLE \"files\" ADD CONSTRAINT \"files_parent_id_documents_id_fk\" FOREIGN KEY (\"parent_id\") REFERENCES \"public\".\"documents\"(\"id\") ON DELETE set null ON UPDATE no action;\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;",
785
+ "\nCREATE INDEX IF NOT EXISTS \"documents_parent_id_idx\" ON \"documents\" USING btree (\"parent_id\");",
786
+ "\nCREATE INDEX IF NOT EXISTS \"files_parent_id_idx\" ON \"files\" USING btree (\"parent_id\");"
787
787
  ],
788
788
  "bps": true,
789
789
  "folderMillis": 1763453175961,
790
- "hash": "6cfc00744de6a8f4d60b793673911bb740f9a50661663e28b843e5adae08f94a"
790
+ "hash": "6c2081c8ac22772a8276052c9ae0852a4c03463cbe998df26f85683ab356b914"
791
+ },
792
+ {
793
+ "sql": [
794
+ "ALTER TABLE \"documents\" ADD COLUMN IF NOT EXISTS \"slug\" varchar(255);",
795
+ "\nDO $$ BEGIN\n CREATE UNIQUE INDEX IF NOT EXISTS \"documents_slug_user_id_unique\" ON \"documents\" (\"slug\",\"user_id\") WHERE \"slug\" IS NOT NULL;\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;"
796
+ ],
797
+ "bps": true,
798
+ "folderMillis": 1763535087148,
799
+ "hash": "57a82ce14d96ffa9f140ff63f00af994e91a74703f4d2378286e36be259f117b"
791
800
  }
792
801
  ]
@@ -0,0 +1,149 @@
1
+ // @vitest-environment node
2
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
3
+
4
+ import { documents, files, users } from '../../schemas';
5
+ import { LobeChatDatabase } from '../../type';
6
+ import { DocumentModel } from '../document';
7
+ import { FileModel } from '../file';
8
+ import { getTestDB } from './_util';
9
+
10
+ const serverDB: LobeChatDatabase = await getTestDB();
11
+
12
+ const userId = 'document-model-test-user-id';
13
+ const userId2 = 'document-model-test-user-id-2';
14
+ const documentModel = new DocumentModel(serverDB, userId);
15
+ const documentModel2 = new DocumentModel(serverDB, userId2);
16
+ const fileModel = new FileModel(serverDB, userId);
17
+ const fileModel2 = new FileModel(serverDB, userId2);
18
+
19
+ beforeEach(async () => {
20
+ await serverDB.delete(users);
21
+ await serverDB.insert(users).values([{ id: userId }, { id: userId2 }]);
22
+ });
23
+
24
+ afterEach(async () => {
25
+ await serverDB.delete(users);
26
+ await serverDB.delete(files);
27
+ await serverDB.delete(documents);
28
+ });
29
+
30
+ // Helper to create a minimal valid document
31
+ const createTestDocument = async (model: DocumentModel, fModel: FileModel, content: string) => {
32
+ const { id: fileId } = await fModel.create({
33
+ fileType: 'text/plain',
34
+ name: 'test.txt',
35
+ size: 100,
36
+ url: 'https://example.com/test.txt',
37
+ });
38
+
39
+ // Fetch the file to get complete data
40
+ const file = await fModel.findById(fileId);
41
+ if (!file) throw new Error('File not found after creation');
42
+
43
+ const { id } = await model.create({
44
+ content,
45
+ fileId: file.id,
46
+ fileType: 'text/plain',
47
+ source: file.url,
48
+ sourceType: 'file',
49
+ totalCharCount: content.length,
50
+ totalLineCount: content.split('\n').length,
51
+ });
52
+
53
+ return { documentId: id, file };
54
+ };
55
+
56
+ describe('DocumentModel', () => {
57
+ describe('findByFileId', () => {
58
+ it('should find document by fileId', async () => {
59
+ const { documentId, file } = await createTestDocument(
60
+ documentModel,
61
+ fileModel,
62
+ 'Test content for file',
63
+ );
64
+
65
+ const found = await documentModel.findByFileId(file.id);
66
+ expect(found).toBeDefined();
67
+ expect(found?.id).toBe(documentId);
68
+ expect(found?.fileId).toBe(file.id);
69
+ expect(found?.content).toBe('Test content for file');
70
+ });
71
+
72
+ it('should not find document from another user', async () => {
73
+ const { file } = await createTestDocument(documentModel, fileModel, 'Test content');
74
+
75
+ // Try to find with another user's model
76
+ const found = await documentModel2.findByFileId(file.id);
77
+ expect(found).toBeUndefined();
78
+ });
79
+
80
+ it('should return undefined for non-existent fileId', async () => {
81
+ const found = await documentModel.findByFileId('non-existent-file-id');
82
+ expect(found).toBeUndefined();
83
+ });
84
+
85
+ it('should return the first document when multiple documents exist for same file', async () => {
86
+ const { id: fileId } = await fileModel.create({
87
+ fileType: 'text/plain',
88
+ name: 'test.txt',
89
+ size: 100,
90
+ url: 'https://example.com/test.txt',
91
+ });
92
+
93
+ const file = await fileModel.findById(fileId);
94
+ if (!file) throw new Error('File not found after creation');
95
+
96
+ const { id: firstId } = await documentModel.create({
97
+ content: 'First document',
98
+ fileId: file.id,
99
+ fileType: 'text/plain',
100
+ source: file.url,
101
+ sourceType: 'file',
102
+ totalCharCount: 14,
103
+ totalLineCount: 1,
104
+ });
105
+
106
+ await documentModel.create({
107
+ content: 'Second document',
108
+ fileId: file.id,
109
+ fileType: 'text/plain',
110
+ source: file.url,
111
+ sourceType: 'file',
112
+ totalCharCount: 15,
113
+ totalLineCount: 1,
114
+ });
115
+
116
+ const found = await documentModel.findByFileId(file.id);
117
+ expect(found).toBeDefined();
118
+ // Should return the first created document
119
+ expect(found?.id).toBe(firstId);
120
+ });
121
+
122
+ it('should handle different file types', async () => {
123
+ const { id: pdfFileId } = await fileModel.create({
124
+ fileType: 'application/pdf',
125
+ name: 'document.pdf',
126
+ size: 5000,
127
+ url: 'https://example.com/document.pdf',
128
+ });
129
+
130
+ const pdfFile = await fileModel.findById(pdfFileId);
131
+ if (!pdfFile) throw new Error('File not found after creation');
132
+
133
+ await documentModel.create({
134
+ content: 'PDF content',
135
+ fileId: pdfFile.id,
136
+ fileType: 'application/pdf',
137
+ source: pdfFile.url,
138
+ sourceType: 'file',
139
+ totalCharCount: 11,
140
+ totalLineCount: 1,
141
+ });
142
+
143
+ const found = await documentModel.findByFileId(pdfFile.id);
144
+ expect(found).toBeDefined();
145
+ expect(found?.fileType).toBe('application/pdf');
146
+ expect(found?.content).toBe('PDF content');
147
+ });
148
+ });
149
+ });
@@ -181,10 +181,12 @@ export class ChunkModel {
181
181
  semanticSearchForChat = async ({
182
182
  embedding,
183
183
  fileIds,
184
+ topK = 15,
184
185
  }: {
185
186
  embedding: number[];
186
187
  fileIds: string[] | undefined;
187
188
  query: string;
189
+ topK?: number;
188
190
  }) => {
189
191
  const similarity = sql<number>`1 - (${cosineDistance(embeddings.embeddings, embedding)})`;
190
192
 
@@ -210,7 +212,7 @@ export class ChunkModel {
210
212
  .where(inArray(fileChunks.fileId, fileIds))
211
213
  .orderBy((t) => desc(t.similarity))
212
214
  // Relaxed to 15 for now
213
- .limit(15);
215
+ .limit(topK);
214
216
 
215
217
  return result.map((item) => {
216
218
  return {
@@ -31,16 +31,22 @@ export class DocumentModel {
31
31
  return this.db.delete(documents).where(eq(documents.userId, this.userId));
32
32
  };
33
33
 
34
- query = async () => {
34
+ query = async (): Promise<DocumentItem[]> => {
35
35
  return this.db.query.documents.findMany({
36
36
  orderBy: [desc(documents.updatedAt)],
37
37
  where: eq(documents.userId, this.userId),
38
38
  });
39
39
  };
40
40
 
41
- findById = async (id: string) => {
41
+ findById = async (id: string): Promise<DocumentItem | undefined> => {
42
42
  return this.db.query.documents.findFirst({
43
- where: and(eq(documents.id, id), eq(documents.userId, this.userId)),
43
+ where: and(eq(documents.userId, this.userId), eq(documents.id, id)),
44
+ });
45
+ };
46
+
47
+ findByFileId = async (fileId: string) => {
48
+ return this.db.query.documents.findFirst({
49
+ where: and(eq(documents.userId, this.userId), eq(documents.fileId, fileId)),
44
50
  });
45
51
  };
46
52
 
@@ -48,6 +54,6 @@ export class DocumentModel {
48
54
  return this.db
49
55
  .update(documents)
50
56
  .set({ ...value, updatedAt: new Date() })
51
- .where(and(eq(documents.id, id), eq(documents.userId, this.userId)));
57
+ .where(and(eq(documents.userId, this.userId), eq(documents.id, id)));
52
58
  };
53
59
  }
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
+ import { isNotNull } from 'drizzle-orm';
2
3
  import {
3
4
  boolean,
4
5
  index,
@@ -91,6 +92,8 @@ export const documents = pgTable(
91
92
 
92
93
  editorData: jsonb('editor_data').$type<Record<string, any>>(),
93
94
 
95
+ slug: varchar('slug', { length: 255 }),
96
+
94
97
  // Timestamps
95
98
  ...timestamps,
96
99
  },
@@ -100,6 +103,9 @@ export const documents = pgTable(
100
103
  index('documents_file_id_idx').on(table.fileId),
101
104
  index('documents_parent_id_idx').on(table.parentId),
102
105
  uniqueIndex('documents_client_id_user_id_unique').on(table.clientId, table.userId),
106
+ uniqueIndex('documents_slug_user_id_unique')
107
+ .on(table.slug, table.userId)
108
+ .where(isNotNull(table.slug)),
103
109
  ],
104
110
  );
105
111
 
@@ -133,7 +139,7 @@ export const files = pgTable(
133
139
  url: text('url').notNull(),
134
140
  source: text('source').$type<FileSource>(),
135
141
 
136
- // Parent document (for folder hierarchy structure)
142
+ // Parent Folder or Document
137
143
  // @ts-ignore
138
144
  parentId: varchar('parent_id', { length: 255 }).references(() => documents.id, {
139
145
  onDelete: 'set null',
@@ -1499,11 +1499,12 @@ const qwenChatModels: AIChatModelCard[] = [
1499
1499
  },
1500
1500
  {
1501
1501
  abilities: {
1502
- vision: true,
1503
1502
  reasoning: true,
1503
+ vision: true,
1504
1504
  },
1505
1505
  contextWindowTokens: 131_072,
1506
- description: 'Qwen3 VL 32B 思考模式(开源版),针对高难度强推理与长视频理解场景,提供顶尖的视觉+文本推理能力。',
1506
+ description:
1507
+ 'Qwen3 VL 32B 思考模式(开源版),针对高难度强推理与长视频理解场景,提供顶尖的视觉+文本推理能力。',
1507
1508
  displayName: 'Qwen3 VL 32B Thinking',
1508
1509
  id: 'qwen3-vl-32b-thinking',
1509
1510
  maxOutput: 32_768,
@@ -1525,7 +1526,8 @@ const qwenChatModels: AIChatModelCard[] = [
1525
1526
  vision: true,
1526
1527
  },
1527
1528
  contextWindowTokens: 131_072,
1528
- description: 'Qwen3 VL 32B 非思考模式(Instruct),适用于非思考指令场景,保持强大的视觉理解能力。',
1529
+ description:
1530
+ 'Qwen3 VL 32B 非思考模式(Instruct),适用于非思考指令场景,保持强大的视觉理解能力。',
1529
1531
  displayName: 'Qwen3 VL 32B Instruct',
1530
1532
  id: 'qwen3-vl-32b-instruct',
1531
1533
  maxOutput: 32_768,
@@ -138,10 +138,10 @@ export interface OpenAICompatibleFactoryOptions<T extends Record<string, any> =
138
138
  useToolsCalling?: boolean;
139
139
  };
140
140
  models?:
141
- | ((params: { client: OpenAI }) => Promise<ChatModelCard[]>)
142
- | {
143
- transformModel?: (model: OpenAI.Model) => ChatModelCard;
144
- };
141
+ | ((params: { client: OpenAI }) => Promise<ChatModelCard[]>)
142
+ | {
143
+ transformModel?: (model: OpenAI.Model) => ChatModelCard;
144
+ };
145
145
  provider: string;
146
146
  responses?: {
147
147
  handlePayload?: (
@@ -317,9 +317,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
317
317
  const postPayload = chatCompletion?.handlePayload
318
318
  ? chatCompletion.handlePayload(processedPayload, this._options)
319
319
  : ({
320
- ...processedPayload,
321
- stream: processedPayload.stream ?? true,
322
- } as OpenAI.ChatCompletionCreateParamsStreaming);
320
+ ...processedPayload,
321
+ stream: processedPayload.stream ?? true,
322
+ } as OpenAI.ChatCompletionCreateParamsStreaming);
323
323
 
324
324
  if ((postPayload as any).apiMode === 'responses') {
325
325
  return this.handleResponseAPIMode(processedPayload, options);
@@ -385,13 +385,13 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
385
385
  return StreamingResponse(
386
386
  chatCompletion?.handleStream
387
387
  ? chatCompletion.handleStream(prod, {
388
- callbacks: streamOptions.callbacks,
389
- inputStartAt,
390
- })
388
+ callbacks: streamOptions.callbacks,
389
+ inputStartAt,
390
+ })
391
391
  : OpenAIStream(prod, {
392
- ...streamOptions,
393
- inputStartAt,
394
- }),
392
+ ...streamOptions,
393
+ inputStartAt,
394
+ }),
395
395
  {
396
396
  headers: options?.headers,
397
397
  },
@@ -415,9 +415,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
415
415
  return StreamingResponse(
416
416
  chatCompletion?.handleStream
417
417
  ? chatCompletion.handleStream(stream, {
418
- callbacks: streamOptions.callbacks,
419
- inputStartAt,
420
- })
418
+ callbacks: streamOptions.callbacks,
419
+ inputStartAt,
420
+ })
421
421
  : OpenAIStream(stream, { ...streamOptions, enableStreaming: false, inputStartAt }),
422
422
  {
423
423
  headers: options?.headers,
@@ -839,11 +839,11 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
839
839
  ...res,
840
840
  ...(reasoning || reasoning_effort
841
841
  ? {
842
- reasoning: {
843
- ...reasoning,
844
- ...(reasoning_effort && { effort: reasoning_effort }),
845
- },
846
- }
842
+ reasoning: {
843
+ ...reasoning,
844
+ ...(reasoning_effort && { effort: reasoning_effort }),
845
+ },
846
+ }
847
847
  : {}),
848
848
  input,
849
849
  ...(max_tokens && { max_output_tokens: max_tokens }),
@@ -9,12 +9,12 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "@opentelemetry/api": "^1.9.0",
12
- "@opentelemetry/auto-instrumentations-node": "^0.66.0",
12
+ "@opentelemetry/auto-instrumentations-node": "^0.67.0",
13
13
  "@opentelemetry/exporter-metrics-otlp-http": "^0.208.0",
14
14
  "@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
15
15
  "@opentelemetry/instrumentation": "^0.208.0",
16
16
  "@opentelemetry/instrumentation-http": "^0.208.0",
17
- "@opentelemetry/instrumentation-pg": "^0.60.0",
17
+ "@opentelemetry/instrumentation-pg": "^0.61.0",
18
18
  "@opentelemetry/resources": "^2.2.0",
19
19
  "@opentelemetry/sdk-metrics": "^2.2.0",
20
20
  "@opentelemetry/sdk-node": "^0.208.0",
@@ -0,0 +1,75 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`promptFileContents > should format multiple file contents 1`] = `
4
+ "<knowledge_base_files totalCount="3">
5
+ <instruction>Use the information from these files to answer the user's question. Always cite the source files.</instruction>
6
+ <file id="file-001" name="auth-guide.md">
7
+ First file content about API authentication.
8
+ </file>
9
+ <file id="file-002" name="db-setup.md">
10
+ Second file content about database setup and configuration.
11
+ </file>
12
+ <file id="file-003" name="deployment.md">
13
+ Third file content with deployment instructions.
14
+ </file>
15
+ </knowledge_base_files>"
16
+ `;
17
+
18
+ exports[`promptFileContents > should format single file content 1`] = `
19
+ "<knowledge_base_files totalCount="1">
20
+ <instruction>Use the information from these files to answer the user's question. Always cite the source files.</instruction>
21
+ <file id="file-001" name="document.md">
22
+ This is the file content with some important information.
23
+ </file>
24
+ </knowledge_base_files>"
25
+ `;
26
+
27
+ exports[`promptFileContents > should handle file with error 1`] = `
28
+ "<knowledge_base_files totalCount="1">
29
+ <instruction>Use the information from these files to answer the user's question. Always cite the source files.</instruction>
30
+ <file id="file-404" name="missing.md" error="File not found" />
31
+ </knowledge_base_files>"
32
+ `;
33
+
34
+ exports[`promptFileContents > should handle file with long content 1`] = `
35
+ "<knowledge_base_files totalCount="1">
36
+ <instruction>Use the information from these files to answer the user's question. Always cite the source files.</instruction>
37
+ <file id="file-long" name="comprehensive-guide.md">
38
+ # Comprehensive Guide
39
+
40
+ ## Introduction
41
+ This is a very long document with multiple sections and detailed information.
42
+
43
+ ## Section 1
44
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
45
+
46
+ ## Section 2
47
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
48
+
49
+ ## Conclusion
50
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
51
+ </file>
52
+ </knowledge_base_files>"
53
+ `;
54
+
55
+ exports[`promptFileContents > should handle file with special characters in name 1`] = `
56
+ "<knowledge_base_files totalCount="1">
57
+ <instruction>Use the information from these files to answer the user's question. Always cite the source files.</instruction>
58
+ <file id="file-special" name="FAQ: Q&A (2024).md">
59
+ Content from a file with special characters in the name.
60
+ </file>
61
+ </knowledge_base_files>"
62
+ `;
63
+
64
+ exports[`promptFileContents > should handle mixed successful and error files 1`] = `
65
+ "<knowledge_base_files totalCount="3">
66
+ <instruction>Use the information from these files to answer the user's question. Always cite the source files.</instruction>
67
+ <file id="file-001" name="success.md">
68
+ Successfully loaded content from this file.
69
+ </file>
70
+ <file id="file-002" name="restricted.md" error="Permission denied" />
71
+ <file id="file-003" name="another-success.md">
72
+ Another successfully loaded file with more content.
73
+ </file>
74
+ </knowledge_base_files>"
75
+ `;
@@ -0,0 +1,45 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`promptNoSearchResults > should format no search results with complex query 1`] = `
4
+ "<knowledge_base_search_results query="API authentication with OAuth 2.0" totalCount="0">
5
+ <instruction>No relevant files found in the knowledge base for this query.</instruction>
6
+ <suggestions>
7
+ <suggestion>Try rephrasing your question with different keywords</suggestion>
8
+ <suggestion>Check if the information exists in the uploaded documents</suggestion>
9
+ <suggestion>Ask the user to provide more context or upload relevant documents</suggestion>
10
+ </suggestions>
11
+ </knowledge_base_search_results>"
12
+ `;
13
+
14
+ exports[`promptNoSearchResults > should format no search results with non-English query 1`] = `
15
+ "<knowledge_base_search_results query="如何配置数据库连接" totalCount="0">
16
+ <instruction>No relevant files found in the knowledge base for this query.</instruction>
17
+ <suggestions>
18
+ <suggestion>Try rephrasing your question with different keywords</suggestion>
19
+ <suggestion>Check if the information exists in the uploaded documents</suggestion>
20
+ <suggestion>Ask the user to provide more context or upload relevant documents</suggestion>
21
+ </suggestions>
22
+ </knowledge_base_search_results>"
23
+ `;
24
+
25
+ exports[`promptNoSearchResults > should format no search results with simple query 1`] = `
26
+ "<knowledge_base_search_results query="how to install" totalCount="0">
27
+ <instruction>No relevant files found in the knowledge base for this query.</instruction>
28
+ <suggestions>
29
+ <suggestion>Try rephrasing your question with different keywords</suggestion>
30
+ <suggestion>Check if the information exists in the uploaded documents</suggestion>
31
+ <suggestion>Ask the user to provide more context or upload relevant documents</suggestion>
32
+ </suggestions>
33
+ </knowledge_base_search_results>"
34
+ `;
35
+
36
+ exports[`promptNoSearchResults > should format no search results with special characters 1`] = `
37
+ "<knowledge_base_search_results query="How to use fetchData<T> with async/await?" totalCount="0">
38
+ <instruction>No relevant files found in the knowledge base for this query.</instruction>
39
+ <suggestions>
40
+ <suggestion>Try rephrasing your question with different keywords</suggestion>
41
+ <suggestion>Check if the information exists in the uploaded documents</suggestion>
42
+ <suggestion>Ask the user to provide more context or upload relevant documents</suggestion>
43
+ </suggestions>
44
+ </knowledge_base_search_results>"
45
+ `;
@@ -0,0 +1,82 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`formatSearchResults > should format file with special characters in name 1`] = `
4
+ "<knowledge_base_search_results query="password reset" totalCount="1">
5
+ <instruction>Here are the search results from the knowledge base. Use the readKnowledge tool with file IDs to get complete content.</instruction>
6
+ <file id="special-123" name="FAQ: Common Questions & Answers (2024).md" relevanceScore="0.88">
7
+ <chunk fileId="special-123" fileName="FAQ: Common Questions & Answers (2024).md" similarity="0.9">Q: How do I reset my password? A: Click on the "Forgot Password" link on the login page.</chunk>
8
+ </file>
9
+ </knowledge_base_search_results>"
10
+ `;
11
+
12
+ exports[`formatSearchResults > should format long text chunks without truncation 1`] = `
13
+ "<knowledge_base_search_results query="documentation details" totalCount="1">
14
+ <instruction>Here are the search results from the knowledge base. Use the readKnowledge tool with file IDs to get complete content.</instruction>
15
+ <file id="long-doc" name="Detailed Documentation.md" relevanceScore="0.91">
16
+ <chunk fileId="long-doc" fileName="Detailed Documentation.md" similarity="0.93">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</chunk>
17
+ </file>
18
+ </knowledge_base_search_results>"
19
+ `;
20
+
21
+ exports[`formatSearchResults > should format multiple files with varying relevance scores 1`] = `
22
+ "<knowledge_base_search_results query="API authentication security" totalCount="3">
23
+ <instruction>Here are the search results from the knowledge base. Use the readKnowledge tool with file IDs to get complete content.</instruction>
24
+ <file id="doc-001" name="API Reference.md" relevanceScore="0.95">
25
+ <chunk fileId="doc-001" fileName="API Reference.md" similarity="0.98">The Authentication API provides endpoints for user login, logout, and token refresh. All endpoints require HTTPS and proper API keys.</chunk>
26
+ <chunk fileId="doc-001" fileName="API Reference.md" similarity="0.92">To authenticate a user, send a POST request to /api/auth/login with username and password in the request body.</chunk>
27
+ </file>
28
+ <file id="doc-002" name="Security Best Practices.md" relevanceScore="0.87">
29
+ <chunk fileId="doc-002" fileName="Security Best Practices.md" similarity="0.89">Always use environment variables to store sensitive information like API keys and database credentials. Never commit these to version control.</chunk>
30
+ <chunk fileId="doc-002" fileName="Security Best Practices.md" similarity="0.85">Implement rate limiting on all public endpoints to prevent abuse and DDoS attacks.</chunk>
31
+ </file>
32
+ <file id="doc-003" name="Troubleshooting Guide.md" relevanceScore="0.73">
33
+ <chunk fileId="doc-003" fileName="Troubleshooting Guide.md" similarity="0.75">If you encounter authentication errors, first verify that your API key is valid and has not expired.</chunk>
34
+ </file>
35
+ </knowledge_base_search_results>"
36
+ `;
37
+
38
+ exports[`formatSearchResults > should format query with special characters 1`] = `
39
+ "<knowledge_base_search_results query="How to use fetchData<T> with async/await?" totalCount="1">
40
+ <instruction>Here are the search results from the knowledge base. Use the readKnowledge tool with file IDs to get complete content.</instruction>
41
+ <file id="code-sample" name="Code Examples.md" relevanceScore="0.94">
42
+ <chunk fileId="code-sample" fileName="Code Examples.md" similarity="0.96">Use the following TypeScript code: const result = await fetchData<T>({ url, params });</chunk>
43
+ </file>
44
+ </knowledge_base_search_results>"
45
+ `;
46
+
47
+ exports[`formatSearchResults > should format single file with multiple chunks 1`] = `
48
+ "<knowledge_base_search_results query="how to install the application" totalCount="1">
49
+ <instruction>Here are the search results from the knowledge base. Use the readKnowledge tool with file IDs to get complete content.</instruction>
50
+ <file id="file-123" name="Getting Started Guide.md" relevanceScore="0.92">
51
+ <chunk fileId="file-123" fileName="Getting Started Guide.md" similarity="0.95">To get started with the application, first install all dependencies using npm install or yarn install. Make sure you have Node.js version 16 or higher installed on your system.</chunk>
52
+ <chunk fileId="file-123" fileName="Getting Started Guide.md" similarity="0.88">After installation, you can run the development server with npm run dev. The application will be available at http://localhost:3000 by default.</chunk>
53
+ </file>
54
+ </knowledge_base_search_results>"
55
+ `;
56
+
57
+ exports[`formatSearchResults > should handle empty file results 1`] = `
58
+ "<knowledge_base_search_results query="no results query" totalCount="0">
59
+ <instruction>No relevant files found in the knowledge base for this query.</instruction>
60
+ </knowledge_base_search_results>"
61
+ `;
62
+
63
+ exports[`formatSearchResults > should handle file with multiple high-similarity chunks 1`] = `
64
+ "<knowledge_base_search_results query="project setup steps" totalCount="1">
65
+ <instruction>Here are the search results from the knowledge base. Use the readKnowledge tool with file IDs to get complete content.</instruction>
66
+ <file id="comprehensive-guide" name="Complete Tutorial.md" relevanceScore="0.96">
67
+ <chunk fileId="comprehensive-guide" fileName="Complete Tutorial.md" similarity="0.98">Step 1: Initialize the project with the configuration file.</chunk>
68
+ <chunk fileId="comprehensive-guide" fileName="Complete Tutorial.md" similarity="0.97">Step 2: Configure the environment variables for your deployment.</chunk>
69
+ <chunk fileId="comprehensive-guide" fileName="Complete Tutorial.md" similarity="0.96">Step 3: Run the migration scripts to set up the database schema.</chunk>
70
+ <chunk fileId="comprehensive-guide" fileName="Complete Tutorial.md" similarity="0.95">Step 4: Start the development server and verify everything is working.</chunk>
71
+ </file>
72
+ </knowledge_base_search_results>"
73
+ `;
74
+
75
+ exports[`formatSearchResults > should handle files with low relevance scores 1`] = `
76
+ "<knowledge_base_search_results query="specific technical query" totalCount="1">
77
+ <instruction>Here are the search results from the knowledge base. Use the readKnowledge tool with file IDs to get complete content.</instruction>
78
+ <file id="low-relevance" name="Tangentially Related.md" relevanceScore="0.32">
79
+ <chunk fileId="low-relevance" fileName="Tangentially Related.md" similarity="0.35">This document contains some loosely related information that might be helpful.</chunk>
80
+ </file>
81
+ </knowledge_base_search_results>"
82
+ `;