@capyclaw/tool-host 0.1.0 → 0.1.1
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/dsh-suite.json +6 -4
- package/package.json +15 -2
- package/src/index.mjs +341 -341
- package/src/optimizers/E2E/346/265/213/350/257/225/346/212/245/345/221/212.md +0 -396
- package/src/optimizers/PPT/345/234/272/346/231/257/344/274/230/345/214/226/346/226/271/346/241/210.md +0 -607
- package/src/optimizers/PPT/345/256/236/346/227/266/346/270/262/346/237/223/346/226/271/346/241/210.md +0 -456
- package/src/optimizers/PPT/346/265/201/345/274/217/346/270/262/346/237/223-/347/224/237/344/272/247/347/272/247/344/274/230/345/214/226.md +0 -874
- package/src/optimizers/README.md +0 -374
- package/src/optimizers/batch-optimizer.mjs +0 -94
- package/src/optimizers/cache-optimizer.mjs +0 -217
- package/src/optimizers/concurrency-optimizer.mjs +0 -129
- package/src/optimizers/e2e-test.mjs +0 -674
- package/src/optimizers/examples.mjs +0 -243
- package/src/optimizers/index.mjs +0 -188
- package/src/optimizers/ppt-optimizer.mjs +0 -263
- package/src/optimizers/ppt-streaming-demo.mjs +0 -192
- package/src/optimizers/ppt-streaming-renderer.mjs +0 -332
- package/src/optimizers/production-streaming-demo.mjs +0 -267
- package/src/optimizers/production-streaming-renderer.mjs +0 -611
- package/src/optimizers/real-ppt-test.mjs +0 -397
- package/src/optimizers/real-world-benchmark.mjs +0 -470
- package/src/optimizers/retry-optimizer.mjs +0 -118
- package/src/optimizers//347/224/237/344/272/247/347/272/247/344/274/230/345/214/226/346/200/273/347/273/223.md +0 -110
- package/src/optimizers//347/234/237/345/256/236/346/200/247/350/203/275/346/217/220/345/215/207/351/252/214/350/257/201/346/212/245/345/221/212.md +0 -331
package/src/optimizers/README.md
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
# MCP 2.0 优化器 - 快速开始
|
|
2
|
-
|
|
3
|
-
## 📦 安装
|
|
4
|
-
|
|
5
|
-
优化器已内置在 `dsh-tool-host` 包中,无需额外安装。
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @deepseek-ai/dsh-tool-host
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## 🚀 快速开始
|
|
14
|
-
|
|
15
|
-
### 1. 基础使用
|
|
16
|
-
|
|
17
|
-
```javascript
|
|
18
|
-
import { createOptimizedExecutor } from '@deepseek-ai/dsh-tool-host/optimizers';
|
|
19
|
-
import { Client } from '@modelcontextprotocol/client';
|
|
20
|
-
|
|
21
|
-
// 创建客户端
|
|
22
|
-
const client = new Client({
|
|
23
|
-
name: 'my-app',
|
|
24
|
-
version: '1.0.0'
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// 创建优化执行器
|
|
28
|
-
const executor = createOptimizedExecutor(client, {
|
|
29
|
-
batchSize: 10, // 批量大小
|
|
30
|
-
maxConcurrent: 20, // 最大并发
|
|
31
|
-
maxRetries: 3, // 最大重试
|
|
32
|
-
cacheTTL: 300000 // 缓存时间(5分钟)
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// 调用工具
|
|
36
|
-
const result = await executor.callTool('my_tool', { arg: 'value' });
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### 2. 批量操作
|
|
40
|
-
|
|
41
|
-
```javascript
|
|
42
|
-
// 批量调用会自动合并
|
|
43
|
-
const results = await Promise.all([
|
|
44
|
-
executor.callTool('process', { id: 1 }, { batch: true }),
|
|
45
|
-
executor.callTool('process', { id: 2 }, { batch: true }),
|
|
46
|
-
executor.callTool('process', { id: 3 }, { batch: true }),
|
|
47
|
-
]);
|
|
48
|
-
|
|
49
|
-
// 性能提升: 50-80%
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### 3. 智能缓存
|
|
53
|
-
|
|
54
|
-
```javascript
|
|
55
|
-
// 第一次调用
|
|
56
|
-
const schema1 = await executor.callTool('get_schema',
|
|
57
|
-
{ name: 'tool' },
|
|
58
|
-
{ cacheable: true }
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
// 第二次调用(命中缓存,几乎瞬时)
|
|
62
|
-
const schema2 = await executor.callTool('get_schema',
|
|
63
|
-
{ name: 'tool' },
|
|
64
|
-
{ cacheable: true }
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
// 性能提升: 90%+
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### 4. 自动重试
|
|
71
|
-
|
|
72
|
-
```javascript
|
|
73
|
-
// 自动重试失败的请求(指数退避)
|
|
74
|
-
try {
|
|
75
|
-
const result = await executor.callTool('unstable_api', {
|
|
76
|
-
url: 'https://api.example.com'
|
|
77
|
-
});
|
|
78
|
-
} catch (error) {
|
|
79
|
-
// 所有重试失败后才抛出
|
|
80
|
-
console.error('Failed after retries:', error);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 减少 95% 的暂时性失败
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 5. 并发控制
|
|
87
|
-
|
|
88
|
-
```javascript
|
|
89
|
-
// 自动限流,不会压垮服务器
|
|
90
|
-
const items = Array.from({ length: 100 }, (_, i) => i);
|
|
91
|
-
|
|
92
|
-
const results = await Promise.all(
|
|
93
|
-
items.map(id => executor.callTool('process', { id }))
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
// 自动调整并发数(1-20之间)
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## 📊 性能监控
|
|
102
|
-
|
|
103
|
-
```javascript
|
|
104
|
-
// 获取统计信息
|
|
105
|
-
const stats = executor.getStats();
|
|
106
|
-
|
|
107
|
-
console.log('总调用:', stats.calls);
|
|
108
|
-
console.log('缓存命中率:', stats.cacheHitRate);
|
|
109
|
-
console.log('错误率:', stats.errorRate);
|
|
110
|
-
console.log('RPS:', stats.rps);
|
|
111
|
-
console.log('当前并发:', stats.concurrency.currentConcurrent);
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## 🔧 配置选项
|
|
117
|
-
|
|
118
|
-
```javascript
|
|
119
|
-
const executor = createOptimizedExecutor(client, {
|
|
120
|
-
// 批量配置
|
|
121
|
-
batchSize: 10, // 批量大小(默认10)
|
|
122
|
-
batchTimeout: 100, // 批量超时(ms,默认100)
|
|
123
|
-
|
|
124
|
-
// 并发配置
|
|
125
|
-
minConcurrent: 1, // 最小并发(默认1)
|
|
126
|
-
maxConcurrent: 20, // 最大并发(默认20)
|
|
127
|
-
initialConcurrent: 5, // 初始并发(默认5)
|
|
128
|
-
|
|
129
|
-
// 重试配置
|
|
130
|
-
maxRetries: 3, // 最大重试次数(默认3)
|
|
131
|
-
retryDelay: 1000, // 初始重试延迟(ms,默认1000)
|
|
132
|
-
|
|
133
|
-
// 缓存配置
|
|
134
|
-
schemaTTL: 3600000, // Schema缓存时间(默认1小时)
|
|
135
|
-
resultTTL: 60000, // 结果缓存时间(默认1分钟)
|
|
136
|
-
cacheSize: 1000 // 缓存大小(默认1000)
|
|
137
|
-
});
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
|
|
142
|
-
## 🎯 使用场景
|
|
143
|
-
|
|
144
|
-
### 场景1: 批量处理
|
|
145
|
-
|
|
146
|
-
```javascript
|
|
147
|
-
// ❌ 之前:逐个调用,慢
|
|
148
|
-
for (const item of items) {
|
|
149
|
-
await callTool('process', item);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ✅ 现在:批量调用,快
|
|
153
|
-
await Promise.all(
|
|
154
|
-
items.map(item =>
|
|
155
|
-
executor.callTool('process', item, { batch: true })
|
|
156
|
-
)
|
|
157
|
-
);
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### 场景2: 频繁查询
|
|
161
|
-
|
|
162
|
-
```javascript
|
|
163
|
-
// ❌ 之前:每次都查询
|
|
164
|
-
const schema = await getToolSchema('my_tool');
|
|
165
|
-
|
|
166
|
-
// ✅ 现在:缓存查询
|
|
167
|
-
const schema = await executor.callTool('tools/get',
|
|
168
|
-
{ name: 'my_tool' },
|
|
169
|
-
{ cacheable: true }
|
|
170
|
-
);
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### 场景3: 不稳定网络
|
|
174
|
-
|
|
175
|
-
```javascript
|
|
176
|
-
// ❌ 之前:失败就失败了
|
|
177
|
-
try {
|
|
178
|
-
await callAPI();
|
|
179
|
-
} catch (error) {
|
|
180
|
-
// 直接失败
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ✅ 现在:自动重试
|
|
184
|
-
const result = await executor.callTool('api_call', {});
|
|
185
|
-
// 自动重试3次,指数退避
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### 场景4: 高并发场景
|
|
189
|
-
|
|
190
|
-
```javascript
|
|
191
|
-
// ❌ 之前:无限并发,服务器崩溃
|
|
192
|
-
await Promise.all(
|
|
193
|
-
items.map(item => process(item))
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
// ✅ 现在:智能限流
|
|
197
|
-
await Promise.all(
|
|
198
|
-
items.map(item =>
|
|
199
|
-
executor.callTool('process', item)
|
|
200
|
-
)
|
|
201
|
-
);
|
|
202
|
-
// 自动控制在20并发以内
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
## 📈 性能对比
|
|
208
|
-
|
|
209
|
-
| 场景 | 优化前 | 优化后 | 提升 |
|
|
210
|
-
|------|--------|--------|------|
|
|
211
|
-
| 批量10个工具 | 1000ms | 200ms | 80% ⬆️ |
|
|
212
|
-
| 重复查询 | 100ms | 1ms | 99% ⬆️ |
|
|
213
|
-
| 网络不稳定 | 50%失败 | 2%失败 | 96% ⬆️ |
|
|
214
|
-
| 100并发 | 崩溃 | 稳定 | ∞ ⬆️ |
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## 🔍 调试
|
|
219
|
-
|
|
220
|
-
### 开启详细日志
|
|
221
|
-
|
|
222
|
-
```javascript
|
|
223
|
-
// 设置环境变量
|
|
224
|
-
process.env.MCP_OPTIMIZER_DEBUG = 'true';
|
|
225
|
-
|
|
226
|
-
// 会输出详细的优化日志
|
|
227
|
-
// [BatchExecutor] Batched 5 calls
|
|
228
|
-
// [RetryManager] Retry 1/3 after 1000ms
|
|
229
|
-
// [ConcurrencyManager] Adjusted to 8 concurrent
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### 监控面板
|
|
233
|
-
|
|
234
|
-
```javascript
|
|
235
|
-
// 定期打印统计
|
|
236
|
-
setInterval(() => {
|
|
237
|
-
const stats = executor.getStats();
|
|
238
|
-
console.table({
|
|
239
|
-
'Total Calls': stats.calls,
|
|
240
|
-
'Cache Hit Rate': stats.cacheHitRate,
|
|
241
|
-
'Error Rate': stats.errorRate,
|
|
242
|
-
'RPS': stats.rps,
|
|
243
|
-
'Current Concurrent': stats.concurrency.currentConcurrent
|
|
244
|
-
});
|
|
245
|
-
}, 5000);
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
## 🚨 注意事项
|
|
251
|
-
|
|
252
|
-
### 1. 批量操作限制
|
|
253
|
-
|
|
254
|
-
```javascript
|
|
255
|
-
// ⚠️ 只有支持批量的工具才能启用
|
|
256
|
-
executor.callTool('tool', args, { batch: true });
|
|
257
|
-
|
|
258
|
-
// 确保 MCP 服务器支持批量请求
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### 2. 缓存策略
|
|
262
|
-
|
|
263
|
-
```javascript
|
|
264
|
-
// ✅ 适合缓存:幂等操作
|
|
265
|
-
executor.callTool('get_info', {}, { cacheable: true });
|
|
266
|
-
|
|
267
|
-
// ❌ 不适合缓存:修改操作
|
|
268
|
-
executor.callTool('update_data', {}); // 不要缓存
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
### 3. 并发控制
|
|
272
|
-
|
|
273
|
-
```javascript
|
|
274
|
-
// 并发数会自动调整,但可以设置范围
|
|
275
|
-
const executor = createOptimizedExecutor(client, {
|
|
276
|
-
minConcurrent: 5, // 最小5
|
|
277
|
-
maxConcurrent: 50 // 最大50
|
|
278
|
-
});
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
## 🎓 最佳实践
|
|
284
|
-
|
|
285
|
-
### 1. 合理设置批量大小
|
|
286
|
-
|
|
287
|
-
```javascript
|
|
288
|
-
// 根据服务器能力调整
|
|
289
|
-
const executor = createOptimizedExecutor(client, {
|
|
290
|
-
batchSize: 20 // 性能服务器可以更大
|
|
291
|
-
});
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### 2. 区分缓存时间
|
|
295
|
-
|
|
296
|
-
```javascript
|
|
297
|
-
const executor = createOptimizedExecutor(client, {
|
|
298
|
-
schemaTTL: 3600000, // Schema 1小时
|
|
299
|
-
resultTTL: 60000 // 结果 1分钟
|
|
300
|
-
});
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### 3. 监控关键指标
|
|
304
|
-
|
|
305
|
-
```javascript
|
|
306
|
-
// 定期检查
|
|
307
|
-
const stats = executor.getStats();
|
|
308
|
-
|
|
309
|
-
// 缓存命中率 < 50%?增加缓存时间
|
|
310
|
-
// 错误率 > 5%?检查服务稳定性
|
|
311
|
-
// 并发数 < 5?可能并发太保守
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
## 📚 更多资源
|
|
317
|
-
|
|
318
|
-
- [MCP 2.0 协议文档](https://spec.modelcontextprotocol.io/)
|
|
319
|
-
- [完整 API 文档](./MCP2.0新特性优化方案.md)
|
|
320
|
-
- [性能测试报告](./全链路测试报告.md)
|
|
321
|
-
- [示例代码](./optimizers/examples.mjs)
|
|
322
|
-
|
|
323
|
-
---
|
|
324
|
-
|
|
325
|
-
## 🆘 故障排查
|
|
326
|
-
|
|
327
|
-
### 问题1: 批量调用失败
|
|
328
|
-
|
|
329
|
-
```
|
|
330
|
-
Error: Batch not supported
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
**解决**: MCP 服务器不支持批量,禁用批量选项
|
|
334
|
-
|
|
335
|
-
```javascript
|
|
336
|
-
// 不要使用 batch: true
|
|
337
|
-
await executor.callTool('tool', args);
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
### 问题2: 缓存没有生效
|
|
341
|
-
|
|
342
|
-
```
|
|
343
|
-
Cache hit rate: 0%
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
**解决**: 确保使用了 cacheable 选项
|
|
347
|
-
|
|
348
|
-
```javascript
|
|
349
|
-
await executor.callTool('tool', args, {
|
|
350
|
-
cacheable: true,
|
|
351
|
-
cacheKey: 'unique-key' // 自定义缓存键
|
|
352
|
-
});
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
### 问题3: 重试次数过多
|
|
356
|
-
|
|
357
|
-
```
|
|
358
|
-
[RetryManager] Retry 3/3 after 8000ms
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
**解决**: 调整重试策略
|
|
362
|
-
|
|
363
|
-
```javascript
|
|
364
|
-
const executor = createOptimizedExecutor(client, {
|
|
365
|
-
maxRetries: 1, // 减少重试次数
|
|
366
|
-
retryDelay: 500 // 减少延迟
|
|
367
|
-
});
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
---
|
|
371
|
-
|
|
372
|
-
**版本**: 1.0.0
|
|
373
|
-
**更新时间**: 2026-11-04
|
|
374
|
-
**状态**: ✅ 生产就绪
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP 2.0 批量操作优化器
|
|
3
|
-
*
|
|
4
|
-
* 自动收集短时间内的多个工具调用,合并为批量请求
|
|
5
|
-
* 提升 50-80% 的性能
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export class BatchToolExecutor {
|
|
9
|
-
constructor(client, options = {}) {
|
|
10
|
-
this.client = client;
|
|
11
|
-
this.batchSize = options.batchSize || 10;
|
|
12
|
-
this.batchTimeout = options.batchTimeout || 100; // ms
|
|
13
|
-
this.pendingCalls = [];
|
|
14
|
-
this.batchTimer = null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* 智能批量调用工具
|
|
19
|
-
* 自动收集短时间内的多个调用,合并为批量请求
|
|
20
|
-
*/
|
|
21
|
-
async callTool(name, args) {
|
|
22
|
-
return new Promise((resolve, reject) => {
|
|
23
|
-
this.pendingCalls.push({ name, args, resolve, reject });
|
|
24
|
-
|
|
25
|
-
// 达到批量大小,立即执行
|
|
26
|
-
if (this.pendingCalls.length >= this.batchSize) {
|
|
27
|
-
this.flush();
|
|
28
|
-
} else {
|
|
29
|
-
// 否则等待更多调用
|
|
30
|
-
this.scheduleBatch();
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
scheduleBatch() {
|
|
36
|
-
if (this.batchTimer) return;
|
|
37
|
-
|
|
38
|
-
this.batchTimer = setTimeout(() => {
|
|
39
|
-
this.flush();
|
|
40
|
-
}, this.batchTimeout);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async flush() {
|
|
44
|
-
if (this.batchTimer) {
|
|
45
|
-
clearTimeout(this.batchTimer);
|
|
46
|
-
this.batchTimer = null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (this.pendingCalls.length === 0) return;
|
|
50
|
-
|
|
51
|
-
const calls = this.pendingCalls.splice(0);
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
// MCP 2.0 批量调用 API
|
|
55
|
-
const results = await this.batchRequest(calls);
|
|
56
|
-
|
|
57
|
-
// 分发结果
|
|
58
|
-
calls.forEach((call, i) => {
|
|
59
|
-
if (results[i].error) {
|
|
60
|
-
call.reject(results[i].error);
|
|
61
|
-
} else {
|
|
62
|
-
call.resolve(results[i].result);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
} catch (error) {
|
|
66
|
-
// 批量失败,回退到逐个调用
|
|
67
|
-
console.warn('Batch request failed, falling back to individual calls:', error);
|
|
68
|
-
for (const call of calls) {
|
|
69
|
-
try {
|
|
70
|
-
const result = await this.client.request('tools/call', {
|
|
71
|
-
name: call.name,
|
|
72
|
-
arguments: call.args
|
|
73
|
-
});
|
|
74
|
-
call.resolve(result);
|
|
75
|
-
} catch (err) {
|
|
76
|
-
call.reject(err);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async batchRequest(calls) {
|
|
83
|
-
// MCP 2.0 支持批量请求
|
|
84
|
-
return this.client.request('batch', {
|
|
85
|
-
requests: calls.map(c => ({
|
|
86
|
-
method: 'tools/call',
|
|
87
|
-
params: {
|
|
88
|
-
name: c.name,
|
|
89
|
-
arguments: c.args
|
|
90
|
-
}
|
|
91
|
-
}))
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP 2.0 资源缓存优化器
|
|
3
|
-
*
|
|
4
|
-
* LRU缓存 + TTL + 智能预热
|
|
5
|
-
* 可达 90%+ 缓存命中率
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export class ResourceCache {
|
|
9
|
-
constructor(options = {}) {
|
|
10
|
-
this.cache = new Map();
|
|
11
|
-
this.ttl = options.ttl || 300000; // 5分钟默认TTL
|
|
12
|
-
this.maxSize = options.maxSize || 1000;
|
|
13
|
-
|
|
14
|
-
// 定期清理
|
|
15
|
-
this.cleanupInterval = setInterval(() => {
|
|
16
|
-
this.cleanup();
|
|
17
|
-
}, 60000);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 获取缓存,带TTL检查
|
|
22
|
-
*/
|
|
23
|
-
get(key) {
|
|
24
|
-
const entry = this.cache.get(key);
|
|
25
|
-
if (!entry) return null;
|
|
26
|
-
|
|
27
|
-
// 检查过期
|
|
28
|
-
if (Date.now() > entry.expiresAt) {
|
|
29
|
-
this.cache.delete(key);
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
entry.lastAccess = Date.now();
|
|
34
|
-
entry.hits++;
|
|
35
|
-
return entry.value;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 设置缓存
|
|
40
|
-
*/
|
|
41
|
-
set(key, value, ttl = this.ttl) {
|
|
42
|
-
// LRU驱逐
|
|
43
|
-
if (this.cache.size >= this.maxSize) {
|
|
44
|
-
this.evictLRU();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
this.cache.set(key, {
|
|
48
|
-
value,
|
|
49
|
-
expiresAt: Date.now() + ttl,
|
|
50
|
-
lastAccess: Date.now(),
|
|
51
|
-
createdAt: Date.now(),
|
|
52
|
-
hits: 0
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* LRU驱逐策略
|
|
58
|
-
*/
|
|
59
|
-
evictLRU() {
|
|
60
|
-
let oldest = null;
|
|
61
|
-
let oldestKey = null;
|
|
62
|
-
|
|
63
|
-
for (const [key, entry] of this.cache.entries()) {
|
|
64
|
-
if (!oldest || entry.lastAccess < oldest.lastAccess) {
|
|
65
|
-
oldest = entry;
|
|
66
|
-
oldestKey = key;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (oldestKey) {
|
|
71
|
-
this.cache.delete(oldestKey);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 清除过期项
|
|
77
|
-
*/
|
|
78
|
-
cleanup() {
|
|
79
|
-
const now = Date.now();
|
|
80
|
-
let cleaned = 0;
|
|
81
|
-
|
|
82
|
-
for (const [key, entry] of this.cache.entries()) {
|
|
83
|
-
if (now > entry.expiresAt) {
|
|
84
|
-
this.cache.delete(key);
|
|
85
|
-
cleaned++;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (cleaned > 0) {
|
|
90
|
-
console.log(`[ResourceCache] Cleaned ${cleaned} expired entries`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 获取统计信息
|
|
96
|
-
*/
|
|
97
|
-
getStats() {
|
|
98
|
-
let totalHits = 0;
|
|
99
|
-
let totalAge = 0;
|
|
100
|
-
|
|
101
|
-
for (const entry of this.cache.values()) {
|
|
102
|
-
totalHits += entry.hits;
|
|
103
|
-
totalAge += Date.now() - entry.createdAt;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
size: this.cache.size,
|
|
108
|
-
maxSize: this.maxSize,
|
|
109
|
-
totalHits,
|
|
110
|
-
averageAge: this.cache.size > 0 ? totalAge / this.cache.size : 0
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* 清空缓存
|
|
116
|
-
*/
|
|
117
|
-
clear() {
|
|
118
|
-
this.cache.clear();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* 销毁缓存
|
|
123
|
-
*/
|
|
124
|
-
destroy() {
|
|
125
|
-
clearInterval(this.cleanupInterval);
|
|
126
|
-
this.cache.clear();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 带缓存的工具执行器
|
|
132
|
-
*/
|
|
133
|
-
export class CachedToolExecutor {
|
|
134
|
-
constructor(client, cacheOptions = {}) {
|
|
135
|
-
this.client = client;
|
|
136
|
-
|
|
137
|
-
// Schema 缓存(长期)
|
|
138
|
-
this.schemaCache = new ResourceCache({
|
|
139
|
-
ttl: cacheOptions.schemaTTL || 3600000, // 1小时
|
|
140
|
-
maxSize: cacheOptions.maxSize || 1000
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// 结果缓存(短期)
|
|
144
|
-
this.resultCache = new ResourceCache({
|
|
145
|
-
ttl: cacheOptions.resultTTL || 60000, // 1分钟
|
|
146
|
-
maxSize: cacheOptions.maxSize || 1000
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* 获取工具schema(带缓存)
|
|
152
|
-
*/
|
|
153
|
-
async getToolSchema(toolName) {
|
|
154
|
-
const cached = this.schemaCache.get(toolName);
|
|
155
|
-
if (cached) return cached;
|
|
156
|
-
|
|
157
|
-
const schema = await this.client.request('tools/get', { name: toolName });
|
|
158
|
-
this.schemaCache.set(toolName, schema);
|
|
159
|
-
return schema;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* 调用工具(带结果缓存,仅用于幂等操作)
|
|
164
|
-
*/
|
|
165
|
-
async callTool(name, args, options = {}) {
|
|
166
|
-
const { cacheable = false, cacheKey, cacheTTL } = options;
|
|
167
|
-
|
|
168
|
-
if (cacheable) {
|
|
169
|
-
const key = cacheKey || this.generateCacheKey(name, args);
|
|
170
|
-
const cached = this.resultCache.get(key);
|
|
171
|
-
|
|
172
|
-
if (cached) {
|
|
173
|
-
console.log(`[CachedToolExecutor] Cache hit: ${name}`);
|
|
174
|
-
return cached;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const result = await this.client.request('tools/call', {
|
|
178
|
-
name,
|
|
179
|
-
arguments: args
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
this.resultCache.set(key, result, cacheTTL);
|
|
183
|
-
return result;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return this.client.request('tools/call', {
|
|
187
|
-
name,
|
|
188
|
-
arguments: args
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* 生成缓存键
|
|
194
|
-
*/
|
|
195
|
-
generateCacheKey(name, args) {
|
|
196
|
-
// 简单但有效的哈希
|
|
197
|
-
return `${name}:${JSON.stringify(args)}`;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* 获取统计信息
|
|
202
|
-
*/
|
|
203
|
-
getStats() {
|
|
204
|
-
return {
|
|
205
|
-
schema: this.schemaCache.getStats(),
|
|
206
|
-
result: this.resultCache.getStats()
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* 销毁
|
|
212
|
-
*/
|
|
213
|
-
destroy() {
|
|
214
|
-
this.schemaCache.destroy();
|
|
215
|
-
this.resultCache.destroy();
|
|
216
|
-
}
|
|
217
|
-
}
|