Spaces:
Running
Running
File size: 4,315 Bytes
bd925df | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | /**
* SSE Parser for Cloud Code
*
* Parses SSE responses for non-streaming thinking models.
* Accumulates all parts and returns a single response.
*/
import { convertGoogleToAnthropic } from '../format/index.js';
import { logger } from '../utils/logger.js';
/**
* Parse SSE response for thinking models and accumulate all parts
*
* @param {Response} response - The HTTP response with SSE body
* @param {string} originalModel - The original model name
* @returns {Promise<Object>} Anthropic-format response object
*/
export async function parseThinkingSSEResponse(response, originalModel) {
let accumulatedThinkingText = '';
let accumulatedThinkingSignature = '';
let accumulatedText = '';
const finalParts = [];
let usageMetadata = {};
let finishReason = 'STOP';
const flushThinking = () => {
if (accumulatedThinkingText) {
finalParts.push({
thought: true,
text: accumulatedThinkingText,
thoughtSignature: accumulatedThinkingSignature
});
accumulatedThinkingText = '';
accumulatedThinkingSignature = '';
}
};
const flushText = () => {
if (accumulatedText) {
finalParts.push({ text: accumulatedText });
accumulatedText = '';
}
};
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.startsWith('data:')) continue;
const jsonText = line.slice(5).trim();
if (!jsonText) continue;
try {
const data = JSON.parse(jsonText);
const innerResponse = data.response || data;
if (innerResponse.usageMetadata) {
usageMetadata = innerResponse.usageMetadata;
}
const candidates = innerResponse.candidates || [];
const firstCandidate = candidates[0] || {};
if (firstCandidate.finishReason) {
finishReason = firstCandidate.finishReason;
}
const parts = firstCandidate.content?.parts || [];
for (const part of parts) {
if (part.thought === true) {
flushText();
accumulatedThinkingText += (part.text || '');
if (part.thoughtSignature) {
accumulatedThinkingSignature = part.thoughtSignature;
}
} else if (part.functionCall) {
flushThinking();
flushText();
finalParts.push(part);
} else if (part.text !== undefined) {
if (!part.text) continue;
flushThinking();
accumulatedText += part.text;
} else if (part.inlineData) {
// Handle image content
flushThinking();
flushText();
finalParts.push(part);
}
}
} catch (e) {
logger.debug('[CloudCode] SSE parse warning:', e.message, 'Raw:', jsonText.slice(0, 100));
}
}
}
flushThinking();
flushText();
const accumulatedResponse = {
candidates: [{ content: { parts: finalParts }, finishReason }],
usageMetadata
};
const partTypes = finalParts.map(p => p.thought ? 'thought' : (p.functionCall ? 'functionCall' : (p.inlineData ? 'inlineData' : 'text')));
logger.debug('[CloudCode] Response received (SSE), part types:', partTypes);
if (finalParts.some(p => p.thought)) {
const thinkingPart = finalParts.find(p => p.thought);
logger.debug('[CloudCode] Thinking signature length:', thinkingPart?.thoughtSignature?.length || 0);
}
return convertGoogleToAnthropic(accumulatedResponse, originalModel);
}
|