MCPcopy
hub / github.com/7836246/cursor2api / handleNonStream

Function handleNonStream

src/handler.ts:1818–2145  ·  view source on GitHub ↗
(res: Response, cursorReq: CursorChatRequest, body: AnthropicRequest, log: RequestLogger, clientRequestedThinking: boolean = false)

Source from the content-addressed store, hash-verified

1816// ==================== 非流式处理 ====================
1817
1818async function handleNonStream(res: Response, cursorReq: CursorChatRequest, body: AnthropicRequest, log: RequestLogger, clientRequestedThinking: boolean = false): Promise<void> {
1819 // ★ 非流式保活:手动设置 chunked 响应,在缓冲期间每 15s 发送空白字符保活
1820 // JSON.parse 会忽略前导空白,所以客户端解析不受影响
1821 res.writeHead(200, { 'Content-Type': 'application/json' });
1822 const keepaliveInterval = setInterval(() => {
1823 try {
1824 res.write(' ');
1825 // @ts-expect-error flush exists on ServerResponse when compression is used
1826 if (typeof res.flush === 'function') res.flush();
1827 } catch { /* connection already closed, ignore */ }
1828 }, 15000);
1829
1830 try {
1831 log.startPhase('send', '发送到 Cursor (非流式)');
1832 const apiStart = Date.now();
1833 let { text: fullText, usage: cursorUsage } = await sendCursorRequestFull(cursorReq);
1834 log.recordTTFT();
1835 log.recordCursorApiTime(apiStart);
1836 log.recordRawResponse(fullText);
1837 log.startPhase('response', '处理响应');
1838 const hasTools = (body.tools?.length ?? 0) > 0;
1839 let activeCursorReq = cursorReq;
1840 let retryCount = 0;
1841
1842 log.info('Handler', 'response', `非流式原始响应: ${fullText.length} chars`, {
1843 preview: fullText.substring(0, 300),
1844 hasTools,
1845 });
1846
1847 // ★ Thinking 提取(在拒绝检测之前)
1848 // 始终剥离 thinking 标签,避免泄漏到最终文本中
1849 let thinkingContent = '';
1850 if (hasLeadingThinking(fullText)) {
1851 const { thinkingContent: extracted, strippedText } = extractThinking(fullText);
1852 if (extracted) {
1853 thinkingContent = extracted;
1854 fullText = strippedText;
1855 if (clientRequestedThinking) {
1856 log.info('Handler', 'thinking', `非流式剥离 thinking → content block: ${thinkingContent.length} chars, 剩余 ${fullText.length} chars`);
1857 } else {
1858 log.info('Handler', 'thinking', `非流式剥离 thinking (非客户端请求): ${thinkingContent.length} chars, 剩余 ${fullText.length} chars`);
1859 }
1860 }
1861 }
1862
1863 // 拒绝检测 + 自动重试
1864 // fullText 已在上方剥离 thinking 标签,可直接用于拒绝检测
1865 const shouldRetry = () => {
1866 return isRefusal(fullText) && !(hasTools && hasToolCalls(fullText));
1867 };
1868
1869 if (shouldRetry()) {
1870 for (let attempt = 0; attempt < MAX_REFUSAL_RETRIES; attempt++) {
1871 retryCount++;
1872 log.warn('Handler', 'retry', `非流式检测到拒绝(第${retryCount}次重试)`, { preview: fullText.substring(0, 200) });
1873 log.updateSummary({ retryCount });
1874 const retryBody = buildRetryRequest(body, attempt);
1875 activeCursorReq = await convertToCursorRequest(retryBody);

Callers 1

handleMessagesFunction · 0.85

Calls 15

getConfigFunction · 0.90
parseToolCallsFunction · 0.90
isRefusalFunction · 0.90
sendCursorRequestFullFunction · 0.85
hasLeadingThinkingFunction · 0.85
extractThinkingFunction · 0.85
buildRetryRequestFunction · 0.85
convertToCursorRequestFunction · 0.85
isToolCapabilityQuestionFunction · 0.85
deduplicateContinuationFunction · 0.85
toolIdFunction · 0.85

Tested by

no test coverage detected