@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.
- package/.console-log-whitelist.json +14 -0
- package/.github/workflows/check-console-log.yml +117 -0
- package/.github/workflows/desktop-pr-build.yml +4 -4
- package/.github/workflows/release-desktop-beta.yml +4 -4
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/test.yml +5 -5
- package/CHANGELOG.md +58 -0
- package/apps/desktop/src/main/services/__tests__/fileSrv.test.ts +603 -0
- package/changelog/v1.json +21 -0
- package/codecov.yml +1 -0
- package/docs/development/database-schema.dbml +1 -0
- package/e2e/package.json +1 -1
- package/locales/ar/file.json +9 -11
- package/locales/ar/plugin.json +34 -22
- package/locales/ar/tool.json +8 -0
- package/locales/bg-BG/file.json +8 -10
- package/locales/bg-BG/plugin.json +34 -22
- package/locales/bg-BG/tool.json +8 -0
- package/locales/de-DE/file.json +9 -11
- package/locales/de-DE/plugin.json +34 -22
- package/locales/de-DE/tool.json +8 -0
- package/locales/en-US/file.json +12 -14
- package/locales/en-US/plugin.json +34 -22
- package/locales/en-US/tool.json +8 -0
- package/locales/es-ES/file.json +7 -9
- package/locales/es-ES/plugin.json +34 -22
- package/locales/es-ES/tool.json +8 -0
- package/locales/fa-IR/file.json +9 -11
- package/locales/fa-IR/plugin.json +34 -22
- package/locales/fa-IR/tool.json +8 -0
- package/locales/fr-FR/file.json +6 -8
- package/locales/fr-FR/plugin.json +34 -22
- package/locales/fr-FR/tool.json +8 -0
- package/locales/it-IT/file.json +8 -10
- package/locales/it-IT/plugin.json +34 -22
- package/locales/it-IT/tool.json +8 -0
- package/locales/ja-JP/file.json +10 -12
- package/locales/ja-JP/plugin.json +34 -22
- package/locales/ja-JP/tool.json +8 -0
- package/locales/ko-KR/file.json +8 -10
- package/locales/ko-KR/plugin.json +34 -22
- package/locales/ko-KR/tool.json +8 -0
- package/locales/nl-NL/file.json +8 -10
- package/locales/nl-NL/plugin.json +34 -22
- package/locales/nl-NL/tool.json +8 -0
- package/locales/pl-PL/file.json +7 -9
- package/locales/pl-PL/plugin.json +34 -22
- package/locales/pl-PL/tool.json +8 -0
- package/locales/pt-BR/file.json +7 -9
- package/locales/pt-BR/plugin.json +34 -22
- package/locales/pt-BR/tool.json +8 -0
- package/locales/ru-RU/file.json +9 -11
- package/locales/ru-RU/plugin.json +34 -22
- package/locales/ru-RU/tool.json +8 -0
- package/locales/tr-TR/file.json +8 -10
- package/locales/tr-TR/plugin.json +34 -22
- package/locales/tr-TR/tool.json +8 -0
- package/locales/vi-VN/file.json +9 -11
- package/locales/vi-VN/plugin.json +34 -22
- package/locales/vi-VN/tool.json +8 -0
- package/locales/zh-CN/file.json +10 -12
- package/locales/zh-CN/plugin.json +34 -22
- package/locales/zh-CN/tool.json +8 -0
- package/locales/zh-TW/file.json +10 -12
- package/locales/zh-TW/plugin.json +34 -22
- package/locales/zh-TW/tool.json +8 -0
- package/package.json +3 -2
- package/packages/database/migrations/0047_add_slug_document.sql +6 -0
- package/packages/database/migrations/meta/0047_snapshot.json +7891 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/core/migrations.json +16 -7
- package/packages/database/src/models/__tests__/document.test.ts +149 -0
- package/packages/database/src/models/chunk.ts +3 -1
- package/packages/database/src/models/document.ts +10 -4
- package/packages/database/src/schemas/file.ts +7 -1
- package/packages/model-bank/src/aiModels/qwen.ts +5 -3
- package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +21 -21
- package/packages/obervability-otel/package.json +2 -2
- package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatFileContents.test.ts.snap +75 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatNoSearchResults.test.ts.snap +45 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatSearchResults.test.ts.snap +82 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatFileContents.test.ts +118 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatFileContents.ts +31 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatNoSearchResults.test.ts +25 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatNoSearchResults.ts +13 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatSearchResults.test.ts +191 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatSearchResults.ts +50 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/index.ts +6 -0
- package/packages/types/src/rag.ts +13 -4
- package/scripts/checkConsoleLog.mts +148 -0
- package/scripts/prebuild.mts +5 -5
- package/src/app/[variants]/(main)/changelog/index.tsx +1 -1
- package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +20 -16
- package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +6 -3
- package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +3 -2
- package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +14 -4
- package/src/app/[variants]/desktopRouter.config.tsx +23 -0
- package/src/app/[variants]/mobileRouter.config.tsx +23 -0
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +2 -2
- package/src/features/ChatInput/ActionBar/Token/TokenTagForGroupChat.tsx +2 -2
- package/src/features/ChatList/Messages/Group/Tool/Inspector/ToolTitle.tsx +5 -23
- package/src/features/ChatList/Messages/Tool/Inspector/ToolTitle.tsx +5 -25
- package/src/features/KnowledgeManager/DocumentExplorer/NoteEditorModal.tsx +0 -20
- package/src/features/KnowledgeManager/DocumentExplorer/index.tsx +3 -3
- package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/index.tsx +0 -20
- package/src/features/KnowledgeManager/Header/AddButton.tsx +0 -1
- package/src/features/KnowledgeManager/Header/NewNoteButton.tsx +1 -1
- package/src/features/KnowledgeManager/Header/TogglePanelButton.tsx +2 -2
- package/src/features/KnowledgeManager/Home/UploadEntries.tsx +2 -2
- package/src/features/KnowledgeManager/Home/index.tsx +4 -4
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -1
- package/src/features/User/UserPanel/useMenu.tsx +7 -3
- package/src/helpers/toolEngineering/index.test.ts +3 -3
- package/src/helpers/toolEngineering/index.ts +17 -4
- package/src/libs/trpc/client/lambda.ts +0 -6
- package/src/locales/default/file.ts +11 -13
- package/src/locales/default/plugin.ts +34 -22
- package/src/locales/default/tool.ts +13 -5
- package/src/server/routers/lambda/chunk.ts +168 -41
- package/src/services/chat/chat.test.ts +3 -3
- package/src/services/chat/index.ts +2 -2
- package/src/services/rag.ts +6 -2
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +1 -4
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +11 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +0 -87
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +2 -69
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +0 -2
- package/src/store/chat/slices/aiChat/actions/rag.ts +0 -47
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +8 -69
- package/src/store/chat/slices/builtinTool/actions/index.ts +4 -1
- package/src/store/chat/slices/builtinTool/actions/knowledgeBase.ts +174 -0
- package/src/store/chat/slices/operation/types.ts +1 -0
- package/src/store/chat/slices/thread/action.test.ts +0 -1
- package/src/store/chat/slices/thread/action.ts +0 -1
- package/src/tools/executionRuntimes.ts +3 -0
- package/src/tools/identifiers.ts +13 -0
- package/src/tools/index.ts +7 -0
- package/src/tools/knowledge-base/ExecutionRuntime/index.ts +96 -0
- package/src/tools/knowledge-base/Render/ReadKnowledge/FileCard.tsx +135 -0
- package/src/tools/knowledge-base/Render/ReadKnowledge/index.tsx +27 -0
- package/src/tools/knowledge-base/Render/SearchKnowledgeBase/Item/index.tsx +54 -0
- package/src/tools/knowledge-base/Render/SearchKnowledgeBase/Item/style.ts +51 -0
- package/src/tools/knowledge-base/Render/SearchKnowledgeBase/index.tsx +23 -0
- package/src/tools/knowledge-base/Render/index.ts +7 -0
- package/src/tools/knowledge-base/index.ts +64 -0
- package/src/tools/knowledge-base/systemRole.ts +102 -0
- package/src/tools/knowledge-base/type.ts +25 -0
- package/src/tools/local-system/Intervention/WriteFile/index.tsx +1 -1
- package/src/tools/renders.ts +4 -0
- 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
|
-
"\
|
|
784
|
-
"\
|
|
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": "
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
|
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:
|
|
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:
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
388
|
+
callbacks: streamOptions.callbacks,
|
|
389
|
+
inputStartAt,
|
|
390
|
+
})
|
|
391
391
|
: OpenAIStream(prod, {
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
419
|
-
|
|
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
|
-
|
|
843
|
-
|
|
844
|
-
|
|
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.
|
|
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.
|
|
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",
|
package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatFileContents.test.ts.snap
ADDED
|
@@ -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
|
+
`;
|
package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatSearchResults.test.ts.snap
ADDED
|
@@ -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
|
+
`;
|