@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
@@ -62,93 +62,6 @@ describe('chatRAG actions', () => {
62
62
  });
63
63
  });
64
64
 
65
- describe('internal_retrieveChunks', () => {
66
- it('should retrieve chunks with existing ragQuery', async () => {
67
- const { result } = renderHook(() => useChatStore());
68
- const messageId = 'message-id';
69
- const existingRagQuery = 'existing-query';
70
- const userQuery = 'user-query';
71
-
72
- // Mock the message with existing ragQuery
73
- vi.spyOn(dbMessageSelectors, 'getDbMessageById').mockReturnValue(
74
- () =>
75
- ({
76
- id: messageId,
77
- ragQuery: existingRagQuery,
78
- }) as UIChatMessage,
79
- );
80
-
81
- // Mock the semantic search response
82
- (ragService.semanticSearchForChat as Mock).mockResolvedValue({
83
- chunks: [{ id: 'chunk-1' }],
84
- queryId: 'query-id',
85
- });
86
-
87
- vi.spyOn(agentSelectors, 'currentKnowledgeIds').mockReturnValue({
88
- fileIds: [],
89
- knowledgeBaseIds: [],
90
- });
91
-
92
- const result1 = await act(async () => {
93
- return await result.current.internal_retrieveChunks(messageId, userQuery, []);
94
- });
95
-
96
- expect(result1).toEqual({
97
- chunks: [{ id: 'chunk-1' }],
98
- queryId: 'query-id',
99
- rewriteQuery: existingRagQuery,
100
- });
101
- expect(ragService.semanticSearchForChat).toHaveBeenCalledWith(
102
- expect.objectContaining({
103
- rewriteQuery: existingRagQuery,
104
- userQuery,
105
- }),
106
- );
107
- });
108
-
109
- it('should rewrite query if no existing ragQuery', async () => {
110
- const { result } = renderHook(() => useChatStore());
111
- const messageId = 'message-id';
112
- const userQuery = 'user-query';
113
- const rewrittenQuery = 'rewritten-query';
114
-
115
- // Mock the message without ragQuery
116
- vi.spyOn(dbMessageSelectors, 'getDbMessageById').mockReturnValue(
117
- () =>
118
- ({
119
- id: messageId,
120
- }) as UIChatMessage,
121
- );
122
-
123
- // Mock the rewrite query function
124
- vi.spyOn(result.current, 'internal_rewriteQuery').mockResolvedValueOnce(rewrittenQuery);
125
-
126
- // Mock the semantic search response
127
- (ragService.semanticSearchForChat as Mock).mockResolvedValue({
128
- chunks: [{ id: 'chunk-1' }],
129
- queryId: 'query-id',
130
- });
131
-
132
- vi.spyOn(agentSelectors, 'currentKnowledgeIds').mockReturnValue({
133
- fileIds: [],
134
- knowledgeBaseIds: [],
135
- });
136
-
137
- const result2 = await act(async () => {
138
- return await result.current.internal_retrieveChunks(messageId, userQuery, ['message']);
139
- });
140
-
141
- expect(result2).toEqual({
142
- chunks: [{ id: 'chunk-1' }],
143
- queryId: 'query-id',
144
- rewriteQuery: rewrittenQuery,
145
- });
146
- expect(result.current.internal_rewriteQuery).toHaveBeenCalledWith(messageId, userQuery, [
147
- 'message',
148
- ]);
149
- });
150
- });
151
-
152
65
  describe('internal_rewriteQuery', () => {
153
66
  it('should return original content if query rewrite is disabled', async () => {
154
67
  const { result } = renderHook(() => useChatStore());
@@ -712,75 +712,8 @@ describe('StreamingExecutor actions', () => {
712
712
  );
713
713
  });
714
714
 
715
- // TODO: This test is complex to set up properly with agent runtime and message creation
716
- // The functionality is verified in the implementation (streamingExecutor.ts:725-728)
717
- it.skip('should pass context to optimisticUpdateMessageRAG', async () => {
718
- act(() => {
719
- useChatStore.setState({
720
- internal_execAgentRuntime: realExecAgentRuntime,
721
- activeId: 'active-session',
722
- activeTopicId: 'active-topic',
723
- });
724
- });
725
-
726
- const { result } = renderHook(() => useChatStore());
727
-
728
- const contextSessionId = 'context-session';
729
- const contextTopicId = 'context-topic';
730
- const userMessage = {
731
- id: TEST_IDS.USER_MESSAGE_ID,
732
- role: 'user',
733
- content: TEST_CONTENT.USER_MESSAGE,
734
- sessionId: contextSessionId,
735
- topicId: contextTopicId,
736
- } as UIChatMessage;
737
-
738
- const ragMetadata = {
739
- ragQueryId: 'query-id',
740
- fileChunks: [{ id: 'chunk-1', similarity: 0.9 }],
741
- };
742
-
743
- const assistantMessageId = 'assistant-msg-id';
744
- const assistantMessage = {
745
- id: assistantMessageId,
746
- role: 'assistant',
747
- content: TEST_CONTENT.AI_RESPONSE,
748
- sessionId: contextSessionId,
749
- topicId: contextTopicId,
750
- } as UIChatMessage;
751
-
752
- // Mock createMessage to return the assistant message
753
- vi.spyOn(messageService, 'createMessage').mockResolvedValue({
754
- id: assistantMessageId,
755
- messages: [userMessage, assistantMessage],
756
- });
757
-
758
- const updateRAGSpy = vi.spyOn(result.current, 'optimisticUpdateMessageRAG');
759
- const streamSpy = vi
760
- .spyOn(chatService, 'createAssistantMessageStream')
761
- .mockImplementation(async ({ onFinish }) => {
762
- await onFinish?.(TEST_CONTENT.AI_RESPONSE, {});
763
- });
764
-
765
- await act(async () => {
766
- await result.current.internal_execAgentRuntime({
767
- messages: [userMessage],
768
- parentMessageId: userMessage.id,
769
- parentMessageType: 'user',
770
- sessionId: contextSessionId,
771
- topicId: contextTopicId,
772
- ragMetadata,
773
- });
774
- });
775
-
776
- // Verify optimisticUpdateMessageRAG was called with context
777
- expect(updateRAGSpy).toHaveBeenCalledWith(expect.any(String), ragMetadata, {
778
- sessionId: contextSessionId,
779
- topicId: contextTopicId,
780
- });
781
-
782
- streamSpy.mockRestore();
783
- });
715
+ // Note: RAG metadata functionality has been removed
716
+ // RAG is now handled by Knowledge Base Tools (searchKnowledgeBase and readKnowledge)
784
717
  });
785
718
 
786
719
  describe('StreamingExecutor OptimisticUpdateContext isolation', () => {
@@ -272,7 +272,6 @@ export const conversationLifecycle: StateCreator<
272
272
  sessionId: activeId,
273
273
  topicId: data.topicId ?? activeTopicId,
274
274
  parentOperationId: operationId, // Pass as parent operation
275
- ragQuery: get().internal_shouldUseRAG() ? message : undefined,
276
275
  threadId: activeThreadId,
277
276
  skipCreateFirstMessage: true,
278
277
  });
@@ -338,7 +337,6 @@ export const conversationLifecycle: StateCreator<
338
337
  sessionId: activeId,
339
338
  topicId: activeTopicId,
340
339
  traceId,
341
- ragQuery: get().internal_shouldUseRAG() ? item.content : undefined,
342
340
  threadId: activeThreadId,
343
341
  parentOperationId: operationId,
344
342
  });
@@ -10,18 +10,9 @@ import { dbMessageSelectors, displayMessageSelectors } from '@/store/chat/select
10
10
  import { toggleBooleanList } from '@/store/chat/utils';
11
11
  import { useUserStore } from '@/store/user';
12
12
  import { systemAgentSelectors } from '@/store/user/selectors';
13
- import { ChatSemanticSearchChunk } from '@/types/chunk';
14
13
 
15
14
  export interface ChatRAGAction {
16
15
  deleteUserMessageRagQuery: (id: string) => Promise<void>;
17
- /**
18
- * Retrieve chunks from semantic search
19
- */
20
- internal_retrieveChunks: (
21
- id: string,
22
- userQuery: string,
23
- messages: string[],
24
- ) => Promise<{ chunks: ChatSemanticSearchChunk[]; queryId?: string; rewriteQuery?: string }>;
25
16
  /**
26
17
  * Rewrite user content to better RAG query
27
18
  */
@@ -35,7 +26,6 @@ export interface ChatRAGAction {
35
26
  rewriteQuery: (id: string) => Promise<void>;
36
27
  }
37
28
 
38
- const knowledgeIds = () => agentSelectors.currentKnowledgeIds(useAgentStore.getState());
39
29
  const hasEnabledKnowledge = () => agentSelectors.hasEnabledKnowledge(useAgentStore.getState());
40
30
 
41
31
  export const chatRag: StateCreator<ChatStore, [['zustand/devtools', never]], [], ChatRAGAction> = (
@@ -58,43 +48,6 @@ export const chatRag: StateCreator<ChatStore, [['zustand/devtools', never]], [],
58
48
  await get().refreshMessages();
59
49
  },
60
50
 
61
- internal_retrieveChunks: async (id, userQuery, messages) => {
62
- get().internal_toggleMessageRAGLoading(true, id);
63
-
64
- const message = dbMessageSelectors.getDbMessageById(id)(get());
65
-
66
- // 1. get the rewrite query
67
- let rewriteQuery = message?.ragQuery as string | undefined;
68
-
69
- // if there is no ragQuery and there is a chat history
70
- // we need to rewrite the user message to get better results
71
- if (!message?.ragQuery && messages.length > 0) {
72
- rewriteQuery = await get().internal_rewriteQuery(id, userQuery, messages);
73
- }
74
-
75
- // 2. retrieve chunks from semantic search
76
- const files = dbMessageSelectors
77
- .dbUserFiles(get())
78
- .map((f) => f?.id)
79
- .filter(Boolean) as string[];
80
- try {
81
- const { chunks, queryId } = await ragService.semanticSearchForChat({
82
- fileIds: knowledgeIds().fileIds.concat(files),
83
- knowledgeIds: knowledgeIds().knowledgeBaseIds,
84
- messageId: id,
85
- rewriteQuery: rewriteQuery || userQuery,
86
- userQuery,
87
- });
88
-
89
- get().internal_toggleMessageRAGLoading(false, id);
90
-
91
- return { chunks, queryId, rewriteQuery };
92
- } catch {
93
- get().internal_toggleMessageRAGLoading(false, id);
94
-
95
- return { chunks: [] };
96
- }
97
- },
98
51
  internal_rewriteQuery: async (id, content, messages) => {
99
52
  let rewriteQuery = content;
100
53
 
@@ -2,7 +2,6 @@
2
2
  // Disable the auto sort key eslint rule to make the code more logic and readable
3
3
  import { AgentRuntime, type AgentRuntimeContext, type AgentState } from '@lobechat/agent-runtime';
4
4
  import { isDesktop } from '@lobechat/const';
5
- import { knowledgeBaseQAPrompts } from '@lobechat/prompts';
6
5
  import {
7
6
  ChatImageItem,
8
7
  ChatToolPayload,
@@ -11,19 +10,18 @@ import {
11
10
  TraceNameMap,
12
11
  UIChatMessage,
13
12
  } from '@lobechat/types';
14
- import type { MessageSemanticSearchChunk } from '@lobechat/types';
15
13
  import debug from 'debug';
16
14
  import { t } from 'i18next';
17
15
  import { throttle } from 'lodash-es';
18
16
  import { StateCreator } from 'zustand/vanilla';
19
17
 
18
+ import { createAgentToolsEngine } from '@/helpers/toolEngineering';
20
19
  import { chatService } from '@/services/chat';
21
20
  import { messageService } from '@/services/message';
22
21
  import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
23
22
  import { getAgentStoreState } from '@/store/agent/store';
24
23
  import { GeneralChatAgent } from '@/store/chat/agents/GeneralChatAgent';
25
24
  import { createAgentExecutors } from '@/store/chat/agents/createAgentExecutors';
26
- import { createAgentToolsEngine } from '@/store/chat/agents/createToolEngine';
27
25
  import { ChatStore } from '@/store/chat/store';
28
26
  import { getFileStoreState } from '@/store/file/store';
29
27
  import { toolInterventionSelectors } from '@/store/user/selectors';
@@ -104,15 +102,10 @@ export interface StreamingExecutorAction {
104
102
  */
105
103
  parentOperationId?: string;
106
104
  inSearchWorkflow?: boolean;
107
- /**
108
- * the RAG query content, should be embedding and used in the semantic search
109
- */
110
- ragQuery?: string;
111
105
  threadId?: string;
112
106
  inPortalThread?: boolean;
113
107
  skipCreateFirstMessage?: boolean;
114
108
  traceId?: string;
115
- ragMetadata?: { ragQueryId: string; fileChunks: MessageSemanticSearchChunk[] };
116
109
  /**
117
110
  * Initial agent state (for resuming execution from a specific point)
118
111
  */
@@ -624,59 +617,17 @@ export const streamingExecutor: StateCreator<
624
617
  const provider = agentConfigData.provider;
625
618
 
626
619
  // ===========================================
627
- // Step 1: RAG Preprocessing (if enabled)
620
+ // Step 1: Knowledge Base Tool Integration
628
621
  // ===========================================
629
- // Skip RAG preprocessing if initialState is provided (messages already preprocessed)
630
- if (params.ragQuery && parentMessageType === 'user') {
631
- const userMessageId = parentMessageId;
632
- log('[internal_execAgentRuntime] RAG preprocessing start');
633
-
634
- // Get relevant chunks from semantic search
635
- const {
636
- chunks,
637
- queryId: ragQueryId,
638
- rewriteQuery,
639
- } = await get().internal_retrieveChunks(
640
- userMessageId,
641
- params.ragQuery,
642
- // Skip the last message content when building context
643
- messages.map((m) => m.content).slice(0, messages.length - 1),
644
- );
645
-
646
- log('[internal_execAgentRuntime] RAG chunks retrieved: %d chunks', chunks.length);
647
-
648
- const lastMsg = messages.pop() as UIChatMessage;
649
-
650
- // Build RAG context and append to user query
651
- const knowledgeBaseQAContext = knowledgeBaseQAPrompts({
652
- chunks,
653
- userQuery: lastMsg.content,
654
- rewriteQuery,
655
- knowledge: agentSelectors.currentEnabledKnowledge(agentStoreState),
656
- });
657
-
658
- messages.push({
659
- ...lastMsg,
660
- content: (lastMsg.content + '\n\n' + knowledgeBaseQAContext).trim(),
661
- });
622
+ // RAG retrieval is now handled by the Knowledge Base Tool
623
+ // The AI will decide when to call searchKnowledgeBase and readKnowledge tools
624
+ // based on the conversation context and available knowledge bases
662
625
 
663
- // Update assistant message with RAG metadata
664
- const fileChunks: MessageSemanticSearchChunk[] = chunks.map((c) => ({
665
- id: c.id,
666
- similarity: c.similarity,
667
- }));
668
-
669
- if (fileChunks.length > 0) {
670
- // Note: RAG metadata will be updated after assistant message is created by call_llm executor
671
- // Store RAG data temporarily in params for later use
672
- params.ragMetadata = { ragQueryId: ragQueryId!, fileChunks };
673
- }
674
-
675
- log('[internal_execAgentRuntime] RAG preprocessing completed');
676
- }
626
+ // TODO: Implement selected files full-text injection if needed
627
+ // User-selected files should be handled differently from knowledge base files
677
628
 
678
629
  // ===========================================
679
- // Step 3: Create and Execute Agent Runtime
630
+ // Step 2: Create and Execute Agent Runtime
680
631
  // ===========================================
681
632
  log('[internal_execAgentRuntime] Creating agent runtime');
682
633
 
@@ -829,18 +780,6 @@ export const streamingExecutor: StateCreator<
829
780
  stepCount,
830
781
  );
831
782
 
832
- // Update RAG metadata if available
833
- if (params.ragMetadata) {
834
- const finalMessages = get().messagesMap[messageKey] || [];
835
- const assistantMessage = finalMessages.findLast((m) => m.role === 'assistant');
836
- if (assistantMessage) {
837
- await get().optimisticUpdateMessageRAG(assistantMessage.id, params.ragMetadata, {
838
- operationId,
839
- });
840
- log('[internal_execAgentRuntime] RAG metadata updated for assistant message');
841
- }
842
- }
843
-
844
783
  // Complete operation
845
784
  if (state.status === 'done') {
846
785
  get().completeOperation(operationId);
@@ -3,13 +3,15 @@ import { StateCreator } from 'zustand/vanilla';
3
3
  import { ChatStore } from '@/store/chat/store';
4
4
 
5
5
  import { ChatCodeInterpreterAction, codeInterpreterSlice } from './interpreter';
6
+ import { KnowledgeBaseAction, knowledgeBaseSlice } from './knowledgeBase';
6
7
  import { LocalFileAction, localSystemSlice } from './localSystem';
7
8
  import { SearchAction, searchSlice } from './search';
8
9
 
9
10
  export interface ChatBuiltinToolAction
10
11
  extends SearchAction,
11
12
  LocalFileAction,
12
- ChatCodeInterpreterAction {}
13
+ ChatCodeInterpreterAction,
14
+ KnowledgeBaseAction {}
13
15
 
14
16
  export const chatToolSlice: StateCreator<
15
17
  ChatStore,
@@ -20,4 +22,5 @@ export const chatToolSlice: StateCreator<
20
22
  ...searchSlice(...params),
21
23
  ...localSystemSlice(...params),
22
24
  ...codeInterpreterSlice(...params),
25
+ ...knowledgeBaseSlice(...params),
23
26
  });
@@ -0,0 +1,174 @@
1
+ import debug from 'debug';
2
+ import { StateCreator } from 'zustand/vanilla';
3
+
4
+ import { agentSelectors } from '@/store/agent/selectors';
5
+ import { getAgentStoreState } from '@/store/agent/store';
6
+ import { ChatStore } from '@/store/chat/store';
7
+ import { KnowledgeBaseExecutionRuntime } from '@/tools/knowledge-base/ExecutionRuntime';
8
+
9
+ import { dbMessageSelectors } from '../../message/selectors';
10
+
11
+ const log = debug('lobe-store:builtin-tool:knowledge-base');
12
+
13
+ export interface KnowledgeBaseAction {
14
+ internal_triggerKnowledgeBaseToolCalling: (
15
+ id: string,
16
+ callingService: () => Promise<{ content: string; error?: any; state?: any; success: boolean }>,
17
+ ) => Promise<boolean>;
18
+
19
+ /**
20
+ * Read full content of specific files from knowledge base
21
+ */
22
+ readKnowledge: (
23
+ id: string,
24
+ params: {
25
+ fileIds: string[];
26
+ },
27
+ ) => Promise<boolean>;
28
+
29
+ /**
30
+ * Search knowledge base for relevant files and chunks
31
+ */
32
+ searchKnowledgeBase: (
33
+ id: string,
34
+ params: {
35
+ query: string;
36
+ topK?: number;
37
+ },
38
+ ) => Promise<boolean>;
39
+ }
40
+
41
+ const runtime = new KnowledgeBaseExecutionRuntime();
42
+
43
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
44
+ export const knowledgeBaseSlice: StateCreator<
45
+ ChatStore,
46
+ [['zustand/devtools', never]],
47
+ [],
48
+ KnowledgeBaseAction
49
+ > = (set, get) => ({
50
+ readKnowledge: async (id, params) => {
51
+ return get().internal_triggerKnowledgeBaseToolCalling(id, async () => {
52
+ return await runtime.readKnowledge(params);
53
+ });
54
+ },
55
+
56
+ searchKnowledgeBase: async (id, params) => {
57
+ // Get knowledge base IDs and file IDs from agent store
58
+ const agentState = getAgentStoreState();
59
+ const knowledgeIds = agentSelectors.currentKnowledgeIds(agentState);
60
+
61
+ // Get user-selected files from messages
62
+ const userFiles = dbMessageSelectors
63
+ .dbUserFiles(get())
64
+ .map((f) => f?.id)
65
+ .filter(Boolean) as string[];
66
+
67
+ // Merge knowledge base files and user-selected files
68
+ const options = {
69
+ fileIds: [...knowledgeIds.fileIds, ...userFiles],
70
+ knowledgeBaseIds: knowledgeIds.knowledgeBaseIds,
71
+ };
72
+
73
+ return get().internal_triggerKnowledgeBaseToolCalling(id, async () => {
74
+ return await runtime.searchKnowledgeBase(params, {
75
+ fileIds: options.fileIds,
76
+ knowledgeBaseIds: options.knowledgeBaseIds,
77
+ messageId: id,
78
+ });
79
+ });
80
+ },
81
+
82
+ // ==================== utils ====================
83
+
84
+ internal_triggerKnowledgeBaseToolCalling: async (id, callingService) => {
85
+ // Get parent operationId from messageOperationMap (should be executeToolCall)
86
+ const parentOperationId = get().messageOperationMap[id];
87
+
88
+ // Create child operation for knowledge base execution
89
+ // Auto-associates message with this operation via messageId in context
90
+ const { operationId: knowledgeBaseOpId, abortController } = get().startOperation({
91
+ context: {
92
+ messageId: id,
93
+ },
94
+ metadata: {
95
+ startTime: Date.now(),
96
+ },
97
+ parentOperationId,
98
+ type: 'builtinToolKnowledgeBase',
99
+ });
100
+
101
+ log(
102
+ '[knowledgeBase] messageId=%s, parentOpId=%s, knowledgeBaseOpId=%s, aborted=%s',
103
+ id,
104
+ parentOperationId,
105
+ knowledgeBaseOpId,
106
+ abortController.signal.aborted,
107
+ );
108
+
109
+ const context = { operationId: knowledgeBaseOpId };
110
+
111
+ try {
112
+ const { state, content, success, error } = await callingService();
113
+
114
+ // Complete knowledge base operation
115
+ get().completeOperation(knowledgeBaseOpId);
116
+
117
+ if (success) {
118
+ if (state) {
119
+ await get().optimisticUpdatePluginState(id, state, context);
120
+ }
121
+ await get().optimisticUpdateMessageContent(id, content, undefined, context);
122
+ } else {
123
+ await get().optimisticUpdateMessagePluginError(
124
+ id,
125
+ {
126
+ body: error,
127
+ message: error?.message || 'Operation failed',
128
+ type: 'PluginServerError',
129
+ },
130
+ context,
131
+ );
132
+ // Still update content even if failed, to show error message
133
+ await get().optimisticUpdateMessageContent(id, content, undefined, context);
134
+ }
135
+
136
+ return true;
137
+ } catch (error) {
138
+ const err = error as Error;
139
+
140
+ log('[knowledgeBase] Error: messageId=%s, error=%s', id, err.message);
141
+
142
+ // Check if it's an abort error
143
+ if (err.message.includes('The user aborted a request.') || err.name === 'AbortError') {
144
+ log('[knowledgeBase] Request aborted: messageId=%s', id);
145
+ // Fail knowledge base operation for abort
146
+ get().failOperation(knowledgeBaseOpId, {
147
+ message: 'User cancelled the request',
148
+ type: 'UserAborted',
149
+ });
150
+ // Don't update error message for user aborts
151
+ return false;
152
+ }
153
+
154
+ // Fail knowledge base operation for other errors
155
+ get().failOperation(knowledgeBaseOpId, {
156
+ message: err.message,
157
+ type: 'PluginServerError',
158
+ });
159
+
160
+ // For other errors, update message
161
+ await get().optimisticUpdateMessagePluginError(
162
+ id,
163
+ {
164
+ body: error,
165
+ message: err.message,
166
+ type: 'PluginServerError',
167
+ },
168
+ context,
169
+ );
170
+
171
+ return false;
172
+ }
173
+ },
174
+ });
@@ -36,6 +36,7 @@ export type OperationType =
36
36
  | 'builtinToolSearch' // Builtin tool: search
37
37
  | 'builtinToolInterpreter' // Builtin tool: code interpreter
38
38
  | 'builtinToolLocalSystem' // Builtin tool: local system
39
+ | 'builtinToolKnowledgeBase' // Builtin tool: knowledge base
39
40
 
40
41
  // === Group Chat ===
41
42
  | 'supervisorDecision' // Supervisor decision
@@ -883,7 +883,6 @@ describe('thread action', () => {
883
883
  parentMessageId: 'new-msg-id',
884
884
  parentMessageType: 'user',
885
885
  inPortalThread: true,
886
- ragQuery: 'test with rag',
887
886
  threadId: 'existing-thread-id',
888
887
  }),
889
888
  );
@@ -178,7 +178,6 @@ export const chatThreadMessage: StateCreator<
178
178
  parentMessageType: 'user',
179
179
  sessionId: get().activeId,
180
180
  topicId: get().activeTopicId,
181
- ragQuery: get().internal_shouldUseRAG() ? message : undefined,
182
181
  threadId: get().portalThreadId,
183
182
  inPortalThread: true,
184
183
  });
@@ -1,9 +1,12 @@
1
+ import { KnowledgeBaseManifest } from './knowledge-base';
2
+ import { KnowledgeBaseExecutionRuntime } from './knowledge-base/ExecutionRuntime';
1
3
  import { LocalSystemManifest } from './local-system';
2
4
  import { LocalSystemExecutionRuntime } from './local-system/ExecutionRuntime';
3
5
  import { WebBrowsingManifest } from './web-browsing';
4
6
  import { WebBrowsingExecutionRuntime } from './web-browsing/ExecutionRuntime';
5
7
 
6
8
  export const BuiltinToolServerRuntimes: Record<string, any> = {
9
+ [KnowledgeBaseManifest.identifier]: KnowledgeBaseExecutionRuntime,
7
10
  [LocalSystemManifest.identifier]: LocalSystemExecutionRuntime,
8
11
  [WebBrowsingManifest.identifier]: WebBrowsingExecutionRuntime,
9
12
  };
@@ -0,0 +1,13 @@
1
+ import { ArtifactsManifest } from './artifacts';
2
+ import { CodeInterpreterManifest } from './code-interpreter';
3
+ import { KnowledgeBaseManifest } from './knowledge-base';
4
+ import { LocalSystemManifest } from './local-system';
5
+ import { WebBrowsingManifest } from './web-browsing';
6
+
7
+ export const builtinToolIdentifiers: string[] = [
8
+ ArtifactsManifest.identifier,
9
+ LocalSystemManifest.identifier,
10
+ WebBrowsingManifest.identifier,
11
+ KnowledgeBaseManifest.identifier,
12
+ CodeInterpreterManifest.identifier,
13
+ ];
@@ -4,6 +4,7 @@ import { isDesktop } from '@/const/version';
4
4
 
5
5
  import { ArtifactsManifest } from './artifacts';
6
6
  import { CodeInterpreterManifest } from './code-interpreter';
7
+ import { KnowledgeBaseManifest } from './knowledge-base';
7
8
  import { LocalSystemManifest } from './local-system';
8
9
  import { WebBrowsingManifest } from './web-browsing';
9
10
 
@@ -30,4 +31,10 @@ export const builtinTools: LobeBuiltinTool[] = [
30
31
  manifest: CodeInterpreterManifest,
31
32
  type: 'builtin',
32
33
  },
34
+ {
35
+ hidden: true,
36
+ identifier: KnowledgeBaseManifest.identifier,
37
+ manifest: KnowledgeBaseManifest,
38
+ type: 'builtin',
39
+ },
33
40
  ];