| 1348 | |
| 1349 | return new ReadableStream({ |
| 1350 | async start(controller) { |
| 1351 | try { |
| 1352 | for await (const chunk of stream) { |
| 1353 | if (chunk.usage) { |
| 1354 | promptTokens = chunk.usage.prompt_tokens ?? 0 |
| 1355 | completionTokens = chunk.usage.completion_tokens ?? 0 |
| 1356 | totalTokens = chunk.usage.total_tokens ?? 0 |
| 1357 | } |
| 1358 | |
| 1359 | const content = chunk.choices?.[0]?.delta?.content || '' |
| 1360 | if (content) { |
| 1361 | fullContent += content |
| 1362 | controller.enqueue(new TextEncoder().encode(content)) |
| 1363 | } |
| 1364 | } |
| 1365 | |
| 1366 | if (onComplete) { |
| 1367 | if (promptTokens === 0 && completionTokens === 0) { |
| 1368 | streamLogger.warn(`${providerName} stream completed without usage data`) |
| 1369 | } |
| 1370 | onComplete(fullContent, { |
| 1371 | prompt_tokens: promptTokens, |
| 1372 | completion_tokens: completionTokens, |
| 1373 | total_tokens: totalTokens || promptTokens + completionTokens, |
| 1374 | }) |
| 1375 | } |
| 1376 | |
| 1377 | controller.close() |
| 1378 | } catch (error) { |
| 1379 | controller.error(error) |
| 1380 | } |
| 1381 | }, |
| 1382 | }) |
| 1383 | } |
| 1384 | |