@gt-fe/eap-runtime 0.0.10 → 0.0.11
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/README.md +3 -3
- package/dist/api/handlers/invoke.handler.js +4 -0
- package/dist/api/handlers/stream.handler.js +5 -1
- package/dist/api/routes/agent.routes.d.ts +2 -2
- package/dist/common/types.d.ts +5 -5
- package/dist/common/types.js +2 -2
- package/dist/contracts/permission-checker.d.ts +1 -1
- package/dist/contracts/trace-collector.d.ts +1 -1
- package/dist/governance/governance-client.d.ts +1 -1
- package/dist/governance/governance-client.js +40 -16
- package/dist/governance/index.d.ts +2 -2
- package/dist/governance/index.js +1 -1
- package/dist/governance/permission-guard.d.ts +2 -1
- package/dist/governance/permission-guard.js +13 -4
- package/dist/graph/adapters/langgraph.adapter.js +12 -7
- package/dist/graph/contracts/executable-graph.d.ts +8 -6
- package/dist/graph/contracts/executable-graph.js +1 -1
- package/dist/graph/hitl/hitl-handler.d.ts +3 -0
- package/dist/graph/hitl/hitl-handler.js +4 -1
- package/dist/graph/nodes/tool-node.js +27 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/providers/echo-llm-provider.js +2 -2
- package/dist/providers/local-llm-provider.d.ts +1 -1
- package/dist/providers/local-llm-provider.js +1 -1
- package/dist/providers/routing-llm-provider.js +1 -1
- package/dist/registry/agent-models.d.ts +1 -1
- package/dist/registry/agent-models.js +1 -1
- package/dist/registry/agent-registry-client.d.ts +1 -1
- package/dist/registry/deployment-snapshot.d.ts +14 -2
- package/dist/registry/deployment-snapshot.js +57 -31
- package/dist/registry/graph-loader.d.ts +1 -1
- package/dist/registry/graph-loader.js +4 -4
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +1 -1
- package/dist/registry/preload-errors.d.ts +1 -1
- package/dist/registry/preload-errors.js +1 -1
- package/dist/registry/skill-io-guard.js +1 -1
- package/dist/registry/skill-tree.js +1 -1
- package/dist/runtime/agent-runtime.js +19 -5
- package/dist/runtime/dev-runtime.d.ts +1 -1
- package/dist/runtime/dev-runtime.js +6 -10
- package/dist/runtime/types.d.ts +2 -2
- package/dist/server.js +1 -1
- package/dist/sse/events/skill-events.d.ts +1 -1
- package/dist/sse/events/skill-events.js +1 -1
- package/dist/tool/create-tool-executor.d.ts +1 -1
- package/dist/tool/dev-http-executor.d.ts +1 -1
- package/dist/tool/dev-http-executor.js +10 -7
- package/dist/tool/dev-mcp-executor.js +5 -3
- package/dist/tool/dev-tool-executor.d.ts +1 -1
- package/dist/tool/dev-tool-executor.js +2 -2
- package/dist/tool/harness-command.d.ts +32 -0
- package/dist/tool/harness-command.js +276 -0
- package/dist/tool/harness-primitives.d.ts +25 -3
- package/dist/tool/harness-primitives.js +157 -50
- package/dist/tool/http-tool-executor.js +15 -20
- package/dist/tool/index.d.ts +3 -2
- package/dist/tool/index.js +3 -2
- package/dist/tool/local-mcp-client.d.ts +1 -1
- package/dist/tool/local-mcp-client.js +54 -23
- package/dist/tool/local-tool-executor.d.ts +1 -1
- package/dist/tool/local-tool-executor.js +13 -9
- package/dist/tool/mcp-tool-executor.js +14 -6
- package/dist/tool/skill-script-refs.d.ts +20 -0
- package/dist/tool/skill-script-refs.js +62 -0
- package/dist/tool/system-prompt-builder.d.ts +2 -2
- package/dist/tool/system-prompt-builder.js +8 -5
- package/dist/tool/timeout.d.ts +14 -0
- package/dist/tool/timeout.js +36 -0
- package/dist/tool/tool-confirmation.js +2 -1
- package/dist/tool/tool-execution-router.d.ts +1 -1
- package/dist/tool/tool-execution-router.js +1 -1
- package/dist/tool/tool-executor.interface.d.ts +13 -3
- package/dist/tool/tool-orchestrator.d.ts +1 -1
- package/dist/tool/tool-orchestrator.js +6 -3
- package/dist/utils/format-error.d.ts +2 -2
- package/dist/utils/format-error.js +2 -2
- package/dist/utils/http-error.d.ts +2 -2
- package/dist/utils/http-error.js +2 -2
- package/package.json +2 -2
- package/dist/common/concurrency.d.ts +0 -79
- package/dist/common/concurrency.js +0 -121
- package/dist/common/queue-consumer.d.ts +0 -125
- package/dist/common/queue-consumer.js +0 -159
- package/dist/tool/mock-tool-executor.d.ts +0 -28
- package/dist/tool/mock-tool-executor.js +0 -65
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 并发控制模块
|
|
3
|
-
*
|
|
4
|
-
* 提供两种并发限制机制:
|
|
5
|
-
* 1. ConcurrencyLimiter — 基于 amqplib channel.prefetch 的 Agent 级并发控制
|
|
6
|
-
* 通过 RabbitMQ consumer prefetch 限制单 Agent 实例同时处理的任务数
|
|
7
|
-
* 2. Semaphore — 纯内存信号量,用于本地并发控制(不依赖 RabbitMQ)
|
|
8
|
-
*
|
|
9
|
-
* @see Requirement 14 AC2: 系统通过消息消费者 prefetch 参数控制单 Agent 实例的并发上限(默认 prefetch=5)
|
|
10
|
-
*/
|
|
11
|
-
// ─────────────────────────────────────────────
|
|
12
|
-
// ConcurrencyLimiter — RabbitMQ prefetch 并发控制
|
|
13
|
-
// ─────────────────────────────────────────────
|
|
14
|
-
/** ConcurrencyLimiter 默认 prefetch 值 */
|
|
15
|
-
export const DEFAULT_PREFETCH = 5;
|
|
16
|
-
/**
|
|
17
|
-
* 基于 amqplib consumer prefetch 的 Agent 级并发限制器
|
|
18
|
-
*
|
|
19
|
-
* RabbitMQ 的 channel.prefetch(n) 表示该 Channel 上的消费者最多同时
|
|
20
|
-
* 持有 n 条未确认消息。超出限制的消息留在 Queue 中排队等待。
|
|
21
|
-
*
|
|
22
|
-
* 整体 QPS = Pod 数 × prefetch。
|
|
23
|
-
*/
|
|
24
|
-
export class ConcurrencyLimiter {
|
|
25
|
-
prefetchCount;
|
|
26
|
-
/**
|
|
27
|
-
* @param prefetchCount 单 Agent 实例最大并发任务数,默认 5
|
|
28
|
-
*/
|
|
29
|
-
constructor(prefetchCount = DEFAULT_PREFETCH) {
|
|
30
|
-
if (prefetchCount < 1) {
|
|
31
|
-
throw new Error(`prefetchCount must be >= 1, got ${prefetchCount}`);
|
|
32
|
-
}
|
|
33
|
-
this.prefetchCount = prefetchCount;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* 配置 amqplib Channel 的 prefetch
|
|
37
|
-
*
|
|
38
|
-
* 调用后该 Channel 上的消费者最多同时处理 prefetchCount 条消息。
|
|
39
|
-
* 超出限制的消息在 RabbitMQ Queue 中排队等待。
|
|
40
|
-
*
|
|
41
|
-
* @param channel amqplib Channel 实例
|
|
42
|
-
*/
|
|
43
|
-
async configure(channel) {
|
|
44
|
-
await channel.prefetch(this.prefetchCount);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* 获取配置的最大并发数
|
|
48
|
-
*/
|
|
49
|
-
getMaxConcurrency() {
|
|
50
|
-
return this.prefetchCount;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// ─────────────────────────────────────────────
|
|
54
|
-
// Semaphore — 内存级信号量
|
|
55
|
-
// ─────────────────────────────────────────────
|
|
56
|
-
/**
|
|
57
|
-
* 内存信号量 — 用于本地并发控制
|
|
58
|
-
*
|
|
59
|
-
* 当无需依赖 RabbitMQ 时(如单进程内限流、测试场景),
|
|
60
|
-
* 使用 Semaphore 控制并发执行数。
|
|
61
|
-
*
|
|
62
|
-
* 用法:
|
|
63
|
-
* ```ts
|
|
64
|
-
* const sem = new Semaphore(3);
|
|
65
|
-
* await sem.acquire(); // 等待可用槽位
|
|
66
|
-
* try {
|
|
67
|
-
* // 执行并发受限的操作
|
|
68
|
-
* } finally {
|
|
69
|
-
* sem.release(); // 释放槽位
|
|
70
|
-
* }
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export class Semaphore {
|
|
74
|
-
maxSlots;
|
|
75
|
-
currentSlots;
|
|
76
|
-
waitQueue = [];
|
|
77
|
-
/**
|
|
78
|
-
* @param maxConcurrency 最大并发数
|
|
79
|
-
*/
|
|
80
|
-
constructor(maxConcurrency) {
|
|
81
|
-
if (maxConcurrency < 1) {
|
|
82
|
-
throw new Error(`maxConcurrency must be >= 1, got ${maxConcurrency}`);
|
|
83
|
-
}
|
|
84
|
-
this.maxSlots = maxConcurrency;
|
|
85
|
-
this.currentSlots = maxConcurrency;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* 获取一个可用槽位。如果当前无可用槽位,则等待直到有释放。
|
|
89
|
-
*/
|
|
90
|
-
acquire() {
|
|
91
|
-
if (this.currentSlots > 0) {
|
|
92
|
-
this.currentSlots--;
|
|
93
|
-
return Promise.resolve();
|
|
94
|
-
}
|
|
95
|
-
return new Promise((resolve) => {
|
|
96
|
-
this.waitQueue.push(resolve);
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* 释放一个槽位。如果有等待者,唤醒队列中最先等待的。
|
|
101
|
-
*/
|
|
102
|
-
release() {
|
|
103
|
-
if (this.waitQueue.length > 0) {
|
|
104
|
-
const next = this.waitQueue.shift();
|
|
105
|
-
// 使用 microtask 调度,确保调用者的 finally 块先完成
|
|
106
|
-
Promise.resolve().then(next);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
this.currentSlots++;
|
|
110
|
-
if (this.currentSlots > this.maxSlots) {
|
|
111
|
-
this.currentSlots = this.maxSlots;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* 当前可用槽位数
|
|
117
|
-
*/
|
|
118
|
-
get availableSlots() {
|
|
119
|
-
return this.currentSlots;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RabbitMQ 队列消费者
|
|
3
|
-
*
|
|
4
|
-
* 从 RabbitMQ 队列中消费任务消息,分发给 Graph 引擎处理。
|
|
5
|
-
* 通过 channel.prefetch(n) 控制并发:
|
|
6
|
-
* - 最多同时处理 n 条未 ack 的消息
|
|
7
|
-
* - 超出 prefetch 限制的消息留在 RabbitMQ Queue 中排队等待
|
|
8
|
-
* - 不丢弃任何请求
|
|
9
|
-
*
|
|
10
|
-
* 整体 QPS = Pod 数 × prefetch
|
|
11
|
-
*
|
|
12
|
-
* @see Requirement 14 AC2: 系统通过消息消费者 prefetch 参数控制单 Agent 实例的并发上限
|
|
13
|
-
* @see Requirement 14 AC3: 并发请求数超过 prefetch 限制时,系统将超出请求放入 Queue 排队等待,不丢弃请求
|
|
14
|
-
*/
|
|
15
|
-
import type { Connection } from 'amqplib';
|
|
16
|
-
/**
|
|
17
|
-
* 任务消息体 — 从 RabbitMQ 队列中消费到的任务负载
|
|
18
|
-
*/
|
|
19
|
-
export interface TaskMessage {
|
|
20
|
-
/** 任务唯一标识 */
|
|
21
|
-
taskId: string;
|
|
22
|
-
/** 目标 Agent(值为 agentCode;字段名保留 agentId 兼容存量消息) */
|
|
23
|
-
agentId: string;
|
|
24
|
-
/** 会话 ID */
|
|
25
|
-
sessionId: string;
|
|
26
|
-
/** 发起请求的用户 ID */
|
|
27
|
-
userId: string;
|
|
28
|
-
/** 用户输入内容 */
|
|
29
|
-
input: string;
|
|
30
|
-
/** 可选的执行配置 */
|
|
31
|
-
config?: Record<string, unknown>;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* 任务处理器 — 接收解析后的 TaskMessage,分发给 Graph 引擎执行
|
|
35
|
-
*
|
|
36
|
-
* 处理成功则 resolve,失败则 reject(消费者据此决定 ack/nack)。
|
|
37
|
-
*/
|
|
38
|
-
export type TaskHandler = (message: TaskMessage) => Promise<void>;
|
|
39
|
-
/**
|
|
40
|
-
* QueueConsumer 配置选项
|
|
41
|
-
*/
|
|
42
|
-
export interface QueueConsumerOptions {
|
|
43
|
-
/** 队列名称 */
|
|
44
|
-
queueName: string;
|
|
45
|
-
/** prefetch 数量 — 单实例最大并发消息处理数,默认 5 */
|
|
46
|
-
prefetch?: number;
|
|
47
|
-
/** 队列是否持久化,默认 true */
|
|
48
|
-
durable?: boolean;
|
|
49
|
-
/** 是否自动确认(不推荐生产使用),默认 false */
|
|
50
|
-
noAck?: boolean;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* RabbitMQ 队列消费者
|
|
54
|
-
*
|
|
55
|
-
* 核心设计:利用 RabbitMQ channel.prefetch 机制实现队列式溢出控制。
|
|
56
|
-
* - consumer.prefetch(5) → 该 Channel 上最多持有 5 条未 ack 消息
|
|
57
|
-
* - 第 6 条及后续消息自动留在 RabbitMQ Queue 中排队
|
|
58
|
-
* - 只有当某条消息被 ack/nack 后,RabbitMQ 才推送下一条
|
|
59
|
-
* - 保证不丢弃任何请求(queue-based overflow)
|
|
60
|
-
*
|
|
61
|
-
* 使用方式:
|
|
62
|
-
* ```ts
|
|
63
|
-
* const consumer = new QueueConsumer(connection, {
|
|
64
|
-
* queueName: 'agent.tasks.ai-bi',
|
|
65
|
-
* prefetch: 5,
|
|
66
|
-
* }, async (msg) => {
|
|
67
|
-
* await graphEngine.execute(msg);
|
|
68
|
-
* });
|
|
69
|
-
*
|
|
70
|
-
* await consumer.start();
|
|
71
|
-
* // ... 优雅关闭
|
|
72
|
-
* await consumer.stop();
|
|
73
|
-
* ```
|
|
74
|
-
*/
|
|
75
|
-
export declare class QueueConsumer {
|
|
76
|
-
private readonly connection;
|
|
77
|
-
private readonly options;
|
|
78
|
-
private readonly taskHandler;
|
|
79
|
-
private channel;
|
|
80
|
-
private consumerTag;
|
|
81
|
-
private started;
|
|
82
|
-
/**
|
|
83
|
-
* @param connection amqplib Connection 实例
|
|
84
|
-
* @param options 消费者配置
|
|
85
|
-
* @param taskHandler 任务处理函数 — 接收解析后的消息,分发给 Graph 引擎
|
|
86
|
-
*/
|
|
87
|
-
constructor(connection: Connection, options: QueueConsumerOptions, taskHandler: TaskHandler);
|
|
88
|
-
/**
|
|
89
|
-
* 启动消费者
|
|
90
|
-
*
|
|
91
|
-
* 1. 创建 Channel
|
|
92
|
-
* 2. 设置 prefetch(限制并发)
|
|
93
|
-
* 3. 声明队列(确保队列存在)
|
|
94
|
-
* 4. 开始消费消息
|
|
95
|
-
*
|
|
96
|
-
* prefetch 机制保证:超出限制的消息留在 RabbitMQ Queue 排队,不丢弃。
|
|
97
|
-
*/
|
|
98
|
-
start(): Promise<void>;
|
|
99
|
-
/**
|
|
100
|
-
* 停止消费者并优雅关闭 Channel
|
|
101
|
-
*
|
|
102
|
-
* 1. 取消消费(不再接收新消息)
|
|
103
|
-
* 2. 关闭 Channel
|
|
104
|
-
*
|
|
105
|
-
* 已在处理中的消息不受影响,会正常完成后 ack/nack。
|
|
106
|
-
*/
|
|
107
|
-
stop(): Promise<void>;
|
|
108
|
-
/**
|
|
109
|
-
* 是否正在运行
|
|
110
|
-
*/
|
|
111
|
-
get isRunning(): boolean;
|
|
112
|
-
/**
|
|
113
|
-
* 获取配置的 prefetch 值
|
|
114
|
-
*/
|
|
115
|
-
get prefetch(): number;
|
|
116
|
-
/**
|
|
117
|
-
* 处理单条消息
|
|
118
|
-
*
|
|
119
|
-
* - 解析消息体为 TaskMessage
|
|
120
|
-
* - 调用 taskHandler 执行任务
|
|
121
|
-
* - 成功 → ack(释放 prefetch 槽位,RabbitMQ 推送下一条)
|
|
122
|
-
* - 失败 → nack + requeue(消息重新入队,稍后重试)
|
|
123
|
-
*/
|
|
124
|
-
private handleMessage;
|
|
125
|
-
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RabbitMQ 队列消费者
|
|
3
|
-
*
|
|
4
|
-
* 从 RabbitMQ 队列中消费任务消息,分发给 Graph 引擎处理。
|
|
5
|
-
* 通过 channel.prefetch(n) 控制并发:
|
|
6
|
-
* - 最多同时处理 n 条未 ack 的消息
|
|
7
|
-
* - 超出 prefetch 限制的消息留在 RabbitMQ Queue 中排队等待
|
|
8
|
-
* - 不丢弃任何请求
|
|
9
|
-
*
|
|
10
|
-
* 整体 QPS = Pod 数 × prefetch
|
|
11
|
-
*
|
|
12
|
-
* @see Requirement 14 AC2: 系统通过消息消费者 prefetch 参数控制单 Agent 实例的并发上限
|
|
13
|
-
* @see Requirement 14 AC3: 并发请求数超过 prefetch 限制时,系统将超出请求放入 Queue 排队等待,不丢弃请求
|
|
14
|
-
*/
|
|
15
|
-
// ─────────────────────────────────────────────
|
|
16
|
-
// QueueConsumer
|
|
17
|
-
// ─────────────────────────────────────────────
|
|
18
|
-
/** 默认 prefetch 值 */
|
|
19
|
-
const DEFAULT_PREFETCH = 5;
|
|
20
|
-
/**
|
|
21
|
-
* RabbitMQ 队列消费者
|
|
22
|
-
*
|
|
23
|
-
* 核心设计:利用 RabbitMQ channel.prefetch 机制实现队列式溢出控制。
|
|
24
|
-
* - consumer.prefetch(5) → 该 Channel 上最多持有 5 条未 ack 消息
|
|
25
|
-
* - 第 6 条及后续消息自动留在 RabbitMQ Queue 中排队
|
|
26
|
-
* - 只有当某条消息被 ack/nack 后,RabbitMQ 才推送下一条
|
|
27
|
-
* - 保证不丢弃任何请求(queue-based overflow)
|
|
28
|
-
*
|
|
29
|
-
* 使用方式:
|
|
30
|
-
* ```ts
|
|
31
|
-
* const consumer = new QueueConsumer(connection, {
|
|
32
|
-
* queueName: 'agent.tasks.ai-bi',
|
|
33
|
-
* prefetch: 5,
|
|
34
|
-
* }, async (msg) => {
|
|
35
|
-
* await graphEngine.execute(msg);
|
|
36
|
-
* });
|
|
37
|
-
*
|
|
38
|
-
* await consumer.start();
|
|
39
|
-
* // ... 优雅关闭
|
|
40
|
-
* await consumer.stop();
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
export class QueueConsumer {
|
|
44
|
-
connection;
|
|
45
|
-
options;
|
|
46
|
-
taskHandler;
|
|
47
|
-
channel = null;
|
|
48
|
-
consumerTag = null;
|
|
49
|
-
started = false;
|
|
50
|
-
/**
|
|
51
|
-
* @param connection amqplib Connection 实例
|
|
52
|
-
* @param options 消费者配置
|
|
53
|
-
* @param taskHandler 任务处理函数 — 接收解析后的消息,分发给 Graph 引擎
|
|
54
|
-
*/
|
|
55
|
-
constructor(connection, options, taskHandler) {
|
|
56
|
-
this.connection = connection;
|
|
57
|
-
this.options = {
|
|
58
|
-
queueName: options.queueName,
|
|
59
|
-
prefetch: options.prefetch ?? DEFAULT_PREFETCH,
|
|
60
|
-
durable: options.durable ?? true,
|
|
61
|
-
noAck: options.noAck ?? false,
|
|
62
|
-
};
|
|
63
|
-
this.taskHandler = taskHandler;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* 启动消费者
|
|
67
|
-
*
|
|
68
|
-
* 1. 创建 Channel
|
|
69
|
-
* 2. 设置 prefetch(限制并发)
|
|
70
|
-
* 3. 声明队列(确保队列存在)
|
|
71
|
-
* 4. 开始消费消息
|
|
72
|
-
*
|
|
73
|
-
* prefetch 机制保证:超出限制的消息留在 RabbitMQ Queue 排队,不丢弃。
|
|
74
|
-
*/
|
|
75
|
-
async start() {
|
|
76
|
-
if (this.started) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
// 1. 创建 Channel
|
|
80
|
-
this.channel = await this.connection.createChannel();
|
|
81
|
-
// 2. 设置 prefetch — 核心并发控制
|
|
82
|
-
// 超出 prefetch 的消息留在 Queue 中排队等待(queue-based overflow)
|
|
83
|
-
await this.channel.prefetch(this.options.prefetch);
|
|
84
|
-
// 3. 声明队列(幂等操作,队列已存在则不做修改)
|
|
85
|
-
await this.channel.assertQueue(this.options.queueName, {
|
|
86
|
-
durable: this.options.durable,
|
|
87
|
-
});
|
|
88
|
-
// 4. 开始消费
|
|
89
|
-
const { consumerTag } = await this.channel.consume(this.options.queueName, (msg) => this.handleMessage(msg), { noAck: this.options.noAck });
|
|
90
|
-
this.consumerTag = consumerTag;
|
|
91
|
-
this.started = true;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* 停止消费者并优雅关闭 Channel
|
|
95
|
-
*
|
|
96
|
-
* 1. 取消消费(不再接收新消息)
|
|
97
|
-
* 2. 关闭 Channel
|
|
98
|
-
*
|
|
99
|
-
* 已在处理中的消息不受影响,会正常完成后 ack/nack。
|
|
100
|
-
*/
|
|
101
|
-
async stop() {
|
|
102
|
-
if (!this.started || !this.channel) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
// 取消消费者
|
|
106
|
-
if (this.consumerTag) {
|
|
107
|
-
await this.channel.cancel(this.consumerTag);
|
|
108
|
-
this.consumerTag = null;
|
|
109
|
-
}
|
|
110
|
-
// 关闭 Channel
|
|
111
|
-
await this.channel.close();
|
|
112
|
-
this.channel = null;
|
|
113
|
-
this.started = false;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* 是否正在运行
|
|
117
|
-
*/
|
|
118
|
-
get isRunning() {
|
|
119
|
-
return this.started;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* 获取配置的 prefetch 值
|
|
123
|
-
*/
|
|
124
|
-
get prefetch() {
|
|
125
|
-
return this.options.prefetch;
|
|
126
|
-
}
|
|
127
|
-
// ─── 内部方法 ───────────────────────────────
|
|
128
|
-
/**
|
|
129
|
-
* 处理单条消息
|
|
130
|
-
*
|
|
131
|
-
* - 解析消息体为 TaskMessage
|
|
132
|
-
* - 调用 taskHandler 执行任务
|
|
133
|
-
* - 成功 → ack(释放 prefetch 槽位,RabbitMQ 推送下一条)
|
|
134
|
-
* - 失败 → nack + requeue(消息重新入队,稍后重试)
|
|
135
|
-
*/
|
|
136
|
-
async handleMessage(msg) {
|
|
137
|
-
// RabbitMQ 在 Channel 关闭时会发送 null
|
|
138
|
-
if (!msg || !this.channel) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
try {
|
|
142
|
-
// 解析消息体
|
|
143
|
-
const body = msg.content.toString('utf-8');
|
|
144
|
-
const taskMessage = JSON.parse(body);
|
|
145
|
-
// 分发给 Graph 引擎处理
|
|
146
|
-
await this.taskHandler(taskMessage);
|
|
147
|
-
// 处理成功 → ack,释放 prefetch 槽位
|
|
148
|
-
if (!this.options.noAck) {
|
|
149
|
-
this.channel.ack(msg);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
// 处理失败 → nack,requeue=true 让消息重新排队
|
|
154
|
-
if (!this.options.noAck && this.channel) {
|
|
155
|
-
this.channel.nack(msg, false, true);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MockToolExecutor — 本地开发模式的模拟 Tool 执行器
|
|
3
|
-
*
|
|
4
|
-
* 定位:`eap dev --tool-mode mock` 场景下替代真实 Gateway/Local 执行器,
|
|
5
|
-
* 让 Agent 开发者在无 foundation-service、无真实 Tool 依赖的前提下跑通 Graph。
|
|
6
|
-
*
|
|
7
|
-
* 响应查找顺序:
|
|
8
|
-
* 1. `${toolsDir}/<toolName>/mock.json`(用户自定义 Mock 响应)
|
|
9
|
-
* 2. `${toolsDir}/<toolName>/tool.manifest.json` 内嵌 `mockResponse` 字段
|
|
10
|
-
* 3. 兜底 echo:`{ toolName, arguments, note: "mock" }`
|
|
11
|
-
*
|
|
12
|
-
* 使用示例:
|
|
13
|
-
* const executor = new MockToolExecutor({ toolsDir: './tools' });
|
|
14
|
-
*/
|
|
15
|
-
import type { IToolExecutor, ToolCallRequest, ToolCallResult } from './tool-executor.interface.js';
|
|
16
|
-
export interface MockToolExecutorOptions {
|
|
17
|
-
/** 本地 tools 根目录,默认 `./tools` 或读 `EAP_LOCAL_TOOLS_DIR` */
|
|
18
|
-
toolsDir?: string;
|
|
19
|
-
/** Mock 响应可自定义 delay,用于验证 SSE/流式表现 */
|
|
20
|
-
delayMs?: number;
|
|
21
|
-
}
|
|
22
|
-
export declare class MockToolExecutor implements IToolExecutor {
|
|
23
|
-
private readonly toolsDir;
|
|
24
|
-
private readonly delayMs;
|
|
25
|
-
constructor(options?: MockToolExecutorOptions);
|
|
26
|
-
execute(request: ToolCallRequest): Promise<ToolCallResult>;
|
|
27
|
-
private resolveMockResponse;
|
|
28
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MockToolExecutor — 本地开发模式的模拟 Tool 执行器
|
|
3
|
-
*
|
|
4
|
-
* 定位:`eap dev --tool-mode mock` 场景下替代真实 Gateway/Local 执行器,
|
|
5
|
-
* 让 Agent 开发者在无 foundation-service、无真实 Tool 依赖的前提下跑通 Graph。
|
|
6
|
-
*
|
|
7
|
-
* 响应查找顺序:
|
|
8
|
-
* 1. `${toolsDir}/<toolName>/mock.json`(用户自定义 Mock 响应)
|
|
9
|
-
* 2. `${toolsDir}/<toolName>/tool.manifest.json` 内嵌 `mockResponse` 字段
|
|
10
|
-
* 3. 兜底 echo:`{ toolName, arguments, note: "mock" }`
|
|
11
|
-
*
|
|
12
|
-
* 使用示例:
|
|
13
|
-
* const executor = new MockToolExecutor({ toolsDir: './tools' });
|
|
14
|
-
*/
|
|
15
|
-
import { readFile } from 'node:fs/promises';
|
|
16
|
-
import path from 'node:path';
|
|
17
|
-
async function readJsonFile(filePath) {
|
|
18
|
-
try {
|
|
19
|
-
const raw = await readFile(filePath, 'utf-8');
|
|
20
|
-
return JSON.parse(raw);
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return undefined;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
export class MockToolExecutor {
|
|
27
|
-
toolsDir;
|
|
28
|
-
delayMs;
|
|
29
|
-
constructor(options = {}) {
|
|
30
|
-
this.toolsDir =
|
|
31
|
-
options.toolsDir ?? process.env.EAP_LOCAL_TOOLS_DIR ?? './tools';
|
|
32
|
-
this.delayMs = options.delayMs ?? 0;
|
|
33
|
-
}
|
|
34
|
-
async execute(request) {
|
|
35
|
-
if (this.delayMs > 0) {
|
|
36
|
-
await new Promise((r) => setTimeout(r, this.delayMs));
|
|
37
|
-
}
|
|
38
|
-
const mockResponse = await this.resolveMockResponse(request.toolName);
|
|
39
|
-
const payload = mockResponse ?? {
|
|
40
|
-
toolName: request.toolName,
|
|
41
|
-
arguments: request.arguments,
|
|
42
|
-
note: 'mock: no explicit mockResponse found, echoing arguments',
|
|
43
|
-
};
|
|
44
|
-
return {
|
|
45
|
-
toolCallId: request.toolCallId,
|
|
46
|
-
toolName: request.toolName,
|
|
47
|
-
content: typeof payload === 'string' ? payload : JSON.stringify(payload),
|
|
48
|
-
success: true,
|
|
49
|
-
runtime: 'mock',
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
async resolveMockResponse(toolName) {
|
|
53
|
-
const toolDir = path.join(this.toolsDir, toolName);
|
|
54
|
-
// 优先级 1:独立 mock.json
|
|
55
|
-
const explicit = await readJsonFile(path.join(toolDir, 'mock.json'));
|
|
56
|
-
if (explicit !== undefined)
|
|
57
|
-
return explicit;
|
|
58
|
-
// 优先级 2:manifest 内嵌 mockResponse
|
|
59
|
-
const manifest = (await readJsonFile(path.join(toolDir, 'tool.manifest.json')));
|
|
60
|
-
if (manifest && manifest.mockResponse !== undefined) {
|
|
61
|
-
return manifest.mockResponse;
|
|
62
|
-
}
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
65
|
-
}
|