@lobehub/lobehub 2.0.0-next.85 → 2.0.0-next.86
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 +25 -0
- package/apps/desktop/src/main/modules/networkProxy/dispatcher.ts +16 -16
- package/apps/desktop/src/main/modules/networkProxy/tester.ts +11 -11
- package/apps/desktop/src/main/modules/networkProxy/urlBuilder.ts +3 -3
- package/apps/desktop/src/main/modules/networkProxy/validator.ts +10 -10
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/agent-runtime/src/core/runtime.ts +36 -1
- package/packages/agent-runtime/src/types/event.ts +1 -0
- package/packages/agent-runtime/src/types/generalAgent.ts +16 -0
- package/packages/agent-runtime/src/types/instruction.ts +30 -0
- package/packages/agent-runtime/src/types/runtime.ts +7 -0
- package/packages/types/src/message/common/metadata.ts +3 -0
- package/packages/types/src/message/common/tools.ts +2 -2
- package/packages/types/src/tool/search/index.ts +8 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +7 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +15 -14
- package/src/app/[variants]/(main)/chat/session/features/SessionListContent/List/Item/index.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
- package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +1 -1
- package/src/features/Conversation/Messages/User/index.tsx +3 -3
- package/src/features/Conversation/Messages/index.tsx +3 -3
- package/src/features/Conversation/components/AutoScroll.tsx +2 -2
- package/src/services/search.ts +2 -2
- package/src/store/chat/agents/GeneralChatAgent.ts +98 -0
- package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +366 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/call-llm.test.ts +1217 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/call-tool.test.ts +1976 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/finish.test.ts +453 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/index.ts +4 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockInstructions.ts +126 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockMessages.ts +94 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockOperations.ts +96 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockStore.ts +138 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/assertions.ts +185 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/index.ts +3 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/operationTestUtils.ts +94 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/helpers/testExecutor.ts +139 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/request-human-approve.test.ts +545 -0
- package/src/store/chat/agents/__tests__/createAgentExecutors/resolve-aborted-tools.test.ts +686 -0
- package/src/store/chat/agents/createAgentExecutors.ts +313 -80
- package/src/store/chat/selectors.ts +1 -0
- package/src/store/chat/slices/aiChat/__tests__/ai-chat.integration.test.ts +667 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/cancel-functionality.test.ts +137 -27
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +163 -125
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +12 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +0 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +0 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +286 -19
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +0 -112
- package/src/store/chat/slices/aiChat/actions/conversationControl.ts +42 -99
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +90 -57
- package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +5 -25
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +220 -98
- package/src/store/chat/slices/aiChat/actions/streamingStates.ts +0 -34
- package/src/store/chat/slices/aiChat/initialState.ts +0 -28
- package/src/store/chat/slices/aiChat/selectors.test.ts +280 -0
- package/src/store/chat/slices/aiChat/selectors.ts +31 -7
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +21 -30
- package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +29 -49
- package/src/store/chat/slices/builtinTool/actions/interpreter.ts +83 -48
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +78 -28
- package/src/store/chat/slices/builtinTool/actions/search.ts +146 -59
- package/src/store/chat/slices/builtinTool/selectors.test.ts +258 -0
- package/src/store/chat/slices/builtinTool/selectors.ts +25 -4
- package/src/store/chat/slices/message/action.test.ts +134 -16
- package/src/store/chat/slices/message/actions/internals.ts +33 -7
- package/src/store/chat/slices/message/actions/optimisticUpdate.ts +85 -52
- package/src/store/chat/slices/message/initialState.ts +0 -10
- package/src/store/chat/slices/message/selectors/messageState.ts +34 -12
- package/src/store/chat/slices/operation/__tests__/actions.test.ts +712 -16
- package/src/store/chat/slices/operation/__tests__/integration.test.ts +342 -0
- package/src/store/chat/slices/operation/__tests__/selectors.test.ts +257 -17
- package/src/store/chat/slices/operation/actions.ts +218 -11
- package/src/store/chat/slices/operation/selectors.ts +135 -6
- package/src/store/chat/slices/operation/types.ts +29 -3
- package/src/store/chat/slices/plugin/action.test.ts +30 -322
- package/src/store/chat/slices/plugin/actions/internals.ts +0 -14
- package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +21 -19
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +45 -27
- package/src/store/chat/slices/plugin/actions/publicApi.ts +3 -4
- package/src/store/chat/slices/plugin/actions/workflow.ts +0 -55
- package/src/store/chat/slices/thread/selectors/index.ts +4 -2
- package/src/store/chat/slices/translate/action.ts +54 -41
- package/src/tools/web-browsing/ExecutionRuntime/index.ts +5 -2
- package/src/tools/web-browsing/Portal/Search/Footer.tsx +11 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.86](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.85...v2.0.0-next.86)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-11-19**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Support user abort in the agent runtime.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Support user abort in the agent runtime, closes [#10289](https://github.com/lobehub/lobe-chat/issues/10289) ([0925069](https://github.com/lobehub/lobe-chat/commit/0925069))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.85](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.84...v2.0.0-next.85)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2025-11-19**</sup>
|
|
@@ -10,14 +10,14 @@ import { ProxyUrlBuilder } from './urlBuilder';
|
|
|
10
10
|
const logger = createLogger('modules:networkProxy:dispatcher');
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Proxy dispatcher manager
|
|
14
14
|
*/
|
|
15
15
|
export class ProxyDispatcherManager {
|
|
16
16
|
private static isChanging = false;
|
|
17
17
|
private static changeQueue: Array<() => Promise<void>> = [];
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* Apply proxy settings (with concurrency control)
|
|
21
21
|
*/
|
|
22
22
|
static async applyProxySettings(config: NetworkProxySettings): Promise<void> {
|
|
23
23
|
return new Promise((resolve, reject) => {
|
|
@@ -31,17 +31,17 @@ export class ProxyDispatcherManager {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
if (this.isChanging) {
|
|
34
|
-
//
|
|
34
|
+
// If currently switching, add to queue
|
|
35
35
|
this.changeQueue.push(operation);
|
|
36
36
|
} else {
|
|
37
|
-
//
|
|
37
|
+
// Execute immediately
|
|
38
38
|
operation();
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* Execute proxy settings application
|
|
45
45
|
*/
|
|
46
46
|
private static async doApplyProxySettings(config: NetworkProxySettings): Promise<void> {
|
|
47
47
|
this.isChanging = true;
|
|
@@ -49,22 +49,22 @@ export class ProxyDispatcherManager {
|
|
|
49
49
|
try {
|
|
50
50
|
const currentDispatcher = getGlobalDispatcher();
|
|
51
51
|
|
|
52
|
-
//
|
|
52
|
+
// Disable proxy, restore default connection
|
|
53
53
|
if (!config.enableProxy) {
|
|
54
54
|
await this.safeDestroyDispatcher(currentDispatcher);
|
|
55
|
-
//
|
|
55
|
+
// Create a new default Agent to replace the proxy
|
|
56
56
|
setGlobalDispatcher(new Agent());
|
|
57
57
|
logger.debug('Proxy disabled, reset to direct connection mode');
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
//
|
|
61
|
+
// Build proxy URL
|
|
62
62
|
const proxyUrl = ProxyUrlBuilder.build(config);
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// Create proxy agent
|
|
65
65
|
const agent = this.createProxyAgent(config.proxyType, proxyUrl);
|
|
66
66
|
|
|
67
|
-
//
|
|
67
|
+
// Destroy old dispatcher before switching proxy
|
|
68
68
|
await this.safeDestroyDispatcher(currentDispatcher);
|
|
69
69
|
setGlobalDispatcher(agent);
|
|
70
70
|
|
|
@@ -77,7 +77,7 @@ export class ProxyDispatcherManager {
|
|
|
77
77
|
} finally {
|
|
78
78
|
this.isChanging = false;
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// Process next operation in queue
|
|
81
81
|
if (this.changeQueue.length > 0) {
|
|
82
82
|
const nextOperation = this.changeQueue.shift();
|
|
83
83
|
if (nextOperation) {
|
|
@@ -88,12 +88,12 @@ export class ProxyDispatcherManager {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
91
|
+
* Create proxy agent
|
|
92
92
|
*/
|
|
93
93
|
static createProxyAgent(proxyType: string, proxyUrl: string) {
|
|
94
94
|
try {
|
|
95
95
|
if (proxyType === 'socks5') {
|
|
96
|
-
//
|
|
96
|
+
// Parse SOCKS5 proxy URL
|
|
97
97
|
const url = new URL(proxyUrl);
|
|
98
98
|
const socksProxies: SocksProxies = [
|
|
99
99
|
{
|
|
@@ -109,10 +109,10 @@ export class ProxyDispatcherManager {
|
|
|
109
109
|
},
|
|
110
110
|
];
|
|
111
111
|
|
|
112
|
-
//
|
|
112
|
+
// Use fetch-socks to handle SOCKS5 proxy
|
|
113
113
|
return socksDispatcher(socksProxies);
|
|
114
114
|
} else {
|
|
115
|
-
// undici
|
|
115
|
+
// undici's ProxyAgent supports http, https
|
|
116
116
|
return new ProxyAgent({ uri: proxyUrl });
|
|
117
117
|
}
|
|
118
118
|
} catch (error) {
|
|
@@ -124,7 +124,7 @@ export class ProxyDispatcherManager {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
|
-
*
|
|
127
|
+
* Safely destroy dispatcher
|
|
128
128
|
*/
|
|
129
129
|
private static async safeDestroyDispatcher(dispatcher: any): Promise<void> {
|
|
130
130
|
try {
|
|
@@ -11,7 +11,7 @@ import { ProxyConfigValidator } from './validator';
|
|
|
11
11
|
const logger = createLogger('modules:networkProxy:tester');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Proxy connection test result
|
|
15
15
|
*/
|
|
16
16
|
export interface ProxyTestResult {
|
|
17
17
|
message?: string;
|
|
@@ -20,14 +20,14 @@ export interface ProxyTestResult {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Proxy connection tester
|
|
24
24
|
*/
|
|
25
25
|
export class ProxyConnectionTester {
|
|
26
|
-
private static readonly DEFAULT_TIMEOUT = 10_000; // 10
|
|
26
|
+
private static readonly DEFAULT_TIMEOUT = 10_000; // 10 seconds timeout
|
|
27
27
|
private static readonly DEFAULT_TEST_URL = 'https://www.google.com';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Test proxy connection
|
|
31
31
|
*/
|
|
32
32
|
static async testConnection(
|
|
33
33
|
url: string = this.DEFAULT_TEST_URL,
|
|
@@ -77,13 +77,13 @@ export class ProxyConnectionTester {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
80
|
+
* Test connection with specified proxy configuration
|
|
81
81
|
*/
|
|
82
82
|
static async testProxyConfig(
|
|
83
83
|
config: NetworkProxySettings,
|
|
84
84
|
testUrl: string = this.DEFAULT_TEST_URL,
|
|
85
85
|
): Promise<ProxyTestResult> {
|
|
86
|
-
//
|
|
86
|
+
// Validate configuration
|
|
87
87
|
const validation = ProxyConfigValidator.validate(config);
|
|
88
88
|
if (!validation.isValid) {
|
|
89
89
|
return {
|
|
@@ -92,12 +92,12 @@ export class ProxyConnectionTester {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
//
|
|
95
|
+
// If proxy is not enabled, test directly
|
|
96
96
|
if (!config.enableProxy) {
|
|
97
97
|
return this.testConnection(testUrl);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
//
|
|
100
|
+
// Create temporary proxy agent for testing
|
|
101
101
|
try {
|
|
102
102
|
const proxyUrl = ProxyUrlBuilder.build(config);
|
|
103
103
|
logger.debug(`Testing proxy with URL: ${proxyUrl}`);
|
|
@@ -108,7 +108,7 @@ export class ProxyConnectionTester {
|
|
|
108
108
|
const controller = new AbortController();
|
|
109
109
|
const timeoutId = setTimeout(() => controller.abort(), this.DEFAULT_TIMEOUT);
|
|
110
110
|
|
|
111
|
-
//
|
|
111
|
+
// Temporarily set proxy for testing
|
|
112
112
|
const originalDispatcher = getGlobalDispatcher();
|
|
113
113
|
setGlobalDispatcher(agent);
|
|
114
114
|
|
|
@@ -138,9 +138,9 @@ export class ProxyConnectionTester {
|
|
|
138
138
|
clearTimeout(timeoutId);
|
|
139
139
|
throw fetchError;
|
|
140
140
|
} finally {
|
|
141
|
-
//
|
|
141
|
+
// Restore original dispatcher
|
|
142
142
|
setGlobalDispatcher(originalDispatcher);
|
|
143
|
-
//
|
|
143
|
+
// Clean up temporary proxy agent
|
|
144
144
|
if (agent && typeof agent.destroy === 'function') {
|
|
145
145
|
try {
|
|
146
146
|
await agent.destroy();
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { NetworkProxySettings } from '@lobechat/electron-client-ipc';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Proxy URL builder
|
|
5
5
|
*/
|
|
6
6
|
export const ProxyUrlBuilder = {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Build proxy URL
|
|
9
9
|
*/
|
|
10
10
|
build(config: NetworkProxySettings): string {
|
|
11
11
|
const { proxyType, proxyServer, proxyPort, proxyRequireAuth, proxyUsername, proxyPassword } =
|
|
@@ -13,7 +13,7 @@ export const ProxyUrlBuilder = {
|
|
|
13
13
|
|
|
14
14
|
let proxyUrl = `${proxyType}://${proxyServer}:${proxyPort}`;
|
|
15
15
|
|
|
16
|
-
//
|
|
16
|
+
// Add authentication information
|
|
17
17
|
if (proxyRequireAuth && proxyUsername && proxyPassword) {
|
|
18
18
|
const encodedUsername = encodeURIComponent(proxyUsername);
|
|
19
19
|
const encodedPassword = encodeURIComponent(proxyPassword);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NetworkProxySettings } from '@lobechat/electron-client-ipc';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Proxy configuration validation result
|
|
5
5
|
*/
|
|
6
6
|
export interface ProxyValidationResult {
|
|
7
7
|
errors: string[];
|
|
@@ -9,38 +9,38 @@ export interface ProxyValidationResult {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Proxy configuration validator
|
|
13
13
|
*/
|
|
14
14
|
export class ProxyConfigValidator {
|
|
15
15
|
private static readonly SUPPORTED_TYPES = ['http', 'https', 'socks5'] as const;
|
|
16
16
|
private static readonly DEFAULT_BYPASS = 'localhost,127.0.0.1,::1';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Validate proxy configuration
|
|
20
20
|
*/
|
|
21
21
|
static validate(config: NetworkProxySettings): ProxyValidationResult {
|
|
22
22
|
const errors: string[] = [];
|
|
23
23
|
|
|
24
|
-
//
|
|
24
|
+
// If proxy is not enabled, skip validation
|
|
25
25
|
if (!config.enableProxy) {
|
|
26
26
|
return { errors: [], isValid: true };
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
//
|
|
29
|
+
// Validate proxy type
|
|
30
30
|
if (!this.SUPPORTED_TYPES.includes(config.proxyType as any)) {
|
|
31
31
|
errors.push(
|
|
32
32
|
`Unsupported proxy type: ${config.proxyType}. Supported types: ${this.SUPPORTED_TYPES.join(', ')}`,
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
//
|
|
36
|
+
// Validate proxy server
|
|
37
37
|
if (!config.proxyServer?.trim()) {
|
|
38
38
|
errors.push('Proxy server is required when proxy is enabled');
|
|
39
39
|
} else if (!this.isValidHost(config.proxyServer)) {
|
|
40
40
|
errors.push('Invalid proxy server format');
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// Validate proxy port
|
|
44
44
|
if (!config.proxyPort?.trim()) {
|
|
45
45
|
errors.push('Proxy port is required when proxy is enabled');
|
|
46
46
|
} else {
|
|
@@ -50,7 +50,7 @@ export class ProxyConfigValidator {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
//
|
|
53
|
+
// Validate authentication information
|
|
54
54
|
if (config.proxyRequireAuth) {
|
|
55
55
|
if (!config.proxyUsername?.trim()) {
|
|
56
56
|
errors.push('Proxy username is required when authentication is enabled');
|
|
@@ -67,10 +67,10 @@ export class ProxyConfigValidator {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
|
-
*
|
|
70
|
+
* Validate host format
|
|
71
71
|
*/
|
|
72
72
|
private static isValidHost(host: string): boolean {
|
|
73
|
-
//
|
|
73
|
+
// Simple host validation (IP address or domain name)
|
|
74
74
|
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
75
75
|
const domainRegex =
|
|
76
76
|
/^[\dA-Za-z]([\dA-Za-z-]*[\dA-Za-z])?(\.[\dA-Za-z]([\dA-Za-z-]*[\dA-Za-z])?)*$/;
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.86",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -24,11 +24,16 @@ import {
|
|
|
24
24
|
*/
|
|
25
25
|
export class AgentRuntime {
|
|
26
26
|
private executors: Record<AgentInstruction['type'], InstructionExecutor>;
|
|
27
|
+
private operationId?: string;
|
|
28
|
+
private getOperation?: RuntimeConfig['getOperation'];
|
|
27
29
|
|
|
28
30
|
constructor(
|
|
29
31
|
private agent: Agent,
|
|
30
32
|
private config: RuntimeConfig = {},
|
|
31
33
|
) {
|
|
34
|
+
this.operationId = config.operationId;
|
|
35
|
+
this.getOperation = config.getOperation;
|
|
36
|
+
|
|
32
37
|
// Build executors with priority: agent.executors > config.executors > built-in
|
|
33
38
|
this.executors = {
|
|
34
39
|
call_llm: this.createCallLLMExecutor(),
|
|
@@ -44,6 +49,28 @@ export class AgentRuntime {
|
|
|
44
49
|
};
|
|
45
50
|
}
|
|
46
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Get operation context (sessionId, topicId, etc.)
|
|
54
|
+
* Returns the business context captured by the operation
|
|
55
|
+
*/
|
|
56
|
+
getContext() {
|
|
57
|
+
if (!this.operationId || !this.getOperation) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
return this.getOperation(this.operationId).context;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get operation abort controller
|
|
65
|
+
* Returns the AbortController for cancellation
|
|
66
|
+
*/
|
|
67
|
+
getAbortController(): AbortController | undefined {
|
|
68
|
+
if (!this.operationId || !this.getOperation) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return this.getOperation(this.operationId).abortController;
|
|
72
|
+
}
|
|
73
|
+
|
|
47
74
|
/**
|
|
48
75
|
* Executes a single step of the Plan -> Execute loop.
|
|
49
76
|
* @param state - Current agent state
|
|
@@ -194,6 +221,7 @@ export class AgentRuntime {
|
|
|
194
221
|
approvedToolCall: ChatToolPayload,
|
|
195
222
|
): Promise<{ events: AgentEvent[]; newState: AgentState; nextContext?: AgentRuntimeContext }> {
|
|
196
223
|
const context: AgentRuntimeContext = {
|
|
224
|
+
operationId: this.operationId,
|
|
197
225
|
payload: { approvedToolCall },
|
|
198
226
|
phase: 'human_approved_tool',
|
|
199
227
|
session: this.createSessionContext(state),
|
|
@@ -289,10 +317,11 @@ export class AgentRuntime {
|
|
|
289
317
|
}
|
|
290
318
|
|
|
291
319
|
// Otherwise, just return the resumed state
|
|
320
|
+
const initialContext = this.createInitialContext(newState);
|
|
292
321
|
return {
|
|
293
322
|
events: [resumeEvent],
|
|
294
323
|
newState,
|
|
295
|
-
nextContext:
|
|
324
|
+
nextContext: initialContext,
|
|
296
325
|
};
|
|
297
326
|
}
|
|
298
327
|
|
|
@@ -439,6 +468,7 @@ export class AgentRuntime {
|
|
|
439
468
|
|
|
440
469
|
// Provide next context based on LLM result
|
|
441
470
|
const nextContext: AgentRuntimeContext = {
|
|
471
|
+
operationId: this.operationId,
|
|
442
472
|
payload: {
|
|
443
473
|
hasToolCalls: toolCalls.length > 0,
|
|
444
474
|
result: { content: assistantContent, tool_calls: toolCalls },
|
|
@@ -511,6 +541,7 @@ export class AgentRuntime {
|
|
|
511
541
|
|
|
512
542
|
// Provide next context for tool result
|
|
513
543
|
const nextContext: AgentRuntimeContext = {
|
|
544
|
+
operationId: this.operationId,
|
|
514
545
|
payload: {
|
|
515
546
|
result,
|
|
516
547
|
toolCall,
|
|
@@ -726,6 +757,7 @@ export class AgentRuntime {
|
|
|
726
757
|
events: allEvents,
|
|
727
758
|
newState,
|
|
728
759
|
nextContext: {
|
|
760
|
+
operationId: this.operationId,
|
|
729
761
|
payload: {
|
|
730
762
|
parentMessageId: lastParentMessageId,
|
|
731
763
|
toolCount: results.length,
|
|
@@ -792,6 +824,7 @@ export class AgentRuntime {
|
|
|
792
824
|
events: [warningEvent],
|
|
793
825
|
newState,
|
|
794
826
|
nextContext: {
|
|
827
|
+
operationId: this.operationId,
|
|
795
828
|
payload: { error: warningEvent.error, isCostWarning: true },
|
|
796
829
|
phase: 'error' as const,
|
|
797
830
|
session: this.createSessionContext(newState),
|
|
@@ -821,6 +854,7 @@ export class AgentRuntime {
|
|
|
821
854
|
|
|
822
855
|
if (lastMessage?.role === 'user') {
|
|
823
856
|
return {
|
|
857
|
+
operationId: this.operationId,
|
|
824
858
|
payload: {
|
|
825
859
|
isFirstMessage: state.messages.length === 1,
|
|
826
860
|
message: lastMessage,
|
|
@@ -831,6 +865,7 @@ export class AgentRuntime {
|
|
|
831
865
|
}
|
|
832
866
|
|
|
833
867
|
return {
|
|
868
|
+
operationId: this.operationId,
|
|
834
869
|
payload: undefined,
|
|
835
870
|
phase: 'init',
|
|
836
871
|
session: this.createSessionContext(state),
|
|
@@ -61,6 +61,7 @@ export interface AgentEventHumanSelectRequired {
|
|
|
61
61
|
export type FinishReason =
|
|
62
62
|
| 'completed' // Normal completion
|
|
63
63
|
| 'user_requested' // User requested to end
|
|
64
|
+
| 'user_aborted' // User abort
|
|
64
65
|
| 'max_steps_exceeded' // Reached maximum steps limit
|
|
65
66
|
| 'cost_limit_exceeded' // Reached cost limit
|
|
66
67
|
| 'timeout' // Execution timeout
|
|
@@ -42,6 +42,22 @@ export interface GeneralAgentCallToolsBatchResultPayload {
|
|
|
42
42
|
toolResults: GeneralAgentCallToolResultPayload[];
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
export interface GeneralAgentHumanAbortPayload {
|
|
46
|
+
/** Whether there are pending tool calls */
|
|
47
|
+
hasToolsCalling?: boolean;
|
|
48
|
+
/** Parent message ID (assistant message) */
|
|
49
|
+
parentMessageId: string;
|
|
50
|
+
/** Reason for the abort */
|
|
51
|
+
reason: string;
|
|
52
|
+
/** LLM result including content and tool_calls */
|
|
53
|
+
result?: {
|
|
54
|
+
content: string;
|
|
55
|
+
tool_calls?: any[];
|
|
56
|
+
};
|
|
57
|
+
/** Pending tool calls that need to be cancelled */
|
|
58
|
+
toolsCalling?: ChatToolPayload[];
|
|
59
|
+
}
|
|
60
|
+
|
|
45
61
|
export interface GeneralAgentConfig {
|
|
46
62
|
agentConfig?: {
|
|
47
63
|
[key: string]: any;
|
|
@@ -24,6 +24,7 @@ export interface AgentRuntimeContext {
|
|
|
24
24
|
| 'tools_batch_result'
|
|
25
25
|
| 'human_response'
|
|
26
26
|
| 'human_approved_tool'
|
|
27
|
+
| 'human_abort'
|
|
27
28
|
| 'error';
|
|
28
29
|
|
|
29
30
|
/** Session info (kept for backward compatibility, will be optional in the future) */
|
|
@@ -104,6 +105,22 @@ export interface CallingToolPayload {
|
|
|
104
105
|
type: 'mcp' | 'default' | 'markdown' | 'standalone';
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
export interface HumanAbortPayload {
|
|
109
|
+
/** Whether there are pending tool calls */
|
|
110
|
+
hasToolsCalling?: boolean;
|
|
111
|
+
/** Parent message ID (assistant message) */
|
|
112
|
+
parentMessageId: string;
|
|
113
|
+
/** Reason for the abort */
|
|
114
|
+
reason: string;
|
|
115
|
+
/** LLM result including content and tool_calls */
|
|
116
|
+
result?: {
|
|
117
|
+
content: string;
|
|
118
|
+
tool_calls?: any[];
|
|
119
|
+
};
|
|
120
|
+
/** Pending tool calls that need to be cancelled */
|
|
121
|
+
toolsCalling?: ChatToolPayload[];
|
|
122
|
+
}
|
|
123
|
+
|
|
107
124
|
export interface AgentInstructionCallLlm {
|
|
108
125
|
payload: any;
|
|
109
126
|
type: 'call_llm';
|
|
@@ -154,6 +171,18 @@ export interface AgentInstructionFinish {
|
|
|
154
171
|
type: 'finish';
|
|
155
172
|
}
|
|
156
173
|
|
|
174
|
+
export interface AgentInstructionResolveAbortedTools {
|
|
175
|
+
payload: {
|
|
176
|
+
/** Parent message ID (assistant message) */
|
|
177
|
+
parentMessageId: string;
|
|
178
|
+
/** Reason for the abort */
|
|
179
|
+
reason?: string;
|
|
180
|
+
/** Tool calls that need to be resolved/cancelled */
|
|
181
|
+
toolsCalling: ChatToolPayload[];
|
|
182
|
+
};
|
|
183
|
+
type: 'resolve_aborted_tools';
|
|
184
|
+
}
|
|
185
|
+
|
|
157
186
|
/**
|
|
158
187
|
* A serializable instruction object that the "Agent" (Brain) returns
|
|
159
188
|
* to the "AgentRuntime" (Engine) to execute.
|
|
@@ -165,4 +194,5 @@ export type AgentInstruction =
|
|
|
165
194
|
| AgentInstructionRequestHumanPrompt
|
|
166
195
|
| AgentInstructionRequestHumanSelect
|
|
167
196
|
| AgentInstructionRequestHumanApprove
|
|
197
|
+
| AgentInstructionResolveAbortedTools
|
|
168
198
|
| AgentInstructionFinish;
|
|
@@ -15,4 +15,11 @@ export type InstructionExecutor = (
|
|
|
15
15
|
export interface RuntimeConfig {
|
|
16
16
|
/** Custom executors for specific instruction types */
|
|
17
17
|
executors?: Partial<Record<AgentInstruction['type'], InstructionExecutor>>;
|
|
18
|
+
/** Function to get operation context and abort controller */
|
|
19
|
+
getOperation?: (operationId: string) => {
|
|
20
|
+
abortController: AbortController;
|
|
21
|
+
context: Record<string, any>;
|
|
22
|
+
};
|
|
23
|
+
/** Operation ID for tracking this runtime instance */
|
|
24
|
+
operationId?: string;
|
|
18
25
|
}
|
|
@@ -108,10 +108,13 @@ export interface ModelPerformance {
|
|
|
108
108
|
export interface MessageMetadata extends ModelUsage, ModelPerformance {
|
|
109
109
|
activeBranchIndex?: number;
|
|
110
110
|
activeColumn?: boolean;
|
|
111
|
+
finishType?: string;
|
|
111
112
|
/**
|
|
112
113
|
* Message collapse state
|
|
113
114
|
* true: collapsed, false/undefined: expanded
|
|
114
115
|
*/
|
|
115
116
|
collapsed?: boolean;
|
|
116
117
|
compare?: boolean;
|
|
118
|
+
usage?: ModelUsage;
|
|
119
|
+
performance?: ModelPerformance;
|
|
117
120
|
}
|
|
@@ -7,12 +7,12 @@ import { LobeToolRenderType } from '../../tool';
|
|
|
7
7
|
// ToolIntervention must be defined first to avoid circular dependency
|
|
8
8
|
export interface ToolIntervention {
|
|
9
9
|
rejectedReason?: string;
|
|
10
|
-
status?: 'pending' | 'approved' | 'rejected' | 'none';
|
|
10
|
+
status?: 'pending' | 'approved' | 'rejected' | 'aborted' | 'none';
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const ToolInterventionSchema = z.object({
|
|
14
14
|
rejectedReason: z.string().optional(),
|
|
15
|
-
status: z.enum(['pending', 'approved', 'rejected', 'none']).optional(),
|
|
15
|
+
status: z.enum(['pending', 'approved', 'rejected', 'aborted', 'none']).optional(),
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
export interface ChatPluginPayload {
|
|
@@ -48,6 +48,12 @@ export interface UniformSearchResponse {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export interface SearchServiceImpl {
|
|
51
|
-
crawlPages(
|
|
52
|
-
|
|
51
|
+
crawlPages(
|
|
52
|
+
params: CrawlMultiPagesQuery,
|
|
53
|
+
options?: { signal?: AbortSignal },
|
|
54
|
+
): Promise<{ results: CrawlUniformResult[] }>;
|
|
55
|
+
webSearch(
|
|
56
|
+
params: SearchQuery,
|
|
57
|
+
options?: { signal?: AbortSignal },
|
|
58
|
+
): Promise<UniformSearchResponse>;
|
|
53
59
|
}
|
package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
9
9
|
import { ActionKeys } from '@/features/ChatInput/ActionBar/config';
|
|
10
10
|
import { useInitAgentConfig } from '@/hooks/useInitAgentConfig';
|
|
11
11
|
import { useChatStore } from '@/store/chat';
|
|
12
|
-
import {
|
|
12
|
+
import { operationSelectors } from '@/store/chat/selectors';
|
|
13
13
|
|
|
14
14
|
import ActionBar from './ActionBar';
|
|
15
15
|
import Files from './Files';
|
|
@@ -37,7 +37,7 @@ const MobileChatInput = memo(() => {
|
|
|
37
37
|
const { isLoading } = useInitAgentConfig();
|
|
38
38
|
|
|
39
39
|
const [loading, value, onInput, onStop] = useChatStore((s) => [
|
|
40
|
-
|
|
40
|
+
operationSelectors.isAgentRuntimeRunning(s),
|
|
41
41
|
s.inputMessage,
|
|
42
42
|
s.updateMessageInput,
|
|
43
43
|
s.stopGenerateMessage,
|
|
@@ -6,7 +6,12 @@ import { useGeminiChineseWarning } from '@/hooks/useGeminiChineseWarning';
|
|
|
6
6
|
import { getAgentStoreState } from '@/store/agent';
|
|
7
7
|
import { agentSelectors } from '@/store/agent/selectors';
|
|
8
8
|
import { useChatStore } from '@/store/chat';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
chatSelectors,
|
|
11
|
+
messageStateSelectors,
|
|
12
|
+
operationSelectors,
|
|
13
|
+
topicSelectors,
|
|
14
|
+
} from '@/store/chat/selectors';
|
|
10
15
|
import { fileChatSelectors, useFileStore } from '@/store/file';
|
|
11
16
|
import { getUserStoreState } from '@/store/user';
|
|
12
17
|
|
|
@@ -34,7 +39,7 @@ export const useSendMessage = () => {
|
|
|
34
39
|
|
|
35
40
|
const send = useCallback(async (params: UseSendMessageParams = {}) => {
|
|
36
41
|
const store = useChatStore.getState();
|
|
37
|
-
if (
|
|
42
|
+
if (operationSelectors.isAgentRuntimeRunning(store)) return;
|
|
38
43
|
|
|
39
44
|
// if uploading file or send button is disabled by message, then we should not send the message
|
|
40
45
|
const isUploadingFiles = fileChatSelectors.isUploadingFiles(useFileStore.getState());
|