smart-image-scraper-mcp 2.9.2 → 2.10.0
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/package.json +1 -1
- package/src/index.js +8 -0
- package/src/services/orchestrator.js +44 -14
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -200,6 +200,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
200
200
|
// 格式化输出
|
|
201
201
|
const formattedResult = orchestrator.formatResult(result);
|
|
202
202
|
|
|
203
|
+
// 如果任务失败,标记为错误
|
|
204
|
+
if (!result.success) {
|
|
205
|
+
return {
|
|
206
|
+
content: [{ type: 'text', text: formattedResult }],
|
|
207
|
+
isError: true,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
203
211
|
return {
|
|
204
212
|
content: [{ type: 'text', text: formattedResult }],
|
|
205
213
|
};
|
|
@@ -347,31 +347,43 @@ export class Orchestrator {
|
|
|
347
347
|
metrics.recordRequest();
|
|
348
348
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
|
|
349
349
|
|
|
350
|
-
// 根据关键词数量动态计算超时时间(每个关键词
|
|
350
|
+
// 根据关键词数量动态计算超时时间(每个关键词 8 秒,最少 20 秒,最多 90 秒)
|
|
351
351
|
const keywords = this.parseKeywords(params.query);
|
|
352
352
|
const keywordCount = keywords.length;
|
|
353
|
-
const GLOBAL_TIMEOUT = Math.min(Math.max(keywordCount *
|
|
353
|
+
const GLOBAL_TIMEOUT = Math.min(Math.max(keywordCount * 8000, 20000), 90000);
|
|
354
|
+
|
|
355
|
+
logger.info(`[Orchestrator] Starting request: ${requestId}, keywords: ${keywordCount}, timeout: ${GLOBAL_TIMEOUT/1000}s`);
|
|
356
|
+
|
|
357
|
+
// 创建超时控制器
|
|
358
|
+
let timeoutId;
|
|
359
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
360
|
+
timeoutId = setTimeout(() => {
|
|
361
|
+
const errorMsg = `REQUEST_TIMEOUT: 请求超时(${GLOBAL_TIMEOUT/1000}秒),关键词过多请减少数量或稍后重试`;
|
|
362
|
+
logger.error(`[Orchestrator] ${errorMsg}`);
|
|
363
|
+
reject(new Error(errorMsg));
|
|
364
|
+
}, GLOBAL_TIMEOUT);
|
|
365
|
+
});
|
|
354
366
|
|
|
355
367
|
try {
|
|
356
|
-
logger.info(`[Orchestrator] Starting request: ${requestId}, keywords: ${keywordCount}, timeout: ${GLOBAL_TIMEOUT/1000}s`);
|
|
357
|
-
|
|
358
368
|
// 添加全局超时熔断机制
|
|
359
369
|
const result = await Promise.race([
|
|
360
370
|
this._executeInternal(params),
|
|
361
|
-
|
|
362
|
-
setTimeout(() => reject(new Error(`REQUEST_TIMEOUT: 请求超时(${GLOBAL_TIMEOUT/1000}秒),关键词过多请减少数量`)), GLOBAL_TIMEOUT)
|
|
363
|
-
)
|
|
371
|
+
timeoutPromise
|
|
364
372
|
]);
|
|
365
373
|
|
|
374
|
+
clearTimeout(timeoutId);
|
|
366
375
|
result.requestId = requestId;
|
|
367
376
|
logger.info(`[Orchestrator] Completed request: ${requestId}`);
|
|
368
377
|
return result;
|
|
369
378
|
} catch (error) {
|
|
379
|
+
clearTimeout(timeoutId);
|
|
370
380
|
logger.error(`[Orchestrator] Failed request: ${requestId} - ${error.message}`);
|
|
381
|
+
// 确保返回标准错误格式
|
|
371
382
|
return {
|
|
372
383
|
success: false,
|
|
373
384
|
error: error.message,
|
|
374
385
|
requestId,
|
|
386
|
+
duration: 0,
|
|
375
387
|
};
|
|
376
388
|
}
|
|
377
389
|
}
|
|
@@ -391,7 +403,7 @@ export class Orchestrator {
|
|
|
391
403
|
const options = { size, safeSearch, aspect, targetSize, fit, position };
|
|
392
404
|
|
|
393
405
|
const startTime = Date.now();
|
|
394
|
-
|
|
406
|
+
let keywords = this.parseKeywords(query);
|
|
395
407
|
|
|
396
408
|
if (keywords.length === 0) {
|
|
397
409
|
return {
|
|
@@ -400,6 +412,13 @@ export class Orchestrator {
|
|
|
400
412
|
};
|
|
401
413
|
}
|
|
402
414
|
|
|
415
|
+
// 限制最大关键词数量为 10 个,避免阻塞
|
|
416
|
+
const MAX_KEYWORDS = 10;
|
|
417
|
+
if (keywords.length > MAX_KEYWORDS) {
|
|
418
|
+
logger.warn(`Too many keywords (${keywords.length}), limiting to ${MAX_KEYWORDS}`);
|
|
419
|
+
keywords = keywords.slice(0, MAX_KEYWORDS);
|
|
420
|
+
}
|
|
421
|
+
|
|
403
422
|
logger.info(`Starting task: mode=${mode}, keywords=${keywords.join(', ')}, count=${count}, source=${source}`);
|
|
404
423
|
|
|
405
424
|
// 根据模式选择处理函数
|
|
@@ -407,12 +426,23 @@ export class Orchestrator {
|
|
|
407
426
|
? this.processKeywordLink.bind(this)
|
|
408
427
|
: this.processKeywordDownload.bind(this);
|
|
409
428
|
|
|
410
|
-
//
|
|
411
|
-
const results =
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
429
|
+
// 串行处理关键词,避免阻塞事件循环
|
|
430
|
+
const results = [];
|
|
431
|
+
for (const keyword of keywords) {
|
|
432
|
+
try {
|
|
433
|
+
const result = await processFunc(keyword, count, source, options);
|
|
434
|
+
results.push(result);
|
|
435
|
+
// 让出事件循环,确保 MCP 通信不被阻塞
|
|
436
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
437
|
+
} catch (error) {
|
|
438
|
+
results.push({
|
|
439
|
+
keyword,
|
|
440
|
+
success: false,
|
|
441
|
+
error: error.message,
|
|
442
|
+
duration: 0,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
416
446
|
|
|
417
447
|
// 汇总结果
|
|
418
448
|
const successResults = results.filter(r => r.success);
|