@lobehub/lobehub 2.0.0-next.110 → 2.0.0-next.112
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/CHANGELOG.md +59 -0
- package/changelog/v1.json +18 -0
- package/docs/development/database-schema.dbml +2 -1
- package/package.json +1 -1
- package/packages/context-engine/src/index.ts +1 -1
- package/packages/context-engine/src/providers/KnowledgeInjector.ts +78 -0
- package/packages/context-engine/src/providers/index.ts +2 -0
- package/packages/database/migrations/0047_add_slug_document.sql +1 -5
- package/packages/database/migrations/meta/0047_snapshot.json +30 -14
- package/packages/database/migrations/meta/_journal.json +1 -1
- package/packages/database/src/core/migrations.json +3 -3
- package/packages/database/src/models/__tests__/agent.test.ts +172 -3
- package/packages/database/src/models/__tests__/userMemories.test.ts +1382 -0
- package/packages/database/src/models/agent.ts +22 -1
- package/packages/database/src/models/userMemory.ts +993 -0
- package/packages/database/src/schemas/file.ts +5 -10
- package/packages/database/src/schemas/userMemories.ts +22 -5
- package/packages/model-bank/src/aiModels/qwen.ts +41 -3
- package/packages/model-runtime/src/providers/qwen/index.ts +9 -3
- package/packages/prompts/src/prompts/files/__snapshots__/knowledgeBase.test.ts.snap +103 -0
- package/packages/prompts/src/prompts/files/index.ts +3 -0
- package/packages/prompts/src/prompts/files/knowledgeBase.test.ts +167 -0
- package/packages/prompts/src/prompts/files/knowledgeBase.ts +85 -0
- package/packages/types/src/files/index.ts +1 -0
- package/packages/types/src/index.ts +1 -0
- package/packages/types/src/knowledgeBase/index.ts +1 -0
- package/packages/types/src/userMemory/index.ts +3 -0
- package/packages/types/src/userMemory/layers.ts +54 -0
- package/packages/types/src/userMemory/shared.ts +64 -0
- package/packages/types/src/userMemory/tools.ts +240 -0
- package/src/features/ChatList/Messages/index.tsx +16 -19
- package/src/features/ChatList/components/ContextMenu.tsx +23 -16
- package/src/helpers/toolEngineering/index.ts +5 -9
- package/src/hooks/useQueryParam.ts +24 -22
- package/src/server/routers/async/file.ts +2 -7
- package/src/server/routers/lambda/chunk.ts +6 -1
- package/src/services/chat/contextEngineering.ts +19 -0
- package/src/store/agent/slices/chat/selectors/agent.ts +4 -0
- package/src/store/chat/slices/builtinTool/actions/knowledgeBase.ts +5 -16
- package/src/tools/knowledge-base/ExecutionRuntime/index.ts +3 -3
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface UserMemoryTimestamps {
|
|
2
|
+
accessedAt: Date;
|
|
3
|
+
createdAt: Date;
|
|
4
|
+
updatedAt: Date;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface UserMemoryContext extends UserMemoryTimestamps {
|
|
8
|
+
associatedObjects: Record<string, unknown>[] | null;
|
|
9
|
+
associatedSubjects: Record<string, unknown>[] | null;
|
|
10
|
+
currentStatus: string | null;
|
|
11
|
+
description: string | null;
|
|
12
|
+
descriptionVector: number[] | null;
|
|
13
|
+
id: string;
|
|
14
|
+
metadata: Record<string, unknown> | null;
|
|
15
|
+
scoreImpact: number | null;
|
|
16
|
+
scoreUrgency: number | null;
|
|
17
|
+
tags: string[] | null;
|
|
18
|
+
title: string | null;
|
|
19
|
+
titleVector: number[] | null;
|
|
20
|
+
type: string | null;
|
|
21
|
+
userId: string | null;
|
|
22
|
+
userMemoryIds: string[] | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface UserMemoryExperience extends UserMemoryTimestamps {
|
|
26
|
+
action: string | null;
|
|
27
|
+
actionVector: number[] | null;
|
|
28
|
+
id: string;
|
|
29
|
+
keyLearning: string | null;
|
|
30
|
+
keyLearningVector: number[] | null;
|
|
31
|
+
metadata: Record<string, unknown> | null;
|
|
32
|
+
possibleOutcome: string | null;
|
|
33
|
+
reasoning: string | null;
|
|
34
|
+
scoreConfidence: number | null;
|
|
35
|
+
situation: string | null;
|
|
36
|
+
situationVector: number[] | null;
|
|
37
|
+
tags: string[] | null;
|
|
38
|
+
type: string | null;
|
|
39
|
+
userId: string | null;
|
|
40
|
+
userMemoryId: string | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface UserMemoryPreference extends UserMemoryTimestamps {
|
|
44
|
+
conclusionDirectives: string | null;
|
|
45
|
+
conclusionDirectivesVector: number[] | null;
|
|
46
|
+
id: string;
|
|
47
|
+
metadata: Record<string, unknown> | null;
|
|
48
|
+
scorePriority: number | null;
|
|
49
|
+
suggestions: string | null;
|
|
50
|
+
tags: string[] | null;
|
|
51
|
+
type: string | null;
|
|
52
|
+
userId: string | null;
|
|
53
|
+
userMemoryId: string | null;
|
|
54
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export enum RelationshipEnum {
|
|
2
|
+
Aunt = 'aunt',
|
|
3
|
+
Brother = 'brother',
|
|
4
|
+
Classmate = 'classmate',
|
|
5
|
+
Colleague = 'colleague',
|
|
6
|
+
Couple = 'couple',
|
|
7
|
+
Coworker = 'coworker',
|
|
8
|
+
Daughter = 'daughter',
|
|
9
|
+
Father = 'father',
|
|
10
|
+
Friend = 'friend',
|
|
11
|
+
Granddaughter = 'granddaughter',
|
|
12
|
+
Grandfather = 'grandfather',
|
|
13
|
+
Grandmother = 'grandmother',
|
|
14
|
+
Grandson = 'grandson',
|
|
15
|
+
Husband = 'husband',
|
|
16
|
+
Manager = 'manager',
|
|
17
|
+
Mentee = 'mentee',
|
|
18
|
+
Mentor = 'mentor',
|
|
19
|
+
Mother = 'mother',
|
|
20
|
+
Nephew = 'nephew',
|
|
21
|
+
Niece = 'niece',
|
|
22
|
+
Other = 'other',
|
|
23
|
+
Partner = 'partner',
|
|
24
|
+
Self = 'self',
|
|
25
|
+
Sibling = 'sibling',
|
|
26
|
+
Sister = 'sister',
|
|
27
|
+
Son = 'son',
|
|
28
|
+
Spouse = 'spouse',
|
|
29
|
+
Teammate = 'teammate',
|
|
30
|
+
Uncle = 'uncle',
|
|
31
|
+
Wife = 'wife',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export enum MergeStrategyEnum {
|
|
35
|
+
Merge = 'merge',
|
|
36
|
+
Replace = 'replace',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export enum IdentityTypeEnum {
|
|
40
|
+
Demographic = 'demographic',
|
|
41
|
+
Personal = 'personal',
|
|
42
|
+
Professional = 'professional',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export enum LayersEnum {
|
|
46
|
+
Activity = 'activity',
|
|
47
|
+
Context = 'context',
|
|
48
|
+
Experience = 'experience',
|
|
49
|
+
Identity = 'identity',
|
|
50
|
+
Preference = 'preference',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export enum TypesEnum {
|
|
54
|
+
Activity = 'activity',
|
|
55
|
+
Context = 'context',
|
|
56
|
+
Event = 'event',
|
|
57
|
+
Fact = 'fact',
|
|
58
|
+
Location = 'location',
|
|
59
|
+
Other = 'other',
|
|
60
|
+
People = 'people',
|
|
61
|
+
Preference = 'preference',
|
|
62
|
+
Technology = 'technology',
|
|
63
|
+
Topic = 'topic',
|
|
64
|
+
}
|
|
@@ -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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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 {
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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.
|
|
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(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
94
|
-
const agentRuntime =
|
|
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,
|
|
@@ -162,7 +162,12 @@ export const chunkRouter = router({
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
// 2. Find existing parsed document
|
|
165
|
-
let document
|
|
165
|
+
let document:
|
|
166
|
+
| {
|
|
167
|
+
content: string | null;
|
|
168
|
+
metadata: Record<string, any> | null;
|
|
169
|
+
}
|
|
170
|
+
| undefined = await ctx.documentModel.findByFileId(fileId);
|
|
166
171
|
|
|
167
172
|
// 3. If not exists, parse the file
|
|
168
173
|
if (!document) {
|
|
@@ -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,
|