MCPcopy Create free account
hub / github.com/Rfym21/Qwen2API / processSSEPayload

Function processSSEPayload

src/controllers/chat.js:257–361  ·  view source on GitHub ↗
(dataContent)

Source from the content-addressed store, hash-verified

255 return await work()
256 } finally {
257 clearInterval(heartbeat)
258 }
259}
260
261const runWithSSEHeartbeat = async (res, work, intervalMs = 15000) => {
262 const heartbeatMs = Math.max(1, Number(intervalMs) || 15000)
263 const heartbeat = setInterval(() => {
264 if (res.writableEnded || res.destroyed) return
265 try {
266 // SSE 已经提交 200 响应后,用注释帧保活。注释不会进入 OpenAI delta,
267 // 但能阻止反代在长 thinking 或纠正 attempt 期间把连接判为空闲。
268 res.write(': qwen2api-agent-keepalive\n\n')
269 if (typeof res.flush === 'function') res.flush()
270 } catch (_) {
271 // 客户端断开会由后续流消费/写入路径统一收敛。
272 }
273 }, heartbeatMs)
274 heartbeat.unref?.()
275 try {
276 return await work()
277 } finally {
278 clearInterval(heartbeat)
279 }
280}
281
282const normalizeAgentUsage = (attempt, requestBody, completionText) => {
283 let usage = { ...(attempt?.totalTokens || {}) }
284 if (!usage.prompt_tokens && !usage.completion_tokens) {
285 usage = createUsageObject(requestBody?.messages || [], completionText, null)
286 }
287 usage.prompt_tokens = Math.max(0, Number(usage.prompt_tokens) || 0)
288 usage.completion_tokens = Math.max(0, Number(usage.completion_tokens) || 0)
289 usage.total_tokens = usage.prompt_tokens + usage.completion_tokens
290 return usage
291}
292
293/**
294 * Residuo de protocolo que TODAVÍA se puede pelar en la entrega.
295 *
296 * Lo que ya salió en vivo por el canal de contenido es irrecuperable, y borrarlo del buffer
297 * rompería el descuento de handleOpenAIAgentStream (`bufferedContent.startsWith(...)`) y lo
298 * duplicaría en el cliente: un residuo entregado una vez es mejor que la respuesta entera
299 * entregada dos. Hoy ninguna ronda aceptada llega aquí con texto ya emitido y residuo a la
300 * vez (el gate 422 corta antes), así que este filtro es defensa, no un camino vivo.
301 */
302const deliverableResidueSpans = (attempt, alreadyStreamed = 0) =>
303 (attempt?.residueSpans || []).filter(span =>
304 span && typeof span.text === 'string' && Number.isInteger(span.at) && span.at >= alreadyStreamed)
305
306/**
307 * Pelado de ENTREGA, gemelo literal de anthropic.js:2278.
308 *
309 * Orden obligatorio: primero el residuo por POSICIÓN —sobre el texto crudo, que es el
310 * sistema de coordenadas en el que el parser registró los spans— y sólo después las
311 * etiquetas de control. Al revés, quitar las etiquetas desplazaría los offsets y el residuo
312 * sobreviviría (lo pinta el gemelo en anthropic-toolcall-salvage: "strip-before-tags keeps
313 * offsets honest").
314 *

Callers 1

pipeUpstreamFunction · 0.85

Calls 7

isJsonFunction · 0.85
writeContentDeltaFunction · 0.85
writeToolCallsDeltaFunction · 0.85
writeReasoningDeltaFunction · 0.85
emitAnswerContentFunction · 0.85
generateMarkdownTableMethod · 0.80

Tested by

no test coverage detected