@lobehub/lobehub 2.0.0-next.110 → 2.0.0-next.111

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 (36) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/changelog/v1.json +9 -0
  3. package/docs/development/database-schema.dbml +2 -1
  4. package/package.json +1 -1
  5. package/packages/context-engine/src/index.ts +1 -1
  6. package/packages/context-engine/src/providers/KnowledgeInjector.ts +78 -0
  7. package/packages/context-engine/src/providers/index.ts +2 -0
  8. package/packages/database/migrations/0047_add_slug_document.sql +1 -5
  9. package/packages/database/migrations/meta/0047_snapshot.json +30 -14
  10. package/packages/database/migrations/meta/_journal.json +1 -1
  11. package/packages/database/src/core/migrations.json +3 -3
  12. package/packages/database/src/models/__tests__/agent.test.ts +172 -3
  13. package/packages/database/src/models/__tests__/userMemories.test.ts +1382 -0
  14. package/packages/database/src/models/agent.ts +17 -0
  15. package/packages/database/src/models/userMemory.ts +993 -0
  16. package/packages/database/src/schemas/userMemories.ts +22 -5
  17. package/packages/prompts/src/prompts/files/__snapshots__/knowledgeBase.test.ts.snap +103 -0
  18. package/packages/prompts/src/prompts/files/index.ts +3 -0
  19. package/packages/prompts/src/prompts/files/knowledgeBase.test.ts +167 -0
  20. package/packages/prompts/src/prompts/files/knowledgeBase.ts +85 -0
  21. package/packages/types/src/files/index.ts +1 -0
  22. package/packages/types/src/index.ts +1 -0
  23. package/packages/types/src/knowledgeBase/index.ts +1 -0
  24. package/packages/types/src/userMemory/index.ts +3 -0
  25. package/packages/types/src/userMemory/layers.ts +54 -0
  26. package/packages/types/src/userMemory/shared.ts +64 -0
  27. package/packages/types/src/userMemory/tools.ts +240 -0
  28. package/src/features/ChatList/Messages/index.tsx +16 -19
  29. package/src/features/ChatList/components/ContextMenu.tsx +23 -16
  30. package/src/helpers/toolEngineering/index.ts +5 -9
  31. package/src/hooks/useQueryParam.ts +24 -22
  32. package/src/server/routers/async/file.ts +2 -7
  33. package/src/services/chat/contextEngineering.ts +19 -0
  34. package/src/store/agent/slices/chat/selectors/agent.ts +4 -0
  35. package/src/store/chat/slices/builtinTool/actions/knowledgeBase.ts +5 -15
  36. package/src/tools/knowledge-base/ExecutionRuntime/index.ts +3 -3
@@ -0,0 +1,240 @@
1
+ import { z } from 'zod';
2
+
3
+ import { UserMemoryContext, UserMemoryExperience, UserMemoryPreference } from './layers';
4
+ import {
5
+ IdentityTypeEnum,
6
+ LayersEnum,
7
+ MergeStrategyEnum,
8
+ RelationshipEnum,
9
+ TypesEnum,
10
+ } from './shared';
11
+
12
+ export const addMemoryBaseSchema = z.object({
13
+ details: z.string().describe('Optional detailed information'),
14
+ memoryCategory: z
15
+ .string()
16
+ .describe(
17
+ 'Category of memory, describes the domain or context the memory belongs to, e.g. talking travel plan counts as `travel` category, all work related memories could be `work` category, except side projects, where we belongs to `project` category',
18
+ ),
19
+ // TODO: migrate to .enum() if we could move to
20
+ // zod v4
21
+ // Read more: https://zod.dev/api#enum
22
+ memoryLayer: z
23
+ .nativeEnum(LayersEnum)
24
+ .describe(
25
+ 'Memory layer, must be one of `context`, `identity`, `preference`, `experience`, `activity`',
26
+ ),
27
+ memoryType: z
28
+ .nativeEnum(TypesEnum)
29
+ .describe(
30
+ 'Type of memory, used to describe the specific nature or classification of the memory related to',
31
+ ),
32
+ summary: z.string().describe('Concise overview of this specific memory'),
33
+ title: z.string().describe('Brief descriptive title'),
34
+ });
35
+
36
+ export type AddMemoryBaseParams = z.infer<typeof addMemoryBaseSchema>;
37
+
38
+ export const addExperienceMemorySchema = addMemoryBaseSchema.extend({
39
+ withExperience: z.object({
40
+ action: z
41
+ .string()
42
+ .nullable()
43
+ .describe('Narrative describing actions taken or behaviors exhibited'),
44
+ keyLearning: z
45
+ .string()
46
+ .nullable()
47
+ .describe('Narrative describing key insights or lessons learned'),
48
+ possibleOutcome: z
49
+ .string()
50
+ .nullable()
51
+ .describe('Narrative describing potential outcomes or learnings'),
52
+ reasoning: z
53
+ .string()
54
+ .nullable()
55
+ .describe('Narrative describing the thought process or motivations'),
56
+ scoreConfidence: z
57
+ .number()
58
+ .nullable()
59
+ .describe(
60
+ 'Numeric score (0-1 or domain-specific) describing confidence in the experience details',
61
+ ),
62
+ situation: z.string().nullable().describe('Narrative describing the situation or event'),
63
+ tags: z.array(z.string()).describe('Model generated tags that summarize the experience facets'),
64
+ }),
65
+ });
66
+
67
+ export type AddExperienceMemoryParams = z.infer<typeof addExperienceMemorySchema>;
68
+
69
+ export const addContextMemorySchema = addMemoryBaseSchema.extend({
70
+ withContext: z.object({
71
+ associatedObjects: z
72
+ .array(z.object({}))
73
+ .describe('describing involved roles, entities, or resources'),
74
+ associatedSubjects: z
75
+ .array(z.object({}))
76
+ .describe('describing involved roles, entities, or resources'),
77
+ currentStatus: z
78
+ .string()
79
+ .nullable()
80
+ .describe("High level status markers (e.g., 'active', 'pending')"),
81
+ description: z
82
+ .string()
83
+ .describe('Rich narrative describing the situation, timeline, or environment'),
84
+ scoreImpact: z
85
+ .number()
86
+ .nullable()
87
+ .describe('Numeric score (0-1 or domain-specific) describing importance'),
88
+ scoreUrgency: z
89
+ .number()
90
+ .nullable()
91
+ .describe('Numeric score (0-1 or domain-specific) describing urgency'),
92
+ tags: z.array(z.string()).describe('Model generated tags that summarize the context themes'),
93
+ title: z.string().nullable().describe('Optional synthesized context headline'),
94
+ type: z
95
+ .string()
96
+ .nullable()
97
+ .describe(
98
+ "High level context archetype (e.g., 'temporarily', 'private', 'questioning', 'formal', 'assistant', 'mentorship')",
99
+ ),
100
+ }),
101
+ });
102
+
103
+ export type AddContextMemoryParams = z.infer<typeof addContextMemorySchema>;
104
+
105
+ export const addPreferenceMemorySchema = addMemoryBaseSchema.extend({
106
+ withPreference: z.object({
107
+ appContext: z
108
+ .object({
109
+ app: z.string().nullable().describe('App or product name this applies to'),
110
+ feature: z.string().nullable(),
111
+ route: z.string().nullable(),
112
+ surface: z.string().nullable().describe('e.g., chat, emails, code review, notes'),
113
+ })
114
+ .strict()
115
+ .describe('Application/surface specific preference, if any'),
116
+ conclusionDirectives: z
117
+ .string()
118
+ .describe(
119
+ "Direct, self-contained instruction to the assistant from the user's perspective (what to do, not how to implement)",
120
+ ),
121
+ extractedScopes: z
122
+ .array(z.string())
123
+ .describe('describing preference facets and applicable scopes'),
124
+ originContext: z
125
+ .object({
126
+ actor: z.string().nullable().describe("Who stated the preference; use 'User' for the user"),
127
+ applicableWhen: z.string().nullable().describe('Conditions where this preference applies'),
128
+ notApplicableWhen: z.string().nullable().describe('Conditions where it does not apply'),
129
+ scenario: z.string().nullable().describe('Applicable scenario or use case'),
130
+ trigger: z.string().nullable().describe('What prompted this preference'),
131
+ })
132
+ .strict()
133
+ .describe('Context of how/why this preference was expressed'),
134
+ scorePriority: z
135
+ .number()
136
+ .nullable()
137
+ .describe('Numeric prioritization weight where higher means more critical to respect'),
138
+ suggestions: z
139
+ .array(z.string())
140
+ .describe('Follow-up actions or assistant guidance derived from the preference'),
141
+ tags: z.array(z.string()).describe('Model generated tags that summarize the preference facets'),
142
+ type: z
143
+ .string()
144
+ .nullable()
145
+ .describe("High level preference classification (e.g., 'lifestyle', 'communication')"),
146
+ }),
147
+ });
148
+
149
+ export type AddPreferenceMemoryParams = z.infer<typeof addPreferenceMemorySchema>;
150
+
151
+ export const addIdentityMemorySchema = addMemoryBaseSchema.extend({
152
+ withIdentity: z.object({
153
+ description: z
154
+ .string()
155
+ .describe(
156
+ "Rich narrative describing the person's background, roles, and attributes (required)",
157
+ ),
158
+ episodicDate: z
159
+ .string()
160
+ .nullable()
161
+ .describe(
162
+ 'Optional ISO 8601 timestamp describing when this identity aspect is most relevant',
163
+ ),
164
+ relationship: z
165
+ .nativeEnum(RelationshipEnum)
166
+ .describe(
167
+ "Relationship of this identity to the user. Use 'self' when the identity is the user themselves",
168
+ ),
169
+ role: z
170
+ .string()
171
+ .nullable()
172
+ .describe("Short role label (e.g., 'software engineer', 'student', 'manager', 'mentor')"),
173
+ scoreConfidence: z
174
+ .number()
175
+ .nullable()
176
+ .describe('Model confidence (0-1) that this identity aspect is correct'),
177
+ sourceEvidence: z
178
+ .string()
179
+ .nullable()
180
+ .describe('Optional short quote or pointer to evidence in the current conversation'),
181
+ tags: z.array(z.string()).describe('Model generated tags that summarize the identity facets'),
182
+ type: z.nativeEnum(IdentityTypeEnum).describe('High level identity archetype'),
183
+ }),
184
+ });
185
+
186
+ export type AddIdentityMemoryParams = z.infer<typeof addIdentityMemorySchema>;
187
+
188
+ export const updateIdentityMemorySchema = z.object({
189
+ id: z.string().describe('The ID of the identity entry to update'),
190
+ mergeStrategy: z
191
+ .nativeEnum(MergeStrategyEnum)
192
+ .describe('replace: overwrite all fields; merge: update only provided fields'),
193
+ set: z
194
+ .object({
195
+ description: z.string().optional(),
196
+ episodicDate: z.string().nullable().optional(),
197
+ relationship: z.nativeEnum(RelationshipEnum).optional(),
198
+ role: z.string().nullable().optional(),
199
+ scoreConfidence: z.number().nullable().optional(),
200
+ sourceEvidence: z.string().nullable().optional(),
201
+ tags: z.array(z.string()).optional(),
202
+ type: z.string().nullable().optional(),
203
+ })
204
+ .strict()
205
+ .describe('Fields to update'),
206
+ });
207
+
208
+ export type UpdateIdentityMemoryParams = z.infer<typeof updateIdentityMemorySchema>;
209
+
210
+ export const removeIdentityMemorySchema = z.object({
211
+ id: z.string().describe('The ID of the identity entry to remove'),
212
+ reason: z.string().describe('Brief reasoning: incorrect, obsolete, or duplicate'),
213
+ });
214
+
215
+ export type RemoveIdentityMemoryParams = z.infer<typeof removeIdentityMemorySchema>;
216
+
217
+ export const searchMemorySchema = z.object({
218
+ // TODO: we need to dynamically fetch the available categories/types from the backend
219
+ // memoryCategory: z.string().optional(),
220
+ // memoryType: z.string().optional(),
221
+ query: z.string(),
222
+ topK: z.object({
223
+ contexts: z.coerce.number().int().min(0),
224
+ experiences: z.coerce.number().int().min(0),
225
+ preferences: z.coerce.number().int().min(0),
226
+ }),
227
+ });
228
+
229
+ export type SearchMemoryParams = z.infer<typeof searchMemorySchema>;
230
+
231
+ export interface SearchMemoryResult {
232
+ contexts: Array<Omit<UserMemoryContext, 'userId' | 'descriptionVector'>>;
233
+ experiences: Array<
234
+ Omit<UserMemoryExperience, 'userId' | 'actionVector' | 'situationVector' | 'keyLearningVector'>
235
+ >;
236
+ preferences: Array<Omit<UserMemoryPreference, 'userId' | 'conclusionDirectivesVector'>>;
237
+ }
238
+
239
+ export type RetrieveMemoryParams = SearchMemoryParams;
240
+ export type RetrieveMemoryResult = SearchMemoryResult;
@@ -7,6 +7,14 @@ import { useSearchParams } from 'next/navigation';
7
7
  import { MouseEvent, ReactNode, memo, use, useCallback, useEffect, useRef } from 'react';
8
8
  import { Flexbox } from 'react-layout-kit';
9
9
 
10
+ import {
11
+ VirtuaContext,
12
+ removeVirtuaVisibleItem,
13
+ upsertVirtuaVisibleItem,
14
+ } from '@/features/ChatList/components/VirtualizedList/VirtuosoContext';
15
+ import { getChatStoreState, useChatStore } from '@/store/chat';
16
+ import { displayMessageSelectors, messageStateSelectors } from '@/store/chat/selectors';
17
+
10
18
  import ContextMenu from '../components/ContextMenu';
11
19
  import History from '../components/History';
12
20
  import { InPortalThreadContext } from '../context/InPortalThreadContext';
@@ -17,14 +25,6 @@ import SupervisorMessage from './Supervisor';
17
25
  import ToolMessage from './Tool';
18
26
  import UserMessage from './User';
19
27
 
20
- import {
21
- VirtuaContext,
22
- removeVirtuaVisibleItem,
23
- upsertVirtuaVisibleItem,
24
- } from '@/features/ChatList/components/VirtualizedList/VirtuosoContext';
25
- import { getChatStoreState, useChatStore } from '@/store/chat';
26
- import { displayMessageSelectors, messageStateSelectors } from '@/store/chat/selectors';
27
-
28
28
  const useStyles = createStyles(({ css, prefixCls }) => ({
29
29
  loading: css`
30
30
  opacity: 0.6;
@@ -66,17 +66,14 @@ const Item = memo<ChatListItemProps>(
66
66
  const searchParams = useSearchParams();
67
67
  const topic = searchParams.get('topic');
68
68
 
69
- const [role, editing, isMessageCreating] = useChatStore(
70
- (s) => {
71
- const item = displayMessageSelectors.getDisplayMessageById(id)(s);
72
- return [
73
- item?.role,
74
- messageStateSelectors.isMessageEditing(id)(s),
75
- messageStateSelectors.isMessageCreating(id)(s),
76
- ];
77
- },
78
- isEqual,
79
- );
69
+ const [role, editing, isMessageCreating] = useChatStore((s) => {
70
+ const item = displayMessageSelectors.getDisplayMessageById(id)(s);
71
+ return [
72
+ item?.role,
73
+ messageStateSelectors.isMessageEditing(id)(s),
74
+ messageStateSelectors.isMessageCreating(id)(s),
75
+ ];
76
+ }, isEqual);
80
77
 
81
78
  const {
82
79
  containerRef: contextMenuContainerRef,
@@ -7,13 +7,20 @@ import { Dropdown, type MenuProps } from 'antd';
7
7
  import { App } from 'antd';
8
8
  import { createStyles } from 'antd-style';
9
9
  import isEqual from 'fast-deep-equal';
10
- import { isValidElement, memo, useCallback, useMemo, useState, type RefObject , ComponentType, ReactNode } from 'react';
10
+ import {
11
+ ComponentType,
12
+ ReactNode,
13
+ type RefObject,
14
+ isValidElement,
15
+ memo,
16
+ useCallback,
17
+ useMemo,
18
+ useState,
19
+ } from 'react';
11
20
  import { createPortal } from 'react-dom';
12
21
  import { useTranslation } from 'react-i18next';
13
22
  import type { VListHandle } from 'virtua';
14
23
 
15
- import ShareMessageModal from './ShareMessageModal';
16
- import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
17
24
  import { getChatStoreState, useChatStore } from '@/store/chat';
18
25
  import {
19
26
  displayMessageSelectors,
@@ -23,6 +30,9 @@ import {
23
30
  import { useSessionStore } from '@/store/session';
24
31
  import { sessionSelectors } from '@/store/session/selectors';
25
32
 
33
+ import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
34
+ import ShareMessageModal from './ShareMessageModal';
35
+
26
36
  interface ActionMenuItem extends ActionIconGroupItemType {
27
37
  children?: { key: string; label: ReactNode }[];
28
38
  disable?: boolean;
@@ -76,19 +86,16 @@ const ContextMenu = memo<ContextMenuProps>(
76
86
  const [shareMessage, setShareMessage] = useState<UIChatMessage | null>(null);
77
87
  const [isShareModalOpen, setShareModalOpen] = useState(false);
78
88
 
79
- const [role, error, isCollapsed, hasThread, isRegenerating] = useChatStore(
80
- (s) => {
81
- const item = displayMessageSelectors.getDisplayMessageById(id)(s);
82
- return [
83
- item?.role,
84
- item?.error,
85
- messageStateSelectors.isMessageCollapsed(id)(s),
86
- threadSelectors.hasThreadBySourceMsgId(id)(s),
87
- messageStateSelectors.isMessageRegenerating(id)(s),
88
- ];
89
- },
90
- isEqual,
91
- );
89
+ const [role, error, isCollapsed, hasThread, isRegenerating] = useChatStore((s) => {
90
+ const item = displayMessageSelectors.getDisplayMessageById(id)(s);
91
+ return [
92
+ item?.role,
93
+ item?.error,
94
+ messageStateSelectors.isMessageCollapsed(id)(s),
95
+ threadSelectors.hasThreadBySourceMsgId(id)(s),
96
+ messageStateSelectors.isMessageRegenerating(id)(s),
97
+ ];
98
+ }, isEqual);
92
99
 
93
100
  const isThreadMode = useChatStore((s) => !!s.activeThreadId);
94
101
  const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
@@ -6,16 +6,16 @@ import type { PluginEnableChecker } from '@lobechat/context-engine';
6
6
  import { ChatCompletionTool, WorkingModel } from '@lobechat/types';
7
7
  import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
8
8
 
9
+ import { getAgentStoreState } from '@/store/agent';
10
+ import { agentSelectors } from '@/store/agent/selectors';
9
11
  import { getToolStoreState } from '@/store/tool';
10
12
  import { pluginSelectors } from '@/store/tool/selectors';
13
+ import { KnowledgeBaseManifest } from '@/tools/knowledge-base';
11
14
  import { WebBrowsingManifest } from '@/tools/web-browsing';
12
15
 
13
16
  import { getSearchConfig } from '../getSearchConfig';
14
17
  import { isCanUseFC } from '../isCanUseFC';
15
18
  import { shouldEnableTool } from '../toolFilters';
16
- import { KnowledgeBaseManifest } from '@/tools/knowledge-base';
17
- import { getAgentStoreState } from '@/store/agent';
18
- import { agentSelectors } from '@/store/agent/slices/chat';
19
19
 
20
20
  /**
21
21
  * Tools engine configuration options
@@ -59,11 +59,7 @@ export const createToolsEngine = (config: ToolsEngineConfig = {}): ToolsEngine =
59
59
  export const createAgentToolsEngine = (workingModel: WorkingModel) =>
60
60
  createToolsEngine({
61
61
  // Add default tools based on configuration
62
- defaultToolIds: [
63
- WebBrowsingManifest.identifier,
64
- // Only add KnowledgeBase tool if knowledge is enabled
65
- KnowledgeBaseManifest.identifier,
66
- ],
62
+ defaultToolIds: [WebBrowsingManifest.identifier, KnowledgeBaseManifest.identifier],
67
63
  // Create search-aware enableChecker for this request
68
64
  enableChecker: ({ pluginId }) => {
69
65
  // Check platform-specific constraints (e.g., LocalSystem desktop-only)
@@ -80,7 +76,7 @@ export const createAgentToolsEngine = (workingModel: WorkingModel) =>
80
76
  if (pluginId === KnowledgeBaseManifest.identifier) {
81
77
  const agentState = getAgentStoreState();
82
78
 
83
- return agentSelectors.hasEnabledKnowledge(agentState);
79
+ return agentSelectors.hasEnabledKnowledgeBases(agentState);
84
80
  }
85
81
 
86
82
  // For all other plugins, enable by default
@@ -85,28 +85,30 @@ export function useQueryParam<T>(
85
85
 
86
86
  const updateParams = () => {
87
87
  // 使用函数式更新,确保基于最新的 searchParams
88
- setSearchParams((prevParams) => {
89
- const newSearchParams = new URLSearchParams(prevParams);
90
- console.log('updateParams', newSearchParams.toString());
91
- const serialized = currentParser.serialize(actualValue);
92
-
93
- // 处理 clearOnDefault 选项
94
- if (
95
- currentClearOnDefault &&
96
- currentDefaultValue !== undefined &&
97
- serialized === currentParser.serialize(currentDefaultValue as T)
98
- ) {
99
- newSearchParams.delete(key);
100
- } else if (serialized === null || serialized === undefined) {
101
- newSearchParams.delete(key);
102
- } else {
103
- newSearchParams.set(key, serialized);
104
- }
105
-
106
- console.log('updateParams', newSearchParams.toString());
107
-
108
- return newSearchParams;
109
- }, { replace: currentHistory === 'replace' });
88
+ setSearchParams(
89
+ (prevParams) => {
90
+ const newSearchParams = new URLSearchParams(prevParams);
91
+ const serialized = currentParser.serialize(actualValue);
92
+
93
+ // 处理 clearOnDefault 选项
94
+ if (
95
+ currentClearOnDefault &&
96
+ currentDefaultValue !== undefined &&
97
+ serialized === currentParser.serialize(currentDefaultValue as T)
98
+ ) {
99
+ newSearchParams.delete(key);
100
+ } else if (serialized === null || serialized === undefined) {
101
+ newSearchParams.delete(key);
102
+ } else {
103
+ newSearchParams.set(key, serialized);
104
+ }
105
+
106
+ console.log('updateParams', newSearchParams.toString());
107
+
108
+ return newSearchParams;
109
+ },
110
+ { replace: currentHistory === 'replace' },
111
+ );
110
112
  };
111
113
 
112
114
  // 处理节流
@@ -90,13 +90,8 @@ export const fileRouter = router({
90
90
  try {
91
91
  await pMap(
92
92
  requestArray,
93
- async (chunks, index) => {
94
- const agentRuntime = await initModelRuntimeWithUserPayload(
95
- provider,
96
- ctx.jwtPayload,
97
- );
98
-
99
- console.log(`run embedding task ${index + 1}`);
93
+ async (chunks) => {
94
+ const agentRuntime = initModelRuntimeWithUserPayload(provider, ctx.jwtPayload);
100
95
 
101
96
  const embeddings = await agentRuntime.embeddings({
102
97
  dimensions: 1024,
@@ -5,6 +5,7 @@ import {
5
5
  HistorySummaryProvider,
6
6
  HistoryTruncateProcessor,
7
7
  InputTemplateProcessor,
8
+ KnowledgeInjector,
8
9
  MessageCleanupProcessor,
9
10
  MessageContentProcessor,
10
11
  PlaceholderVariablesProcessor,
@@ -19,6 +20,8 @@ import { OpenAIChatMessage, UIChatMessage } from '@lobechat/types';
19
20
  import { VARIABLE_GENERATORS } from '@lobechat/utils/client';
20
21
 
21
22
  import { isCanUseFC } from '@/helpers/isCanUseFC';
23
+ import { getAgentStoreState } from '@/store/agent';
24
+ import { agentSelectors } from '@/store/agent/selectors';
22
25
  import { getToolStoreState } from '@/store/tool';
23
26
  import { toolSelectors } from '@/store/tool/selectors';
24
27
 
@@ -50,6 +53,19 @@ export const contextEngineering = async ({
50
53
  }: ContextEngineeringContext): Promise<OpenAIChatMessage[]> => {
51
54
  const toolNameResolver = new ToolNameResolver();
52
55
 
56
+ // Get enabled agent files with content and knowledge bases from agent store
57
+ const agentStoreState = getAgentStoreState();
58
+ const agentFiles = agentSelectors.currentAgentFiles(agentStoreState);
59
+ const agentKnowledgeBases = agentSelectors.currentAgentKnowledgeBases(agentStoreState);
60
+
61
+ const fileContents = agentFiles
62
+ .filter((file) => file.enabled && file.content)
63
+ .map((file) => ({ content: file.content!, fileId: file.id, filename: file.name }));
64
+
65
+ const knowledgeBases = agentKnowledgeBases
66
+ .filter((kb) => kb.enabled)
67
+ .map((kb) => ({ description: kb.description, id: kb.id, name: kb.name }));
68
+
53
69
  const pipeline = new ContextEngine({
54
70
  pipeline: [
55
71
  // 1. History truncation (MUST be first, before any message injection)
@@ -60,6 +76,9 @@ export const contextEngineering = async ({
60
76
  // 2. System role injection (agent's system role)
61
77
  new SystemRoleInjector({ systemRole }),
62
78
 
79
+ // 3. Knowledge injection (full content for agent files + metadata for knowledge bases)
80
+ new KnowledgeInjector({ fileContents, knowledgeBases }),
81
+
63
82
  // 4. Tool system role injection
64
83
  new ToolSystemRoleProvider({
65
84
  getToolSystemRoles: (tools) => toolSelectors.enabledSystemRoles(tools)(getToolStoreState()),
@@ -152,6 +152,9 @@ const hasFiles = (s: AgentStoreState) => {
152
152
 
153
153
  const hasKnowledge = (s: AgentStoreState) => hasKnowledgeBases(s) || hasFiles(s);
154
154
  const hasEnabledKnowledge = (s: AgentStoreState) => currentEnabledKnowledge(s).length > 0;
155
+ const hasEnabledKnowledgeBases = (s: AgentStoreState) =>
156
+ currentAgentKnowledgeBases(s).some((s) => s.enabled);
157
+
155
158
  const currentKnowledgeIds = (s: AgentStoreState) => {
156
159
  return {
157
160
  fileIds: currentAgentFiles(s)
@@ -185,6 +188,7 @@ export const agentSelectors = {
185
188
  getAgentConfigByAgentId,
186
189
  getAgentConfigById,
187
190
  hasEnabledKnowledge,
191
+ hasEnabledKnowledgeBases,
188
192
  hasKnowledge,
189
193
  hasSystemRole,
190
194
  inboxAgentConfig,
@@ -6,7 +6,6 @@ import { getAgentStoreState } from '@/store/agent/store';
6
6
  import { ChatStore } from '@/store/chat/store';
7
7
  import { KnowledgeBaseExecutionRuntime } from '@/tools/knowledge-base/ExecutionRuntime';
8
8
 
9
- import { dbMessageSelectors } from '../../message/selectors';
10
9
 
11
10
  const log = debug('lobe-store:builtin-tool:knowledge-base');
12
11
 
@@ -54,26 +53,17 @@ export const knowledgeBaseSlice: StateCreator<
54
53
  },
55
54
 
56
55
  searchKnowledgeBase: async (id, params) => {
57
- // Get knowledge base IDs and file IDs from agent store
56
+ // Get knowledge base IDs from agent store
58
57
  const agentState = getAgentStoreState();
59
58
  const knowledgeIds = agentSelectors.currentKnowledgeIds(agentState);
60
59
 
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
- };
60
+ // Only search in knowledge bases, not agent files
61
+ // Agent files will be injected as full content in context-engine
62
+ const knowledgeBaseIds = knowledgeIds.knowledgeBaseIds;
72
63
 
73
64
  return get().internal_triggerKnowledgeBaseToolCalling(id, async () => {
74
65
  return await runtime.searchKnowledgeBase(params, {
75
- fileIds: options.fileIds,
76
- knowledgeBaseIds: options.knowledgeBaseIds,
66
+ knowledgeBaseIds,
77
67
  messageId: id,
78
68
  });
79
69
  });
@@ -16,7 +16,6 @@ export class KnowledgeBaseExecutionRuntime {
16
16
  async searchKnowledgeBase(
17
17
  args: SearchKnowledgeBaseArgs,
18
18
  options?: {
19
- fileIds?: string[];
20
19
  knowledgeBaseIds?: string[];
21
20
  messageId?: string;
22
21
  signal?: AbortSignal;
@@ -25,9 +24,10 @@ export class KnowledgeBaseExecutionRuntime {
25
24
  try {
26
25
  const { query, topK = 20 } = args;
27
26
 
28
- // Call the existing RAG service
27
+ // Only search in knowledge bases, not agent files
28
+ // Agent files will be injected as full content in context-engine
29
29
  const { chunks, fileResults } = await ragService.semanticSearchForChat(
30
- { fileIds: options?.fileIds, knowledgeIds: options?.knowledgeBaseIds, query, topK },
30
+ { knowledgeIds: options?.knowledgeBaseIds, query, topK },
31
31
  options?.signal,
32
32
  );
33
33