Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"./*": {
"./*.mjs": {
"types": "./dist/*.d.ts",
"require": "./dist/*.js",
"default": "./dist/*.mjs"
},
"./*.js": {
"types": "./dist/*.d.ts",
"default": "./dist/*.js"
},
"./*.mjs": {
"./*": {
"types": "./dist/*.d.ts",
"require": "./dist/*.js",
"default": "./dist/*.mjs"
}
},
Expand All @@ -74,14 +74,12 @@
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"@types/qs": "^6.9.7",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"digest-fetch": "^1.3.0",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"qs": "^6.10.3"
"node-fetch": "^2.6.7"
},
"devDependencies": {
"@types/jest": "^29.4.0",
Expand Down
2 changes: 1 addition & 1 deletion src/_shims/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ exports.Request = Request;
exports.Response = Response;
exports.Headers = Headers;

exports.isPolyfilled = true;
exports.isPolyfilled = false;
40 changes: 23 additions & 17 deletions src/core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as qs from 'qs';
import { VERSION } from './version';
import { Stream } from './streaming';
import { APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError } from './error';
Expand Down Expand Up @@ -82,18 +81,6 @@ export abstract class APIClient {
*/
protected validateHeaders(headers: Headers, customHeaders: Headers) {}

/**
* Override this to add your own qs.stringify options, for example:
*
* {
* ...super.qsOptions(),
* strictNullHandling: true,
* }
*/
protected qsOptions(): qs.IStringifyOptions | undefined {
return {};
}

protected defaultIdempotencyKey(): string {
return `stainless-node-retry-${uuid4()}`;
}
Expand Down Expand Up @@ -128,9 +115,11 @@ export abstract class APIClient {
return Buffer.byteLength(body, 'utf8').toString();
}

const encoder = new TextEncoder();
const encoded = encoder.encode(body);
return encoded.length.toString();
if (typeof TextEncoder !== 'undefined') {
const encoder = new TextEncoder();
const encoded = encoder.encode(body);
return encoded.length.toString();
}
}

return null;
Expand Down Expand Up @@ -302,12 +291,29 @@ export abstract class APIClient {
}

if (query) {
url.search = qs.stringify(query, this.qsOptions());
url.search = this.stringifyQuery(query);
}

return url.toString();
}

protected stringifyQuery(query: Record<string, unknown>): string {
return Object.entries(query)
.filter(([_, value]) => typeof value !== 'undefined')
.map(([key, value]) => {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
if (value === null) {
return `${encodeURIComponent(key)}=`;
}
throw new Error(
`Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`,
);
})
.join('&');
}

async fetchWithTimeout(
url: RequestInfo,
init: RequestInit | undefined,
Expand Down
19 changes: 7 additions & 12 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// File generated from our OpenAPI spec by Stainless.

import * as qs from 'qs';
import * as Core from './core';
import * as API from './resources/index';
import * as Errors from './error';
import type { Agent } from '@anthropic-ai/sdk/_shims/agent';
import * as Uploads from './uploads';

type Config = {
export interface ClientOptions {
/**
* Defaults to process.env["ANTHROPIC_API_KEY"]. Set it to null if you want to send unauthenticated requests.
*/
Expand Down Expand Up @@ -68,20 +67,20 @@ type Config = {
defaultQuery?: Core.DefaultQuery;

authToken?: string | null;
};
}

/** Instantiate the API Client. */
export class Anthropic extends Core.APIClient {
apiKey: string | null;
authToken?: string | null;

private _options: Config;
private _options: ClientOptions;

constructor(config?: Config) {
const options: Config = {
constructor(opts?: ClientOptions) {
const options: ClientOptions = {
apiKey: typeof process === 'undefined' ? '' : process.env['ANTHROPIC_API_KEY'] || '',
baseURL: 'https://api.anthropic.com',
...config,
...opts,
};

super({
Expand All @@ -94,7 +93,7 @@ export class Anthropic extends Core.APIClient {
this.apiKey = options.apiKey || null;
this._options = options;

this.authToken = config?.authToken || process.env['ANTHROPIC_AUTH_TOKEN'] || null;
this.authToken = opts?.authToken || process.env['ANTHROPIC_AUTH_TOKEN'] || null;
}

completions: API.Completions = new API.Completions(this);
Expand Down Expand Up @@ -157,10 +156,6 @@ export class Anthropic extends Core.APIClient {
return { Authorization: `Bearer ${this.authToken}` };
}

protected override qsOptions(): qs.IStringifyOptions {
return { arrayFormat: 'comma' };
}

static Anthropic = this;
static HUMAN_PROMPT = '\n\nHuman:';
static AI_PROMPT = '\n\nAssistant:';
Expand Down
4 changes: 2 additions & 2 deletions src/resources/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export namespace CompletionCreateParams {
* [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
* additional details.
*/
model: string;
model: (string & {}) | 'claude-2' | 'claude-instant-1';

/**
* The prompt that you want Claude to complete.
Expand Down Expand Up @@ -175,7 +175,7 @@ export namespace CompletionCreateParams {
* [models](https://docs.anthropic.com/claude/reference/selecting-a-model) for
* additional details.
*/
model: string;
model: (string & {}) | 'claude-2' | 'claude-instant-1';

/**
* The prompt that you want Claude to complete.
Expand Down
26 changes: 26 additions & 0 deletions tests/stringifyQuery.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { APIClient } from '@anthropic-ai/sdk/core';

const { stringifyQuery } = APIClient.prototype as any;

describe('APIClient.stringifyQuery', () => {
for (const [input, expected] of [
[{ a: '1', b: 2, c: true }, 'a=1&b=2&c=true'],
[{ a: null, b: false, c: undefined }, 'a=&b=false'],
[{ 'a/b': 1.28341 }, `${encodeURIComponent('a/b')}=1.28341`],
[
{ 'a/b': 'c/d', 'e=f': 'g&h' },
`${encodeURIComponent('a/b')}=${encodeURIComponent('c/d')}&${encodeURIComponent(
'e=f',
)}=${encodeURIComponent('g&h')}`,
],
]) {
it(`${JSON.stringify(input)} -> ${expected}`, () => {
expect(stringifyQuery(input)).toEqual(expected);
});
}
for (const value of [[], {}, new Date()]) {
it(`${JSON.stringify(value)} -> <error>`, () => {
expect(() => stringifyQuery({ value })).toThrow(`Cannot stringify type ${typeof value}`);
});
}
});
48 changes: 0 additions & 48 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -845,11 +845,6 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.0.tgz#efcbd41937f9ae7434c714ab698604822d890759"
integrity sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw==

"@types/qs@^6.9.7":
version "6.9.7"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==

"@types/stack-utils@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
Expand Down Expand Up @@ -1260,14 +1255,6 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==

call-bind@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
dependencies:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"

callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
Expand Down Expand Up @@ -2027,15 +2014,6 @@ get-caller-file@^2.0.5:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==

get-intrinsic@^1.0.2:
version "1.1.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"

get-package-type@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
Expand Down Expand Up @@ -2126,11 +2104,6 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==

has-symbols@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==

has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
Expand Down Expand Up @@ -3053,11 +3026,6 @@ npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"

object-inspect@^1.9.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==

once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
Expand Down Expand Up @@ -3397,13 +3365,6 @@ pure-rand@^6.0.0:
resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306"
integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==

qs@^6.10.3:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
dependencies:
side-channel "^1.0.4"

queue-lit@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/queue-lit/-/queue-lit-1.5.0.tgz#8197fdafda1edd615c8a0fc14c48353626e5160a"
Expand Down Expand Up @@ -3597,15 +3558,6 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==

side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
dependencies:
call-bind "^1.0.0"
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"

sigmund@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
Expand Down