|
1 | 1 | import { ReadableStream, type Response } from './_shims/index'; |
2 | 2 | import { OpenAIError } from './error'; |
| 3 | +import { LineDecoder } from './internal/decoders/line'; |
3 | 4 |
|
4 | 5 | import { APIError } from 'openai/error'; |
5 | 6 |
|
@@ -329,117 +330,6 @@ class SSEDecoder { |
329 | 330 | } |
330 | 331 | } |
331 | 332 |
|
332 | | -/** |
333 | | - * A re-implementation of httpx's `LineDecoder` in Python that handles incrementally |
334 | | - * reading lines from text. |
335 | | - * |
336 | | - * https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258 |
337 | | - */ |
338 | | -class LineDecoder { |
339 | | - // prettier-ignore |
340 | | - static NEWLINE_CHARS = new Set(['\n', '\r']); |
341 | | - static NEWLINE_REGEXP = /\r\n|[\n\r]/g; |
342 | | - |
343 | | - buffer: string[]; |
344 | | - trailingCR: boolean; |
345 | | - textDecoder: any; // TextDecoder found in browsers; not typed to avoid pulling in either "dom" or "node" types. |
346 | | - |
347 | | - constructor() { |
348 | | - this.buffer = []; |
349 | | - this.trailingCR = false; |
350 | | - } |
351 | | - |
352 | | - decode(chunk: Bytes): string[] { |
353 | | - let text = this.decodeText(chunk); |
354 | | - |
355 | | - if (this.trailingCR) { |
356 | | - text = '\r' + text; |
357 | | - this.trailingCR = false; |
358 | | - } |
359 | | - if (text.endsWith('\r')) { |
360 | | - this.trailingCR = true; |
361 | | - text = text.slice(0, -1); |
362 | | - } |
363 | | - |
364 | | - if (!text) { |
365 | | - return []; |
366 | | - } |
367 | | - |
368 | | - const trailingNewline = LineDecoder.NEWLINE_CHARS.has(text[text.length - 1] || ''); |
369 | | - let lines = text.split(LineDecoder.NEWLINE_REGEXP); |
370 | | - |
371 | | - // if there is a trailing new line then the last entry will be an empty |
372 | | - // string which we don't care about |
373 | | - if (trailingNewline) { |
374 | | - lines.pop(); |
375 | | - } |
376 | | - |
377 | | - if (lines.length === 1 && !trailingNewline) { |
378 | | - this.buffer.push(lines[0]!); |
379 | | - return []; |
380 | | - } |
381 | | - |
382 | | - if (this.buffer.length > 0) { |
383 | | - lines = [this.buffer.join('') + lines[0], ...lines.slice(1)]; |
384 | | - this.buffer = []; |
385 | | - } |
386 | | - |
387 | | - if (!trailingNewline) { |
388 | | - this.buffer = [lines.pop() || '']; |
389 | | - } |
390 | | - |
391 | | - return lines; |
392 | | - } |
393 | | - |
394 | | - decodeText(bytes: Bytes): string { |
395 | | - if (bytes == null) return ''; |
396 | | - if (typeof bytes === 'string') return bytes; |
397 | | - |
398 | | - // Node: |
399 | | - if (typeof Buffer !== 'undefined') { |
400 | | - if (bytes instanceof Buffer) { |
401 | | - return bytes.toString(); |
402 | | - } |
403 | | - if (bytes instanceof Uint8Array) { |
404 | | - return Buffer.from(bytes).toString(); |
405 | | - } |
406 | | - |
407 | | - throw new OpenAIError( |
408 | | - `Unexpected: received non-Uint8Array (${bytes.constructor.name}) stream chunk in an environment with a global "Buffer" defined, which this library assumes to be Node. Please report this error.`, |
409 | | - ); |
410 | | - } |
411 | | - |
412 | | - // Browser |
413 | | - if (typeof TextDecoder !== 'undefined') { |
414 | | - if (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) { |
415 | | - this.textDecoder ??= new TextDecoder('utf8'); |
416 | | - return this.textDecoder.decode(bytes); |
417 | | - } |
418 | | - |
419 | | - throw new OpenAIError( |
420 | | - `Unexpected: received non-Uint8Array/ArrayBuffer (${ |
421 | | - (bytes as any).constructor.name |
422 | | - }) in a web platform. Please report this error.`, |
423 | | - ); |
424 | | - } |
425 | | - |
426 | | - throw new OpenAIError( |
427 | | - `Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.`, |
428 | | - ); |
429 | | - } |
430 | | - |
431 | | - flush(): string[] { |
432 | | - if (!this.buffer.length && !this.trailingCR) { |
433 | | - return []; |
434 | | - } |
435 | | - |
436 | | - const lines = [this.buffer.join('')]; |
437 | | - this.buffer = []; |
438 | | - this.trailingCR = false; |
439 | | - return lines; |
440 | | - } |
441 | | -} |
442 | | - |
443 | 333 | /** This is an internal helper function that's just used for testing */ |
444 | 334 | export function _decodeChunks(chunks: string[]): string[] { |
445 | 335 | const decoder = new LineDecoder(); |
|
0 commit comments