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