Skip to content

Commit

Permalink
v0.5.2 (#33)
Browse files Browse the repository at this point in the history
* fix(streaming): polyfill ReadableStream async iterator and text decoding

* fix: include README.md, LICENSE and CHANGELOG.md in published package

* v0.5.2

---------

Co-authored-by: Stainless Bot <107565488+stainless-bot@users.noreply.github.com>
  • Loading branch information
rattrayalex and stainless-bot authored Jul 8, 2023
1 parent 387bd17 commit 85b29d5
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 14 deletions.
5 changes: 4 additions & 1 deletion build
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ rm -rf dist
mkdir dist
# Copy src to dist/src and build from dist/src into dist, so that
# the source map for index.js.map will refer to ./src/index.ts etc
cp -rp src dist
cp -rp src README.md dist
for file in LICENSE CHANGELOG.md; do
if [ -e "${file}" ]; then cp "${file}" dist; fi
done
# this converts the export map paths for the dist directory
# and does a few other minor things
node scripts/make-dist-package-json.cjs > dist/package.json
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@anthropic-ai/sdk",
"version": "0.5.1",
"version": "0.5.2",
"description": "Client library for the Anthropic API",
"author": "Anthropic <support@anthropic.com>",
"types": "dist/index.d.ts",
Expand Down
82 changes: 71 additions & 11 deletions src/streaming.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import type { Response } from '@anthropic-ai/sdk/_shims/fetch';

import { APIResponse, Headers, createResponseHeaders } from './core';

import { safeJSON } from '@anthropic-ai/sdk/core';
import { APIError } from '@anthropic-ai/sdk/error';

type Bytes = string | ArrayBuffer | Uint8Array | Buffer | null | undefined;

type ServerSentEvent = {
event: string | null;
data: string;
Expand Down Expand Up @@ -85,19 +89,11 @@ export class Stream<Item> implements AsyncIterable<Item>, APIResponse<Stream<Ite
this.controller.abort();
throw new Error(`Attempted to iterate over a response with no body`);
}

const lineDecoder = new LineDecoder();

// @ts-ignore
for await (const chunk of this.response.body) {
let text;
if (chunk instanceof Buffer) {
text = chunk.toString();
} else if ((chunk as any) instanceof Uint8Array) {
text = Buffer.from(chunk).toString();
} else {
text = chunk;
}
const iter = readableStreamAsyncIterable<Bytes>(this.response.body);
for await (const chunk of iter) {
const text = decodeText(chunk);

for (const line of lineDecoder.decode(text)) {
const sse = this.decoder.decode(line);
Expand Down Expand Up @@ -218,3 +214,67 @@ function partition(str: string, delimiter: string): [string, string, string] {

return [str, '', ''];
}

let _textDecoder;
function decodeText(bytes: Bytes): string {
if (bytes == null) return '';
if (typeof bytes === 'string') return bytes;

// Node:
if (typeof Buffer !== 'undefined') {
if (bytes instanceof Buffer) {
return bytes.toString();
}
if (bytes instanceof Uint8Array) {
return Buffer.from(bytes).toString();
}

throw new Error(`Unexpected: received non-Uint8Array (${bytes.constructor.name}) in Node.`);
}

// Browser
if (typeof TextDecoder !== 'undefined') {
if (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) {
_textDecoder ??= new TextDecoder('utf8');
return _textDecoder.decode(bytes);
}

throw new Error(
`Unexpected: received non-Uint8Array/ArrayBuffer (${
(bytes as any).constructor.name
}) in a web platform.`,
);
}

throw new Error(`Unexpected: neither Buffer nor TextDecoder are available as globals.`);
}

/**
* Most browsers don't yet have async iterable support for ReadableStream,
* and Node has a very different way of reading bytes from its "ReadableStream".
*
* This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1624185965
*
* We make extensive use of "any" here to avoid pulling in either "node" or "dom" types
* to library users' type scopes.
*/
function readableStreamAsyncIterable<T>(stream: any): AsyncIterableIterator<T> {
if (stream[Symbol.asyncIterator]) {
return stream[Symbol.asyncIterator];
}

const reader = stream.getReader();

return {
next() {
return reader.read();
},
async return() {
reader.releaseLock();
return { done: true, value: undefined };
},
[Symbol.asyncIterator]() {
return this;
},
};
}
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const VERSION = '0.5.1';
export const VERSION = '0.5.2';

0 comments on commit 85b29d5

Please sign in to comment.