(res: Response, cursorReq: CursorChatRequest, body: AnthropicRequest, log: RequestLogger, clientRequestedThinking: boolean = false)
| 1141 | // ==================== 流式处理 ==================== |
| 1142 | |
| 1143 | async function handleStream(res: Response, cursorReq: CursorChatRequest, body: AnthropicRequest, log: RequestLogger, clientRequestedThinking: boolean = false): Promise<void> { |
| 1144 | // 设置 SSE headers |
| 1145 | res.writeHead(200, { |
| 1146 | 'Content-Type': 'text/event-stream', |
| 1147 | 'Cache-Control': 'no-cache', |
| 1148 | 'Connection': 'keep-alive', |
| 1149 | 'X-Accel-Buffering': 'no', |
| 1150 | }); |
| 1151 | |
| 1152 | const id = msgId(); |
| 1153 | const model = body.model; |
| 1154 | const hasTools = (body.tools?.length ?? 0) > 0; |
| 1155 | |
| 1156 | // 发送 message_start |
| 1157 | writeSSE(res, 'message_start', { |
| 1158 | type: 'message_start', |
| 1159 | message: { |
| 1160 | id, type: 'message', role: 'assistant', content: [], |
| 1161 | model, stop_reason: null, stop_sequence: null, |
| 1162 | usage: { input_tokens: estimateInputTokens(body), output_tokens: 0 }, |
| 1163 | }, |
| 1164 | }); |
| 1165 | |
| 1166 | // ★ 流式保活 — 注意:无工具的增量流式路径(handleDirectTextStream)有自己的 keepalive |
| 1167 | // 这里的 keepalive 仅用于工具模式下的缓冲/续写期间 |
| 1168 | let keepaliveInterval: ReturnType<typeof setInterval> | undefined; |
| 1169 | |
| 1170 | let fullResponse = ''; |
| 1171 | let sentText = ''; |
| 1172 | let blockIndex = 0; |
| 1173 | let textBlockStarted = false; |
| 1174 | let thinkingBlockEmitted = false; |
| 1175 | let cursorUsage: { inputTokens?: number; outputTokens?: number; totalTokens?: number } | undefined; |
| 1176 | |
| 1177 | // 无工具模式:先缓冲全部响应再检测拒绝,如果是拒绝则重试 |
| 1178 | let activeCursorReq = cursorReq; |
| 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); |
no test coverage detected