diff --git a/modules/story-summary/vector/llm/atom-extraction.js b/modules/story-summary/vector/llm/atom-extraction.js index d433d9f..b8046ae 100644 --- a/modules/story-summary/vector/llm/atom-extraction.js +++ b/modules/story-summary/vector/llm/atom-extraction.js @@ -8,14 +8,14 @@ // 每楼层 1-2 个场景锚点(非碎片原子),60-100 字场景摘要 // ============================================================================ -import { callLLM, parseJson } from './llm-service.js'; +import { callLLM, cancelAllL0Requests, parseJson } from './llm-service.js'; import { xbLog } from '../../../../core/debug-core.js'; import { filterText } from '../utils/text-filter.js'; const MODULE_ID = 'atom-extraction'; const CONCURRENCY = 10; -const RETRY_COUNT = 2; +const RETRY_COUNT = 1; const RETRY_DELAY = 500; const DEFAULT_TIMEOUT = 40000; const STAGGER_DELAY = 80; @@ -25,6 +25,7 @@ let batchCancelled = false; export function cancelBatchExtraction() { batchCancelled = true; + cancelAllL0Requests(); } export function isBatchCancelled() { diff --git a/modules/story-summary/vector/llm/llm-service.js b/modules/story-summary/vector/llm/llm-service.js index 6309aa0..4d10576 100644 --- a/modules/story-summary/vector/llm/llm-service.js +++ b/modules/story-summary/vector/llm/llm-service.js @@ -9,6 +9,7 @@ const SILICONFLOW_API_URL = 'https://api.siliconflow.cn/v1'; const DEFAULT_L0_MODEL = 'Qwen/Qwen3-8B'; let callCounter = 0; +const activeL0SessionIds = new Set(); function getStreamingModule() { const mod = window.xiaobaixStreamingGeneration; @@ -69,6 +70,7 @@ export async function callLLM(messages, options = {}) { } try { + activeL0SessionIds.add(uniqueId); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error(`L0 request timeout after ${timeout}ms`)), timeout); }); @@ -80,9 +82,20 @@ export async function callLLM(messages, options = {}) { } catch (e) { xbLog.error(MODULE_ID, 'LLM调用失败', e); throw e; + } finally { + activeL0SessionIds.delete(uniqueId); } } +export function cancelAllL0Requests() { + const mod = getStreamingModule(); + if (!mod?.cancel) return; + for (const sessionId of activeL0SessionIds) { + try { mod.cancel(sessionId); } catch {} + } + activeL0SessionIds.clear(); +} + export function parseJson(text) { if (!text) return null; let s = text.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '').trim(); diff --git a/modules/story-summary/vector/pipeline/state-integration.js b/modules/story-summary/vector/pipeline/state-integration.js index 1ebb7a0..996bbb8 100644 --- a/modules/story-summary/vector/pipeline/state-integration.js +++ b/modules/story-summary/vector/pipeline/state-integration.js @@ -29,7 +29,7 @@ import { filterText } from '../utils/text-filter.js'; const MODULE_ID = 'state-integration'; // ★ 并发配置 -const CONCURRENCY = 50; +const CONCURRENCY = 10; const STAGGER_DELAY = 15; const DEBUG_CONCURRENCY = true; const R_AGG_MAX_CHARS = 256;