@lobehub/lobehub 2.0.0-next.266 → 2.0.0-next.268
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/.cursor/rules/microcopy-cn.mdc +75 -63
- package/.cursor/rules/microcopy-en.mdc +4 -8
- package/CHANGELOG.md +50 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/apps/desktop/src/main/locales/default/common.ts +2 -2
- package/changelog/v1.json +10 -0
- package/docs/development/database-schema.dbml +4 -0
- package/e2e/CLAUDE.md +43 -81
- package/e2e/cucumber.config.js +1 -0
- package/e2e/docs/local-setup.md +67 -219
- package/e2e/scripts/setup.ts +529 -0
- package/e2e/src/features/home/sidebarAgent.feature +62 -0
- package/e2e/src/features/home/sidebarGroup.feature +62 -0
- package/e2e/src/features/page/README.md +118 -0
- package/e2e/src/features/page/crud.feature +62 -0
- package/e2e/src/features/page/editor-content.feature +93 -0
- package/e2e/src/features/page/editor-meta.feature +60 -0
- package/e2e/src/steps/agent/conversation.steps.ts +4 -4
- package/e2e/src/steps/home/sidebarAgent.steps.ts +370 -0
- package/e2e/src/steps/home/sidebarGroup.steps.ts +168 -0
- package/e2e/src/steps/hooks.ts +4 -0
- package/e2e/src/steps/page/editor-content.steps.ts +344 -0
- package/e2e/src/steps/page/editor-meta.steps.ts +410 -0
- package/e2e/src/steps/page/page-crud.steps.ts +363 -0
- package/e2e/src/support/world.ts +12 -0
- package/locales/ar/file.json +2 -0
- package/locales/bg-BG/file.json +2 -0
- package/locales/de-DE/file.json +2 -0
- package/locales/en-US/auth.json +1 -1
- package/locales/en-US/file.json +2 -0
- package/locales/en-US/metadata.json +2 -2
- package/locales/es-ES/file.json +2 -0
- package/locales/fa-IR/file.json +2 -0
- package/locales/fr-FR/file.json +2 -0
- package/locales/it-IT/file.json +2 -0
- package/locales/ja-JP/file.json +2 -0
- package/locales/ko-KR/file.json +2 -0
- package/locales/nl-NL/file.json +2 -0
- package/locales/pl-PL/file.json +2 -0
- package/locales/pt-BR/file.json +2 -0
- package/locales/ru-RU/file.json +2 -0
- package/locales/tr-TR/file.json +2 -0
- package/locales/vi-VN/file.json +2 -0
- package/locales/zh-CN/file.json +2 -0
- package/locales/zh-TW/file.json +2 -0
- package/package.json +3 -3
- package/packages/builtin-agents/src/agents/agent-builder/index.ts +1 -1
- package/packages/builtin-agents/src/agents/group-agent-builder/index.ts +1 -1
- package/packages/builtin-agents/src/agents/page-agent/index.ts +1 -1
- package/packages/const/src/settings/group.ts +0 -10
- package/packages/database/migrations/0068_update_group_data.sql +4 -0
- package/packages/database/migrations/meta/0068_snapshot.json +9588 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/models/__tests__/chatGroup.test.ts +5 -7
- package/packages/database/src/models/__tests__/knowledgeBase.test.ts +185 -0
- package/packages/database/src/models/knowledgeBase.ts +67 -3
- package/packages/database/src/repositories/agentGroup/index.test.ts +23 -29
- package/packages/database/src/repositories/agentGroup/index.ts +4 -9
- package/packages/database/src/repositories/knowledge/index.ts +3 -3
- package/packages/database/src/schemas/chatGroup.ts +4 -3
- package/packages/database/src/types/chatGroup.ts +0 -7
- package/packages/types/src/agentGroup/index.ts +30 -9
- package/packages/utils/src/multimodalContent.test.ts +302 -0
- package/packages/utils/src/server/__tests__/sse.test.ts +353 -0
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentGroupItem/Editing.tsx +4 -11
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentGroupItem/index.tsx +3 -3
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/ModalProvider.tsx +9 -32
- package/src/app/[variants]/(main)/home/_layout/hooks/useCreateMenuItems.tsx +3 -37
- package/src/app/[variants]/(main)/home/_layout/hooks/useSessionGroupMenuItems.tsx +7 -53
- package/src/app/[variants]/(main)/home/features/RecentPage/List.tsx +2 -1
- package/src/app/[variants]/(main)/resource/features/DndContextWrapper.tsx +1 -1
- package/src/app/[variants]/(main)/resource/library/_layout/Sidebar.tsx +2 -2
- package/src/app/[variants]/(main)/resource/library/features/LibraryMenu.tsx +2 -2
- package/src/app/[variants]/(mobile)/chat/settings/features/SettingButton.tsx +2 -12
- package/src/components/ChatGroupWizard/ChatGroupWizard.tsx +5 -27
- package/src/components/DragUpload/index.tsx +24 -27
- package/src/components/MemberSelectionModal/MemberSelectionModal.tsx +2 -11
- package/src/features/ChatInput/ActionBar/Params/Controls.tsx +42 -7
- package/src/features/CommandMenu/useCommandMenu.ts +4 -14
- package/src/features/ResourceManager/components/Editor/index.tsx +2 -3
- package/src/features/ResourceManager/components/Explorer/Header/index.tsx +13 -17
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +1 -1
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/TruncatedFileName.tsx +130 -0
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +36 -4
- package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +4 -3
- package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +58 -2
- package/src/features/ResourceManager/components/Explorer/MasonryView/index.tsx +58 -6
- package/src/features/ResourceManager/components/Explorer/MoveToFolderModal.tsx +2 -5
- package/src/features/ResourceManager/components/Explorer/ToolBar/BatchActionsDropdown.tsx +9 -5
- package/src/features/ResourceManager/components/Explorer/index.tsx +11 -56
- package/src/features/ResourceManager/components/Header/AddButton.tsx +5 -6
- package/src/features/ResourceManager/components/LibraryHierarchy/HierarchyNode.tsx +382 -0
- package/src/features/ResourceManager/components/LibraryHierarchy/index.tsx +396 -0
- package/src/features/ResourceManager/components/LibraryHierarchy/styles.ts +19 -0
- package/src/features/ResourceManager/components/LibraryHierarchy/treeState.ts +178 -0
- package/src/features/ResourceManager/components/LibraryHierarchy/types.ts +10 -0
- package/src/features/ResourceManager/index.tsx +3 -0
- package/src/layout/GlobalProvider/GroupWizardProvider.tsx +6 -29
- package/src/locales/default/auth.ts +1 -1
- package/src/locales/default/file.ts +2 -0
- package/src/locales/default/metadata.ts +2 -2
- package/src/server/modules/AgentRuntime/AgentRuntimeCoordinator.ts +30 -30
- package/src/server/modules/AgentRuntime/AgentStateManager.ts +23 -23
- package/src/server/modules/AgentRuntime/InMemoryAgentStateManager.ts +16 -16
- package/src/server/modules/AgentRuntime/InMemoryStreamEventManager.ts +13 -13
- package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +2 -2
- package/src/server/modules/AgentRuntime/StreamEventManager.ts +18 -18
- package/src/server/modules/AgentRuntime/types.ts +21 -21
- package/src/server/routers/lambda/__tests__/agentGroup.test.ts +8 -8
- package/src/server/routers/lambda/agentGroup.ts +10 -12
- package/src/server/services/document/index.ts +1 -0
- package/src/store/agentGroup/slices/curd.test.ts +4 -4
- package/src/store/file/slices/fileManager/action.ts +12 -4
- package/src/store/home/slices/homeInput/action.ts +0 -3
- package/src/store/home/slices/sidebarUI/action.ts +9 -0
- package/src/store/session/slices/session/action.ts +5 -9
- package/src/app/[variants]/(mobile)/chat/settings/features/AgentTeamSettings/index.tsx +0 -95
- package/src/features/GroupChatSettings/AgentCard.tsx +0 -154
- package/src/features/GroupChatSettings/AgentTeamChatSettings.tsx +0 -179
- package/src/features/GroupChatSettings/AgentTeamMembersSettings.tsx +0 -244
- package/src/features/GroupChatSettings/AgentTeamMetaSettings.tsx +0 -94
- package/src/features/GroupChatSettings/AgentTeamSettings.tsx +0 -54
- package/src/features/GroupChatSettings/GroupCategory/index.tsx +0 -30
- package/src/features/GroupChatSettings/GroupCategory/useGroupCategory.tsx +0 -42
- package/src/features/GroupChatSettings/GroupChatSettingsProvider.tsx +0 -19
- package/src/features/GroupChatSettings/HostMemberCard.tsx +0 -113
- package/src/features/GroupChatSettings/StoreUpdater.tsx +0 -34
- package/src/features/GroupChatSettings/hooks/useGroupChatSettings.ts +0 -25
- package/src/features/GroupChatSettings/index.ts +0 -16
- package/src/features/GroupChatSettings/store/action.ts +0 -105
- package/src/features/GroupChatSettings/store/index.ts +0 -18
- package/src/features/GroupChatSettings/store/initialState.ts +0 -23
- package/src/features/GroupChatSettings/store/selectors.ts +0 -13
- package/src/features/ResourceManager/components/Tree/index.tsx +0 -883
- /package/src/features/ResourceManager/components/{Tree → LibraryHierarchy}/TreeSkeleton.tsx +0 -0
|
@@ -3,7 +3,7 @@ export default {
|
|
|
3
3
|
'changelog.title': 'Changelog',
|
|
4
4
|
'chat.description':
|
|
5
5
|
'{{appName}} brings you the best UI experience for ChatGPT, Claude, Gemini, and OLLaMA.',
|
|
6
|
-
'chat.title': '{{appName}} ·
|
|
6
|
+
'chat.title': '{{appName}} · Where Agents Collaborate',
|
|
7
7
|
'discover.assistants.description':
|
|
8
8
|
'Content, Q&A, images, video, voice, workflows—browse and add Agents from the Community.',
|
|
9
9
|
'discover.assistants.title': 'Agent Community',
|
|
@@ -30,5 +30,5 @@ export default {
|
|
|
30
30
|
'plugins.title': 'Skill Community',
|
|
31
31
|
'welcome.description':
|
|
32
32
|
'{{appName}} brings you the best UI experience for ChatGPT, Claude, Gemini, and OLLaMA.',
|
|
33
|
-
'welcome.title': 'Welcome to {{appName}} ·
|
|
33
|
+
'welcome.title': 'Welcome to {{appName}} · Where Agents Collaborate',
|
|
34
34
|
};
|
|
@@ -9,27 +9,27 @@ const log = debug('lobe-server:agent-runtime:coordinator');
|
|
|
9
9
|
|
|
10
10
|
export interface AgentRuntimeCoordinatorOptions {
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Custom state manager implementation
|
|
13
|
+
* Defaults to automatic selection based on Redis availability
|
|
14
14
|
*/
|
|
15
15
|
stateManager?: IAgentStateManager;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Custom stream event manager implementation
|
|
18
|
+
* Defaults to automatic selection based on Redis availability
|
|
19
19
|
*/
|
|
20
20
|
streamEventManager?: IStreamEventManager;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Agent Runtime Coordinator
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* Coordinates operations between AgentStateManager and StreamEventManager
|
|
26
|
+
* Responsible for sending corresponding events when state changes occur
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
* - Redis
|
|
30
|
-
* - Redis
|
|
28
|
+
* Default behavior:
|
|
29
|
+
* - Uses Redis implementation when Redis is available
|
|
30
|
+
* - Automatically falls back to in-memory implementation when Redis is unavailable (local development mode)
|
|
31
31
|
*
|
|
32
|
-
*
|
|
32
|
+
* Supports dependency injection, allowing custom implementations to be passed in
|
|
33
33
|
*/
|
|
34
34
|
export class AgentRuntimeCoordinator {
|
|
35
35
|
private stateManager: IAgentStateManager;
|
|
@@ -41,7 +41,7 @@ export class AgentRuntimeCoordinator {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* Create a new Agent operation and send initialization event
|
|
45
45
|
*/
|
|
46
46
|
async createAgentOperation(
|
|
47
47
|
operationId: string,
|
|
@@ -52,14 +52,14 @@ export class AgentRuntimeCoordinator {
|
|
|
52
52
|
},
|
|
53
53
|
): Promise<void> {
|
|
54
54
|
try {
|
|
55
|
-
//
|
|
55
|
+
// Create operation metadata
|
|
56
56
|
await this.stateManager.createOperationMetadata(operationId, data);
|
|
57
57
|
|
|
58
|
-
//
|
|
58
|
+
// Get the created metadata
|
|
59
59
|
const metadata = await this.stateManager.getOperationMetadata(operationId);
|
|
60
60
|
|
|
61
61
|
if (metadata) {
|
|
62
|
-
//
|
|
62
|
+
// Send agent runtime init event
|
|
63
63
|
await this.streamEventManager.publishAgentRuntimeInit(operationId, metadata);
|
|
64
64
|
log('[%s] Agent operation created and initialized', operationId);
|
|
65
65
|
}
|
|
@@ -70,16 +70,16 @@ export class AgentRuntimeCoordinator {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* Save Agent state and handle corresponding events
|
|
74
74
|
*/
|
|
75
75
|
async saveAgentState(operationId: string, state: AgentState): Promise<void> {
|
|
76
76
|
try {
|
|
77
77
|
const previousState = await this.stateManager.loadAgentState(operationId);
|
|
78
78
|
|
|
79
|
-
//
|
|
79
|
+
// Save state
|
|
80
80
|
await this.stateManager.saveAgentState(operationId, state);
|
|
81
81
|
|
|
82
|
-
//
|
|
82
|
+
// If status changes to done, send agent runtime end event
|
|
83
83
|
if (state.status === 'done' && previousState?.status !== 'done') {
|
|
84
84
|
await this.streamEventManager.publishAgentRuntimeEnd(operationId, state.stepCount, state);
|
|
85
85
|
log('[%s] Agent runtime completed', operationId);
|
|
@@ -91,18 +91,18 @@ export class AgentRuntimeCoordinator {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
94
|
+
* Save step result and handle corresponding events
|
|
95
95
|
*/
|
|
96
96
|
async saveStepResult(operationId: string, stepResult: StepResult): Promise<void> {
|
|
97
97
|
try {
|
|
98
|
-
//
|
|
98
|
+
// Get previous state for detecting state changes
|
|
99
99
|
const previousState = await this.stateManager.loadAgentState(operationId);
|
|
100
100
|
|
|
101
|
-
//
|
|
101
|
+
// Save step result
|
|
102
102
|
await this.stateManager.saveStepResult(operationId, stepResult);
|
|
103
103
|
|
|
104
|
-
//
|
|
105
|
-
//
|
|
104
|
+
// If status changes to done, send agent_runtime_end event
|
|
105
|
+
// This ensures agent_runtime_end is sent after all step events
|
|
106
106
|
if (stepResult.newState.status === 'done' && previousState?.status !== 'done') {
|
|
107
107
|
await this.streamEventManager.publishAgentRuntimeEnd(
|
|
108
108
|
operationId,
|
|
@@ -118,28 +118,28 @@ export class AgentRuntimeCoordinator {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
|
-
*
|
|
121
|
+
* Get Agent state
|
|
122
122
|
*/
|
|
123
123
|
async loadAgentState(operationId: string): Promise<AgentState | null> {
|
|
124
124
|
return this.stateManager.loadAgentState(operationId);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
*
|
|
128
|
+
* Get operation metadata
|
|
129
129
|
*/
|
|
130
130
|
async getOperationMetadata(operationId: string): Promise<AgentOperationMetadata | null> {
|
|
131
131
|
return this.stateManager.getOperationMetadata(operationId);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
|
-
*
|
|
135
|
+
* Get execution history
|
|
136
136
|
*/
|
|
137
137
|
async getExecutionHistory(operationId: string, limit?: number): Promise<any[]> {
|
|
138
138
|
return this.stateManager.getExecutionHistory(operationId, limit);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
|
-
*
|
|
142
|
+
* Delete Agent operation
|
|
143
143
|
*/
|
|
144
144
|
async deleteAgentOperation(operationId: string): Promise<void> {
|
|
145
145
|
try {
|
|
@@ -155,14 +155,14 @@ export class AgentRuntimeCoordinator {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/**
|
|
158
|
-
*
|
|
158
|
+
* Get active operations
|
|
159
159
|
*/
|
|
160
160
|
async getActiveOperations(): Promise<string[]> {
|
|
161
161
|
return this.stateManager.getActiveOperations();
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
|
-
*
|
|
165
|
+
* Get statistics
|
|
166
166
|
*/
|
|
167
167
|
async getStats(): Promise<{
|
|
168
168
|
activeOperations: number;
|
|
@@ -174,14 +174,14 @@ export class AgentRuntimeCoordinator {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
|
-
*
|
|
177
|
+
* Clean up expired operations
|
|
178
178
|
*/
|
|
179
179
|
async cleanupExpiredOperations(): Promise<number> {
|
|
180
180
|
return this.stateManager.cleanupExpiredOperations();
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
/**
|
|
184
|
-
*
|
|
184
|
+
* Close connections
|
|
185
185
|
*/
|
|
186
186
|
async disconnect(): Promise<void> {
|
|
187
187
|
await Promise.all([this.stateManager.disconnect(), this.streamEventManager.disconnect()]);
|
|
@@ -42,7 +42,7 @@ export class AgentStateManager {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* Save Agent state
|
|
46
46
|
*/
|
|
47
47
|
async saveAgentState(operationId: string, state: AgentState): Promise<void> {
|
|
48
48
|
const stateKey = `${this.STATE_PREFIX}:${operationId}`;
|
|
@@ -51,7 +51,7 @@ export class AgentStateManager {
|
|
|
51
51
|
const serializedState = JSON.stringify(state);
|
|
52
52
|
await this.redis.setex(stateKey, this.DEFAULT_TTL, serializedState);
|
|
53
53
|
|
|
54
|
-
//
|
|
54
|
+
// Update metadata
|
|
55
55
|
await this.updateOperationMetadata(operationId, {
|
|
56
56
|
lastActiveAt: new Date().toISOString(),
|
|
57
57
|
status: state.status,
|
|
@@ -59,7 +59,7 @@ export class AgentStateManager {
|
|
|
59
59
|
totalSteps: state.stepCount,
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
//
|
|
62
|
+
// State change events are recorded through the events array in saveStepResult
|
|
63
63
|
|
|
64
64
|
log('[%s] Saved state for step %d', operationId, state.stepCount);
|
|
65
65
|
} catch (error) {
|
|
@@ -69,7 +69,7 @@ export class AgentStateManager {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
72
|
+
* Load Agent state
|
|
73
73
|
*/
|
|
74
74
|
async loadAgentState(operationId: string): Promise<AgentState | null> {
|
|
75
75
|
const stateKey = `${this.STATE_PREFIX}:${operationId}`;
|
|
@@ -92,17 +92,17 @@ export class AgentStateManager {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
*
|
|
95
|
+
* Save step execution result
|
|
96
96
|
*/
|
|
97
97
|
async saveStepResult(operationId: string, stepResult: StepResult): Promise<void> {
|
|
98
98
|
const pipeline = this.redis.multi();
|
|
99
99
|
|
|
100
100
|
try {
|
|
101
|
-
//
|
|
101
|
+
// Save latest state
|
|
102
102
|
const stateKey = `${this.STATE_PREFIX}:${operationId}`;
|
|
103
103
|
pipeline.setex(stateKey, this.DEFAULT_TTL, JSON.stringify(stepResult.newState));
|
|
104
104
|
|
|
105
|
-
//
|
|
105
|
+
// Save step history
|
|
106
106
|
const stepsKey = `${this.STEPS_PREFIX}:${operationId}`;
|
|
107
107
|
const stepData = {
|
|
108
108
|
context: stepResult.nextContext,
|
|
@@ -114,19 +114,19 @@ export class AgentStateManager {
|
|
|
114
114
|
};
|
|
115
115
|
|
|
116
116
|
pipeline.lpush(stepsKey, JSON.stringify(stepData));
|
|
117
|
-
pipeline.ltrim(stepsKey, 0, 199); //
|
|
117
|
+
pipeline.ltrim(stepsKey, 0, 199); // Keep most recent 200 steps
|
|
118
118
|
pipeline.expire(stepsKey, this.DEFAULT_TTL);
|
|
119
119
|
|
|
120
|
-
//
|
|
120
|
+
// Save step event sequence to agent_runtime_events
|
|
121
121
|
if (stepResult.events && stepResult.events.length > 0) {
|
|
122
122
|
const eventsKey = `${this.EVENTS_PREFIX}:${operationId}`;
|
|
123
123
|
|
|
124
124
|
pipeline.lpush(eventsKey, JSON.stringify(stepResult.events));
|
|
125
|
-
pipeline.ltrim(eventsKey, 0, 199); //
|
|
125
|
+
pipeline.ltrim(eventsKey, 0, 199); // Keep events from most recent 200 steps
|
|
126
126
|
pipeline.expire(eventsKey, this.DEFAULT_TTL);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
//
|
|
129
|
+
// Update operation metadata
|
|
130
130
|
const metaKey = `${this.METADATA_PREFIX}:${operationId}`;
|
|
131
131
|
const metadata: Partial<AgentOperationMetadata> = {
|
|
132
132
|
lastActiveAt: new Date().toISOString(),
|
|
@@ -152,14 +152,14 @@ export class AgentStateManager {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
-
*
|
|
155
|
+
* Get execution history
|
|
156
156
|
*/
|
|
157
157
|
async getExecutionHistory(operationId: string, limit: number = 50): Promise<any[]> {
|
|
158
158
|
const stepsKey = `${this.STEPS_PREFIX}:${operationId}`;
|
|
159
159
|
|
|
160
160
|
try {
|
|
161
161
|
const history = await this.redis.lrange(stepsKey, 0, limit - 1);
|
|
162
|
-
return history.map((item) => JSON.parse(item)).reverse(); //
|
|
162
|
+
return history.map((item) => JSON.parse(item)).reverse(); // Earliest first
|
|
163
163
|
} catch (error) {
|
|
164
164
|
console.error('Failed to get execution history:', error);
|
|
165
165
|
return [];
|
|
@@ -167,7 +167,7 @@ export class AgentStateManager {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/**
|
|
170
|
-
*
|
|
170
|
+
* Get operation metadata
|
|
171
171
|
*/
|
|
172
172
|
async getOperationMetadata(operationId: string): Promise<AgentOperationMetadata | null> {
|
|
173
173
|
const metaKey = `${this.METADATA_PREFIX}:${operationId}`;
|
|
@@ -198,7 +198,7 @@ export class AgentStateManager {
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
/**
|
|
201
|
-
*
|
|
201
|
+
* Create new operation metadata
|
|
202
202
|
*/
|
|
203
203
|
async createOperationMetadata(
|
|
204
204
|
operationId: string,
|
|
@@ -222,7 +222,7 @@ export class AgentStateManager {
|
|
|
222
222
|
userId: data.userId,
|
|
223
223
|
};
|
|
224
224
|
|
|
225
|
-
//
|
|
225
|
+
// Serialize complex objects
|
|
226
226
|
const redisData: Record<string, string> = {
|
|
227
227
|
createdAt: metadata.createdAt,
|
|
228
228
|
lastActiveAt: metadata.lastActiveAt,
|
|
@@ -247,7 +247,7 @@ export class AgentStateManager {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
/**
|
|
250
|
-
*
|
|
250
|
+
* Update operation metadata
|
|
251
251
|
*/
|
|
252
252
|
private async updateOperationMetadata(
|
|
253
253
|
operationId: string,
|
|
@@ -278,7 +278,7 @@ export class AgentStateManager {
|
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
/**
|
|
281
|
-
*
|
|
281
|
+
* Delete all data for Agent operation
|
|
282
282
|
*/
|
|
283
283
|
async deleteAgentOperation(operationId: string): Promise<void> {
|
|
284
284
|
const keys = [
|
|
@@ -298,7 +298,7 @@ export class AgentStateManager {
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
/**
|
|
301
|
-
*
|
|
301
|
+
* Get all active operations
|
|
302
302
|
*/
|
|
303
303
|
async getActiveOperations(): Promise<string[]> {
|
|
304
304
|
try {
|
|
@@ -312,7 +312,7 @@ export class AgentStateManager {
|
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
/**
|
|
315
|
-
*
|
|
315
|
+
* Clean up expired operation data
|
|
316
316
|
*/
|
|
317
317
|
async cleanupExpiredOperations(): Promise<number> {
|
|
318
318
|
try {
|
|
@@ -327,7 +327,7 @@ export class AgentStateManager {
|
|
|
327
327
|
const now = Date.now();
|
|
328
328
|
const hoursSinceActive = (now - lastActiveTime) / (1000 * 60 * 60);
|
|
329
329
|
|
|
330
|
-
//
|
|
330
|
+
// Clean up operations inactive for more than 2 hours
|
|
331
331
|
if (hoursSinceActive > 2) {
|
|
332
332
|
await this.deleteAgentOperation(operationId);
|
|
333
333
|
cleanedCount++;
|
|
@@ -344,7 +344,7 @@ export class AgentStateManager {
|
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
/**
|
|
347
|
-
*
|
|
347
|
+
* Get statistics
|
|
348
348
|
*/
|
|
349
349
|
async getStats(): Promise<{
|
|
350
350
|
activeOperations: number;
|
|
@@ -397,7 +397,7 @@ export class AgentStateManager {
|
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
/**
|
|
400
|
-
*
|
|
400
|
+
* Close Redis connection
|
|
401
401
|
*/
|
|
402
402
|
async disconnect(): Promise<void> {
|
|
403
403
|
await this.redis.quit();
|
|
@@ -8,7 +8,7 @@ const log = debug('lobe-server:agent-runtime:in-memory-state-manager');
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* In-Memory Agent State Manager
|
|
11
|
-
*
|
|
11
|
+
* In-memory implementation for testing and local development environments
|
|
12
12
|
*/
|
|
13
13
|
export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
14
14
|
private states: Map<string, AgentState> = new Map();
|
|
@@ -17,10 +17,10 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
17
17
|
private events: Map<string, any[][]> = new Map();
|
|
18
18
|
|
|
19
19
|
async saveAgentState(operationId: string, state: AgentState): Promise<void> {
|
|
20
|
-
//
|
|
20
|
+
// Deep clone to avoid reference issues
|
|
21
21
|
this.states.set(operationId, structuredClone(state));
|
|
22
22
|
|
|
23
|
-
//
|
|
23
|
+
// Update metadata
|
|
24
24
|
const existingMeta = this.metadata.get(operationId);
|
|
25
25
|
if (existingMeta) {
|
|
26
26
|
existingMeta.lastActiveAt = new Date().toISOString();
|
|
@@ -39,15 +39,15 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
log('[%s] Loaded state (step %d)', operationId, state.stepCount);
|
|
42
|
-
//
|
|
42
|
+
// Return deep clone to prevent external modifications from affecting internal state
|
|
43
43
|
return structuredClone(state);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
async saveStepResult(operationId: string, stepResult: StepResult): Promise<void> {
|
|
47
|
-
//
|
|
47
|
+
// Save latest state
|
|
48
48
|
this.states.set(operationId, structuredClone(stepResult.newState));
|
|
49
49
|
|
|
50
|
-
//
|
|
50
|
+
// Save step history
|
|
51
51
|
let stepHistory = this.steps.get(operationId);
|
|
52
52
|
if (!stepHistory) {
|
|
53
53
|
stepHistory = [];
|
|
@@ -63,14 +63,14 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
63
63
|
timestamp: Date.now(),
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// Insert at beginning (newest first)
|
|
67
67
|
stepHistory.unshift(stepData);
|
|
68
|
-
//
|
|
68
|
+
// Keep most recent 200 steps
|
|
69
69
|
if (stepHistory.length > 200) {
|
|
70
70
|
stepHistory.length = 200;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
//
|
|
73
|
+
// Save step event sequence
|
|
74
74
|
if (stepResult.events && stepResult.events.length > 0) {
|
|
75
75
|
let eventHistory = this.events.get(operationId);
|
|
76
76
|
if (!eventHistory) {
|
|
@@ -83,7 +83,7 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
//
|
|
86
|
+
// Update operation metadata
|
|
87
87
|
const existingMeta = this.metadata.get(operationId);
|
|
88
88
|
if (existingMeta) {
|
|
89
89
|
existingMeta.lastActiveAt = new Date().toISOString();
|
|
@@ -106,7 +106,7 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
106
106
|
return [];
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
//
|
|
109
|
+
// Return reversed array (earliest first)
|
|
110
110
|
return history.slice(0, limit).reverse();
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -161,7 +161,7 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
161
161
|
const now = Date.now();
|
|
162
162
|
const hoursSinceActive = (now - lastActiveTime) / (1000 * 60 * 60);
|
|
163
163
|
|
|
164
|
-
//
|
|
164
|
+
// Clean up operations inactive for more than 1 hour
|
|
165
165
|
if (hoursSinceActive > 1) {
|
|
166
166
|
await this.deleteAgentOperation(operationId);
|
|
167
167
|
cleanedCount++;
|
|
@@ -214,12 +214,12 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
async disconnect(): Promise<void> {
|
|
217
|
-
//
|
|
217
|
+
// In-memory implementation doesn't need to disconnect
|
|
218
218
|
log('InMemoryAgentStateManager disconnected');
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
|
-
*
|
|
222
|
+
* Clear all data (for testing)
|
|
223
223
|
*/
|
|
224
224
|
clear(): void {
|
|
225
225
|
this.states.clear();
|
|
@@ -230,7 +230,7 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
/**
|
|
233
|
-
*
|
|
233
|
+
* Get event history (for test verification)
|
|
234
234
|
*/
|
|
235
235
|
getEventHistory(operationId: string): any[][] {
|
|
236
236
|
return this.events.get(operationId) ?? [];
|
|
@@ -238,6 +238,6 @@ export class InMemoryAgentStateManager implements IAgentStateManager {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
/**
|
|
241
|
-
*
|
|
241
|
+
* Singleton instance for testing and local development environments
|
|
242
242
|
*/
|
|
243
243
|
export const inMemoryAgentStateManager = new InMemoryAgentStateManager();
|
|
@@ -9,7 +9,7 @@ type EventCallback = (events: StreamEvent[]) => void;
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* In-Memory Stream Event Manager
|
|
12
|
-
*
|
|
12
|
+
* In-memory implementation for testing and local development environments
|
|
13
13
|
*/
|
|
14
14
|
export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
15
15
|
private streams: Map<string, StreamEvent[]> = new Map();
|
|
@@ -34,7 +34,7 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
34
34
|
timestamp: Date.now(),
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// Get or create stream
|
|
38
38
|
let stream = this.streams.get(operationId);
|
|
39
39
|
if (!stream) {
|
|
40
40
|
stream = [];
|
|
@@ -43,14 +43,14 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
43
43
|
|
|
44
44
|
stream.push(eventData);
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// Limit stream length to prevent memory overflow
|
|
47
47
|
if (stream.length > 1000) {
|
|
48
48
|
stream.shift();
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
log('Published event %s for operation %s:%d', eventData.type, operationId, eventData.stepIndex);
|
|
52
52
|
|
|
53
|
-
//
|
|
53
|
+
// Notify subscribers
|
|
54
54
|
const callbacks = this.subscribers.get(operationId);
|
|
55
55
|
if (callbacks) {
|
|
56
56
|
for (const callback of callbacks) {
|
|
@@ -111,7 +111,7 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
111
111
|
return [];
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
//
|
|
114
|
+
// Return most recent count events (in reverse order)
|
|
115
115
|
return stream.slice(-count).reverse();
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -126,12 +126,12 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
async disconnect(): Promise<void> {
|
|
129
|
-
//
|
|
129
|
+
// In-memory implementation doesn't need to disconnect
|
|
130
130
|
log('InMemoryStreamEventManager disconnected');
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
*
|
|
134
|
+
* Subscribe to stream events (for testing)
|
|
135
135
|
*/
|
|
136
136
|
subscribe(operationId: string, callback: EventCallback): () => void {
|
|
137
137
|
let callbacks = this.subscribers.get(operationId);
|
|
@@ -141,7 +141,7 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
141
141
|
}
|
|
142
142
|
callbacks.push(callback);
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// Return unsubscribe function
|
|
145
145
|
return () => {
|
|
146
146
|
const cbs = this.subscribers.get(operationId);
|
|
147
147
|
if (cbs) {
|
|
@@ -154,7 +154,7 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
|
-
*
|
|
157
|
+
* Clear all data (for testing)
|
|
158
158
|
*/
|
|
159
159
|
clear(): void {
|
|
160
160
|
this.streams.clear();
|
|
@@ -164,14 +164,14 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
|
-
*
|
|
167
|
+
* Get all events (for test verification)
|
|
168
168
|
*/
|
|
169
169
|
getAllEvents(operationId: string): StreamEvent[] {
|
|
170
170
|
return this.streams.get(operationId) ?? [];
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
174
|
+
* Wait for a specific event type (for testing)
|
|
175
175
|
*/
|
|
176
176
|
waitForEvent(
|
|
177
177
|
operationId: string,
|
|
@@ -196,7 +196,7 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
196
196
|
}
|
|
197
197
|
});
|
|
198
198
|
|
|
199
|
-
//
|
|
199
|
+
// Check existing events
|
|
200
200
|
const existingEvents = this.streams.get(operationId) ?? [];
|
|
201
201
|
for (const event of existingEvents) {
|
|
202
202
|
if (event.type === eventType) {
|
|
@@ -211,6 +211,6 @@ export class InMemoryStreamEventManager implements IStreamEventManager {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
/**
|
|
214
|
-
*
|
|
214
|
+
* Singleton instance for testing and local development environments
|
|
215
215
|
*/
|
|
216
216
|
export const inMemoryStreamEventManager = new InMemoryStreamEventManager();
|
|
@@ -43,8 +43,8 @@ export const createRuntimeExecutors = (
|
|
|
43
43
|
ctx: RuntimeExecutorContext,
|
|
44
44
|
): Partial<Record<AgentInstruction['type'], InstructionExecutor>> => ({
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* Create streaming LLM executor
|
|
47
|
+
* Integrates Agent Runtime and stream event publishing
|
|
48
48
|
*/
|
|
49
49
|
call_llm: async (instruction, state) => {
|
|
50
50
|
const { payload } = instruction as Extract<AgentInstruction, { type: 'call_llm' }>;
|