( stream: AsyncGenerator<GenerateContentResponse>, onComplete?: (content: string, usage: GeminiUsage) => void )
| 283 | * Creates a ReadableStream from a Google Gemini streaming response |
| 284 | */ |
| 285 | export function createReadableStreamFromGeminiStream( |
| 286 | stream: AsyncGenerator<GenerateContentResponse>, |
| 287 | onComplete?: (content: string, usage: GeminiUsage) => void |
| 288 | ): ReadableStream<Uint8Array> { |
| 289 | let fullContent = '' |
| 290 | let usage: GeminiUsage = { promptTokenCount: 0, candidatesTokenCount: 0, totalTokenCount: 0 } |
| 291 | |
| 292 | return new ReadableStream({ |
| 293 | async start(controller) { |
| 294 | try { |
| 295 | for await (const chunk of stream) { |
| 296 | if (chunk.usageMetadata) { |
| 297 | usage = convertUsageMetadata(chunk.usageMetadata) |
| 298 | } |
| 299 | |
| 300 | const text = chunk.text |
| 301 | if (text) { |
| 302 | fullContent += text |
| 303 | controller.enqueue(new TextEncoder().encode(text)) |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | onComplete?.(fullContent, usage) |
| 308 | controller.close() |
| 309 | } catch (error) { |
| 310 | logger.error('Error reading Google Gemini stream', { |
| 311 | error: toError(error).message, |
| 312 | }) |
| 313 | controller.error(error) |
| 314 | } |
| 315 | }, |
| 316 | }) |
| 317 | } |
| 318 | |
| 319 | /** |
| 320 | * Maps string mode to FunctionCallingConfigMode enum |
no outgoing calls
no test coverage detected