(detectRefusalEarly = false, onTextDelta?: (delta: string) => void)
| 1179 | let retryCount = 0; |
| 1180 | |
| 1181 | const executeStream = async (detectRefusalEarly = false, onTextDelta?: (delta: string) => void): Promise<{ earlyAborted: boolean }> => { |
| 1182 | fullResponse = ''; |
| 1183 | const apiStart = Date.now(); |
| 1184 | let firstChunk = true; |
| 1185 | let earlyAborted = false; |
| 1186 | log.startPhase('send', '发送到 Cursor'); |
| 1187 | |
| 1188 | // ★ 早期中止支持:检测到拒绝后立即中断流,不等完整响应 |
| 1189 | const abortController = detectRefusalEarly ? new AbortController() : undefined; |
| 1190 | |
| 1191 | try { |
| 1192 | await sendCursorRequest(activeCursorReq, (event: CursorSSEEvent) => { |
| 1193 | if (event.type === 'finish') { |
| 1194 | if (event.messageMetadata?.usage) cursorUsage = event.messageMetadata.usage; |
| 1195 | return; |
| 1196 | } |
| 1197 | if (event.type !== 'text-delta' || !event.delta) return; |
| 1198 | if (firstChunk) { log.recordTTFT(); log.endPhase(); log.startPhase('response', '接收响应'); firstChunk = false; } |
| 1199 | fullResponse += event.delta; |
| 1200 | onTextDelta?.(event.delta); |
| 1201 | |
| 1202 | // ★ 早期拒绝检测:前 300 字符即可判断 |
| 1203 | if (detectRefusalEarly && !earlyAborted && fullResponse.length >= 200 && fullResponse.length < 600) { |
| 1204 | const preview = fullResponse.substring(0, 400); |
| 1205 | if (isRefusal(preview) && !hasToolCalls(preview)) { |
| 1206 | earlyAborted = true; |
| 1207 | log.info('Handler', 'response', `前${fullResponse.length}字符检测到拒绝,提前中止流`, { preview: preview.substring(0, 150) }); |
| 1208 | abortController?.abort(); |
| 1209 | } |
| 1210 | } |
| 1211 | }, abortController?.signal); |
| 1212 | } catch (err) { |
| 1213 | // 仅在非主动中止时抛出 |
| 1214 | if (!earlyAborted) throw err; |
| 1215 | } |
| 1216 | |
| 1217 | log.endPhase(); |
| 1218 | log.recordCursorApiTime(apiStart); |
| 1219 | return { earlyAborted }; |
| 1220 | }; |
| 1221 | |
| 1222 | try { |
| 1223 | if (!hasTools) { |
no test coverage detected