@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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { promptFileContents } from './formatFileContents';
|
|
4
|
+
import type { FileContent } from './formatFileContents';
|
|
5
|
+
|
|
6
|
+
describe('promptFileContents', () => {
|
|
7
|
+
it('should format single file content', () => {
|
|
8
|
+
const fileContents: FileContent[] = [
|
|
9
|
+
{
|
|
10
|
+
content: 'This is the file content with some important information.',
|
|
11
|
+
fileId: 'file-001',
|
|
12
|
+
filename: 'document.md',
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const result = promptFileContents(fileContents);
|
|
17
|
+
expect(result).toMatchSnapshot();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should format multiple file contents', () => {
|
|
21
|
+
const fileContents: FileContent[] = [
|
|
22
|
+
{
|
|
23
|
+
content: 'First file content about API authentication.',
|
|
24
|
+
fileId: 'file-001',
|
|
25
|
+
filename: 'auth-guide.md',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
content: 'Second file content about database setup and configuration.',
|
|
29
|
+
fileId: 'file-002',
|
|
30
|
+
filename: 'db-setup.md',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
content: 'Third file content with deployment instructions.',
|
|
34
|
+
fileId: 'file-003',
|
|
35
|
+
filename: 'deployment.md',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const result = promptFileContents(fileContents);
|
|
40
|
+
expect(result).toMatchSnapshot();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should handle file with error', () => {
|
|
44
|
+
const fileContents: FileContent[] = [
|
|
45
|
+
{
|
|
46
|
+
content: '',
|
|
47
|
+
error: 'File not found',
|
|
48
|
+
fileId: 'file-404',
|
|
49
|
+
filename: 'missing.md',
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const result = promptFileContents(fileContents);
|
|
54
|
+
expect(result).toMatchSnapshot();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should handle mixed successful and error files', () => {
|
|
58
|
+
const fileContents: FileContent[] = [
|
|
59
|
+
{
|
|
60
|
+
content: 'Successfully loaded content from this file.',
|
|
61
|
+
fileId: 'file-001',
|
|
62
|
+
filename: 'success.md',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
content: '',
|
|
66
|
+
error: 'Permission denied',
|
|
67
|
+
fileId: 'file-002',
|
|
68
|
+
filename: 'restricted.md',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
content: 'Another successfully loaded file with more content.',
|
|
72
|
+
fileId: 'file-003',
|
|
73
|
+
filename: 'another-success.md',
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const result = promptFileContents(fileContents);
|
|
78
|
+
expect(result).toMatchSnapshot();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should handle file with special characters in name', () => {
|
|
82
|
+
const fileContents: FileContent[] = [
|
|
83
|
+
{
|
|
84
|
+
content: 'Content from a file with special characters in the name.',
|
|
85
|
+
fileId: 'file-special',
|
|
86
|
+
filename: 'FAQ: Q&A (2024).md',
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
const result = promptFileContents(fileContents);
|
|
91
|
+
expect(result).toMatchSnapshot();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should handle file with long content', () => {
|
|
95
|
+
const fileContents: FileContent[] = [
|
|
96
|
+
{
|
|
97
|
+
content: `# Comprehensive Guide
|
|
98
|
+
|
|
99
|
+
## Introduction
|
|
100
|
+
This is a very long document with multiple sections and detailed information.
|
|
101
|
+
|
|
102
|
+
## Section 1
|
|
103
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
|
104
|
+
|
|
105
|
+
## Section 2
|
|
106
|
+
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
|
107
|
+
|
|
108
|
+
## Conclusion
|
|
109
|
+
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.`,
|
|
110
|
+
fileId: 'file-long',
|
|
111
|
+
filename: 'comprehensive-guide.md',
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
const result = promptFileContents(fileContents);
|
|
116
|
+
expect(result).toMatchSnapshot();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface FileContent {
|
|
2
|
+
content: string;
|
|
3
|
+
error?: string;
|
|
4
|
+
fileId: string;
|
|
5
|
+
filename: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Formats a single file content with XML tags
|
|
10
|
+
*/
|
|
11
|
+
const formatFileContent = (file: FileContent): string => {
|
|
12
|
+
if (file.error) {
|
|
13
|
+
return `<file id="${file.fileId}" name="${file.filename}" error="${file.error}" />`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return `<file id="${file.fileId}" name="${file.filename}">
|
|
17
|
+
${file.content}
|
|
18
|
+
</file>`;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Format file contents prompt for AI consumption using XML structure
|
|
23
|
+
*/
|
|
24
|
+
export const promptFileContents = (fileContents: FileContent[]): string => {
|
|
25
|
+
const filesXml = fileContents.map((file) => formatFileContent(file)).join('\n');
|
|
26
|
+
|
|
27
|
+
return `<knowledge_base_files totalCount="${fileContents.length}">
|
|
28
|
+
<instruction>Use the information from these files to answer the user's question. Always cite the source files.</instruction>
|
|
29
|
+
${filesXml}
|
|
30
|
+
</knowledge_base_files>`;
|
|
31
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { promptNoSearchResults } from './formatNoSearchResults';
|
|
4
|
+
|
|
5
|
+
describe('promptNoSearchResults', () => {
|
|
6
|
+
it('should format no search results with simple query', () => {
|
|
7
|
+
const result = promptNoSearchResults('how to install');
|
|
8
|
+
expect(result).toMatchSnapshot();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should format no search results with complex query', () => {
|
|
12
|
+
const result = promptNoSearchResults('API authentication with OAuth 2.0');
|
|
13
|
+
expect(result).toMatchSnapshot();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should format no search results with special characters', () => {
|
|
17
|
+
const result = promptNoSearchResults('How to use fetchData<T> with async/await?');
|
|
18
|
+
expect(result).toMatchSnapshot();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should format no search results with non-English query', () => {
|
|
22
|
+
const result = promptNoSearchResults('å¦ä½é
ē½®ę°ę®åŗčæę„');
|
|
23
|
+
expect(result).toMatchSnapshot();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format prompt when no search results found using XML structure
|
|
3
|
+
*/
|
|
4
|
+
export const promptNoSearchResults = (query: string): string => {
|
|
5
|
+
return `<knowledge_base_search_results query="${query}" totalCount="0">
|
|
6
|
+
<instruction>No relevant files found in the knowledge base for this query.</instruction>
|
|
7
|
+
<suggestions>
|
|
8
|
+
<suggestion>Try rephrasing your question with different keywords</suggestion>
|
|
9
|
+
<suggestion>Check if the information exists in the uploaded documents</suggestion>
|
|
10
|
+
<suggestion>Ask the user to provide more context or upload relevant documents</suggestion>
|
|
11
|
+
</suggestions>
|
|
12
|
+
</knowledge_base_search_results>`;
|
|
13
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { formatSearchResults } from './formatSearchResults';
|
|
4
|
+
import type { FileSearchResult } from './formatSearchResults';
|
|
5
|
+
|
|
6
|
+
describe('formatSearchResults', () => {
|
|
7
|
+
it('should format single file with multiple chunks', () => {
|
|
8
|
+
const fileResults: FileSearchResult[] = [
|
|
9
|
+
{
|
|
10
|
+
fileId: 'file-123',
|
|
11
|
+
fileName: 'Getting Started Guide.md',
|
|
12
|
+
relevanceScore: 0.92,
|
|
13
|
+
topChunks: [
|
|
14
|
+
{
|
|
15
|
+
similarity: 0.95,
|
|
16
|
+
text: '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.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
similarity: 0.88,
|
|
20
|
+
text: 'After installation, you can run the development server with npm run dev. The application will be available at http://localhost:3000 by default.',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const result = formatSearchResults(fileResults, 'how to install the application');
|
|
27
|
+
expect(result).toMatchSnapshot();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should format multiple files with varying relevance scores', () => {
|
|
31
|
+
const fileResults: FileSearchResult[] = [
|
|
32
|
+
{
|
|
33
|
+
fileId: 'doc-001',
|
|
34
|
+
fileName: 'API Reference.md',
|
|
35
|
+
relevanceScore: 0.95,
|
|
36
|
+
topChunks: [
|
|
37
|
+
{
|
|
38
|
+
similarity: 0.98,
|
|
39
|
+
text: 'The Authentication API provides endpoints for user login, logout, and token refresh. All endpoints require HTTPS and proper API keys.',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
similarity: 0.92,
|
|
43
|
+
text: 'To authenticate a user, send a POST request to /api/auth/login with username and password in the request body.',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
fileId: 'doc-002',
|
|
49
|
+
fileName: 'Security Best Practices.md',
|
|
50
|
+
relevanceScore: 0.87,
|
|
51
|
+
topChunks: [
|
|
52
|
+
{
|
|
53
|
+
similarity: 0.89,
|
|
54
|
+
text: 'Always use environment variables to store sensitive information like API keys and database credentials. Never commit these to version control.',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
similarity: 0.85,
|
|
58
|
+
text: 'Implement rate limiting on all public endpoints to prevent abuse and DDoS attacks.',
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
fileId: 'doc-003',
|
|
64
|
+
fileName: 'Troubleshooting Guide.md',
|
|
65
|
+
relevanceScore: 0.73,
|
|
66
|
+
topChunks: [
|
|
67
|
+
{
|
|
68
|
+
similarity: 0.75,
|
|
69
|
+
text: 'If you encounter authentication errors, first verify that your API key is valid and has not expired.',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const result = formatSearchResults(fileResults, 'API authentication security');
|
|
76
|
+
expect(result).toMatchSnapshot();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should format long text chunks without truncation', () => {
|
|
80
|
+
const fileResults: FileSearchResult[] = [
|
|
81
|
+
{
|
|
82
|
+
fileId: 'long-doc',
|
|
83
|
+
fileName: 'Detailed Documentation.md',
|
|
84
|
+
relevanceScore: 0.91,
|
|
85
|
+
topChunks: [
|
|
86
|
+
{
|
|
87
|
+
similarity: 0.93,
|
|
88
|
+
text: '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.',
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const result = formatSearchResults(fileResults, 'documentation details');
|
|
95
|
+
expect(result).toMatchSnapshot();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should handle empty file results', () => {
|
|
99
|
+
const fileResults: FileSearchResult[] = [];
|
|
100
|
+
const result = formatSearchResults(fileResults, 'no results query');
|
|
101
|
+
expect(result).toMatchSnapshot();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should format file with special characters in name', () => {
|
|
105
|
+
const fileResults: FileSearchResult[] = [
|
|
106
|
+
{
|
|
107
|
+
fileId: 'special-123',
|
|
108
|
+
fileName: 'FAQ: Common Questions & Answers (2024).md',
|
|
109
|
+
relevanceScore: 0.88,
|
|
110
|
+
topChunks: [
|
|
111
|
+
{
|
|
112
|
+
similarity: 0.9,
|
|
113
|
+
text: 'Q: How do I reset my password? A: Click on the "Forgot Password" link on the login page.',
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
const result = formatSearchResults(fileResults, 'password reset');
|
|
120
|
+
expect(result).toMatchSnapshot();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should handle files with low relevance scores', () => {
|
|
124
|
+
const fileResults: FileSearchResult[] = [
|
|
125
|
+
{
|
|
126
|
+
fileId: 'low-relevance',
|
|
127
|
+
fileName: 'Tangentially Related.md',
|
|
128
|
+
relevanceScore: 0.32,
|
|
129
|
+
topChunks: [
|
|
130
|
+
{
|
|
131
|
+
similarity: 0.35,
|
|
132
|
+
text: 'This document contains some loosely related information that might be helpful.',
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
const result = formatSearchResults(fileResults, 'specific technical query');
|
|
139
|
+
expect(result).toMatchSnapshot();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should format query with special characters', () => {
|
|
143
|
+
const fileResults: FileSearchResult[] = [
|
|
144
|
+
{
|
|
145
|
+
fileId: 'code-sample',
|
|
146
|
+
fileName: 'Code Examples.md',
|
|
147
|
+
relevanceScore: 0.94,
|
|
148
|
+
topChunks: [
|
|
149
|
+
{
|
|
150
|
+
similarity: 0.96,
|
|
151
|
+
text: 'Use the following TypeScript code: const result = await fetchData<T>({ url, params });',
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
const result = formatSearchResults(fileResults, 'How to use fetchData<T> with async/await?');
|
|
158
|
+
expect(result).toMatchSnapshot();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should handle file with multiple high-similarity chunks', () => {
|
|
162
|
+
const fileResults: FileSearchResult[] = [
|
|
163
|
+
{
|
|
164
|
+
fileId: 'comprehensive-guide',
|
|
165
|
+
fileName: 'Complete Tutorial.md',
|
|
166
|
+
relevanceScore: 0.96,
|
|
167
|
+
topChunks: [
|
|
168
|
+
{
|
|
169
|
+
similarity: 0.98,
|
|
170
|
+
text: 'Step 1: Initialize the project with the configuration file.',
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
similarity: 0.97,
|
|
174
|
+
text: 'Step 2: Configure the environment variables for your deployment.',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
similarity: 0.96,
|
|
178
|
+
text: 'Step 3: Run the migration scripts to set up the database schema.',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
similarity: 0.95,
|
|
182
|
+
text: 'Step 4: Start the development server and verify everything is working.',
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
const result = formatSearchResults(fileResults, 'project setup steps');
|
|
189
|
+
expect(result).toMatchSnapshot();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface FileSearchResultChunk {
|
|
2
|
+
similarity: number;
|
|
3
|
+
text: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface FileSearchResult {
|
|
7
|
+
fileId: string;
|
|
8
|
+
fileName: string;
|
|
9
|
+
relevanceScore: number;
|
|
10
|
+
topChunks: FileSearchResultChunk[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Formats a single chunk with XML tags
|
|
15
|
+
*/
|
|
16
|
+
const formatChunk = (chunk: FileSearchResultChunk, fileId: string, fileName: string): string => {
|
|
17
|
+
return `<chunk fileId="${fileId}" fileName="${fileName}" similarity="${chunk.similarity}">${chunk.text}</chunk>`;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Formats a single file search result with XML tags
|
|
22
|
+
*/
|
|
23
|
+
const formatFile = (file: FileSearchResult): string => {
|
|
24
|
+
const chunks = file.topChunks.map((chunk) => formatChunk(chunk, file.fileId, file.fileName));
|
|
25
|
+
|
|
26
|
+
return `<file id="${file.fileId}" name="${file.fileName}" relevanceScore="${file.relevanceScore}">
|
|
27
|
+
${chunks.join('\n')}
|
|
28
|
+
</file>`;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Formats knowledge base search results into an XML structure
|
|
33
|
+
* @param fileResults - Array of file search results with relevance scores and chunks
|
|
34
|
+
* @param query - The original search query
|
|
35
|
+
* @returns Formatted XML string with search results
|
|
36
|
+
*/
|
|
37
|
+
export const formatSearchResults = (fileResults: FileSearchResult[], query: string): string => {
|
|
38
|
+
if (fileResults.length === 0) {
|
|
39
|
+
return `<knowledge_base_search_results query="${query}" totalCount="0">
|
|
40
|
+
<instruction>No relevant files found in the knowledge base for this query.</instruction>
|
|
41
|
+
</knowledge_base_search_results>`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const filesXml = fileResults.map((file) => formatFile(file)).join('\n');
|
|
45
|
+
|
|
46
|
+
return `<knowledge_base_search_results query="${query}" totalCount="${fileResults.length}">
|
|
47
|
+
<instruction>Here are the search results from the knowledge base. Use the readKnowledge tool with file IDs to get complete content.</instruction>
|
|
48
|
+
${filesXml}
|
|
49
|
+
</knowledge_base_search_results>`;
|
|
50
|
+
};
|
|
@@ -4,6 +4,12 @@ import { chunkPrompts } from './chunk';
|
|
|
4
4
|
import { knowledgePrompts } from './knowledge';
|
|
5
5
|
import { userQueryPrompt } from './userQuery';
|
|
6
6
|
|
|
7
|
+
export type { FileContent } from './formatFileContents';
|
|
8
|
+
export { promptFileContents } from './formatFileContents';
|
|
9
|
+
export { promptNoSearchResults } from './formatNoSearchResults';
|
|
10
|
+
export type { FileSearchResult, FileSearchResultChunk } from './formatSearchResults';
|
|
11
|
+
export { formatSearchResults } from './formatSearchResults';
|
|
12
|
+
|
|
7
13
|
export const knowledgeBaseQAPrompts = ({
|
|
8
14
|
chunks,
|
|
9
15
|
knowledge,
|
|
@@ -5,12 +5,21 @@ import { ChatSemanticSearchChunk } from './chunk';
|
|
|
5
5
|
export const SemanticSearchSchema = z.object({
|
|
6
6
|
fileIds: z.array(z.string()).optional(),
|
|
7
7
|
knowledgeIds: z.array(z.string()).optional(),
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
rewriteQuery: z.string(),
|
|
11
|
-
userQuery: z.string(),
|
|
8
|
+
query: z.string(),
|
|
9
|
+
topK: z.number().optional(),
|
|
12
10
|
});
|
|
13
11
|
|
|
14
12
|
export type SemanticSearchSchemaType = z.infer<typeof SemanticSearchSchema>;
|
|
15
13
|
|
|
16
14
|
export type MessageSemanticSearchChunk = Pick<ChatSemanticSearchChunk, 'id' | 'similarity'>;
|
|
15
|
+
|
|
16
|
+
export interface FileSearchResult {
|
|
17
|
+
fileId: string;
|
|
18
|
+
fileName: string;
|
|
19
|
+
relevanceScore: number;
|
|
20
|
+
topChunks: Array<{
|
|
21
|
+
id: string;
|
|
22
|
+
similarity: number;
|
|
23
|
+
text: string;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
interface WhitelistConfig {
|
|
7
|
+
files?: string[];
|
|
8
|
+
patterns?: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const WHITELIST_PATH = '.console-log-whitelist.json';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Load whitelist configuration
|
|
15
|
+
*/
|
|
16
|
+
const loadWhitelist = (): WhitelistConfig => {
|
|
17
|
+
try {
|
|
18
|
+
const content = readFileSync(WHITELIST_PATH, 'utf8');
|
|
19
|
+
return JSON.parse(content);
|
|
20
|
+
} catch {
|
|
21
|
+
return { files: [], patterns: [] };
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if a file is whitelisted
|
|
27
|
+
*/
|
|
28
|
+
const isWhitelisted = (filePath: string, whitelist: WhitelistConfig): boolean => {
|
|
29
|
+
const normalizedPath = filePath.replaceAll('\\', '/');
|
|
30
|
+
|
|
31
|
+
// Check exact file matches
|
|
32
|
+
if (whitelist.files?.some((f) => normalizedPath.includes(f.replaceAll('\\', '/')))) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check pattern matches (simple glob-like patterns)
|
|
37
|
+
if (whitelist.patterns) {
|
|
38
|
+
for (const pattern of whitelist.patterns) {
|
|
39
|
+
// Escape dots and replace glob patterns
|
|
40
|
+
// Use a placeholder for ** to avoid conflicts with single *
|
|
41
|
+
let regexPattern = pattern
|
|
42
|
+
.replaceAll('.', '\\.')
|
|
43
|
+
.replaceAll('**', '\u0000DOUBLESTAR\u0000')
|
|
44
|
+
.replaceAll('*', '[^/]*')
|
|
45
|
+
.replaceAll('\u0000DOUBLESTAR\u0000', '.*');
|
|
46
|
+
|
|
47
|
+
// If pattern ends with /**, match everything under that directory
|
|
48
|
+
// If pattern ends with **, just match everything from that point
|
|
49
|
+
const regex = new RegExp(`^${regexPattern}`);
|
|
50
|
+
if (regex.test(normalizedPath)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Main check function
|
|
61
|
+
*/
|
|
62
|
+
const checkConsoleLogs = () => {
|
|
63
|
+
const whitelist = loadWhitelist();
|
|
64
|
+
|
|
65
|
+
console.log('š Checking for console.log statements...\n');
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
// Search for console.log in TypeScript and JavaScript files
|
|
69
|
+
const output = execSync(
|
|
70
|
+
`git grep -n "console\\.log" -- "*.ts" "*.tsx" "*.js" "*.jsx" "*.mts" "*.cts" || true`,
|
|
71
|
+
{ encoding: 'utf8' },
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!output.trim()) {
|
|
75
|
+
console.log('ā
No console.log statements found!');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const lines = output.trim().split('\n');
|
|
80
|
+
const violations: Array<{ content: string, file: string; line: string; }> = [];
|
|
81
|
+
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
// Parse git grep output: filename:lineNumber:content
|
|
84
|
+
const match = line.match(/^([^:]+):(\d+):(.+)$/);
|
|
85
|
+
if (!match) continue;
|
|
86
|
+
|
|
87
|
+
const [, filePath, lineNumber, content] = match;
|
|
88
|
+
|
|
89
|
+
// Skip if whitelisted
|
|
90
|
+
if (isWhitelisted(filePath, whitelist)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Skip comments
|
|
95
|
+
const trimmedContent = content.trim();
|
|
96
|
+
if (trimmedContent.startsWith('//') || trimmedContent.startsWith('*')) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
violations.push({
|
|
101
|
+
content: content.trim(),
|
|
102
|
+
file: filePath,
|
|
103
|
+
line: lineNumber,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (violations.length === 0) {
|
|
108
|
+
console.log('ā
No console.log violations found (all matches are whitelisted or in comments)!');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Report violations as warnings
|
|
113
|
+
console.log('ā ļø Found console.log statements in the following files:\n');
|
|
114
|
+
|
|
115
|
+
// Use GitHub Actions annotation format for better visibility in CI
|
|
116
|
+
const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
|
|
117
|
+
|
|
118
|
+
for (const violation of violations) {
|
|
119
|
+
if (isCI) {
|
|
120
|
+
// GitHub Actions warning annotation format
|
|
121
|
+
console.log(`::warning file=${violation.file},line=${violation.line}::console.log found: ${violation.content}`);
|
|
122
|
+
} else {
|
|
123
|
+
console.log(` ${violation.file}:${violation.line}`);
|
|
124
|
+
console.log(` ${violation.content}\n`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log(`\nš” Total violations: ${violations.length}`);
|
|
129
|
+
console.log(
|
|
130
|
+
`\nš To whitelist files, add them to ${WHITELIST_PATH} in the following format:`,
|
|
131
|
+
);
|
|
132
|
+
console.log(`{
|
|
133
|
+
"files": ["path/to/file.ts"],
|
|
134
|
+
"patterns": ["scripts/**/*.mts", "**/*.test.ts"]
|
|
135
|
+
}\n`);
|
|
136
|
+
|
|
137
|
+
// Exit with 0 to not block CI, but warnings will still be visible
|
|
138
|
+
process.exit(0);
|
|
139
|
+
} catch (error: unknown) {
|
|
140
|
+
if (error instanceof Error && 'status' in error && error.status !== 0) {
|
|
141
|
+
console.error('ā Error running git grep:', error.message);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
checkConsoleLogs();
|
package/scripts/prebuild.mts
CHANGED
|
@@ -10,11 +10,11 @@ dotenv.config();
|
|
|
10
10
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
11
11
|
const partialBuildPages = [
|
|
12
12
|
// no need for desktop
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
},
|
|
13
|
+
// {
|
|
14
|
+
// name: 'changelog',
|
|
15
|
+
// disabled: isDesktop,
|
|
16
|
+
// paths: ['src/app/[variants]/(main)/changelog'],
|
|
17
|
+
// },
|
|
18
18
|
{
|
|
19
19
|
name: 'auth',
|
|
20
20
|
disabled: isDesktop,
|
|
@@ -18,7 +18,7 @@ const Page = (props: { isMobile: boolean }) => {
|
|
|
18
18
|
const { isMobile } = props;
|
|
19
19
|
const { hideDocs } = useServerConfigStore(featureFlagsSelectors);
|
|
20
20
|
|
|
21
|
-
const { data } = useSWR('changelog-index', async () => {
|
|
21
|
+
const { data = [] } = useSWR('changelog-index', async () => {
|
|
22
22
|
const changelogService = new ChangelogService();
|
|
23
23
|
return await changelogService.getChangelogIndex();
|
|
24
24
|
});
|