From 52a2a11467e4c117b50516104eb9b29aca86e232 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:44:10 +0000 Subject: [PATCH 01/11] chore(internal): reorder model constants (#676) chore(internal): update models used in tests --- .stats.yml | 2 +- src/resources/messages/messages.ts | 4 ++-- tests/api-resources/beta/messages/batches.test.ts | 4 ++-- tests/api-resources/beta/messages/messages.test.ts | 8 ++++---- tests/api-resources/completions.test.ts | 4 ++-- tests/api-resources/messages/batches.test.ts | 4 ++-- tests/api-resources/messages/messages.test.ts | 8 ++++---- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.stats.yml b/.stats.yml index 64f8716d..3ea9b07f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 21 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic-f5276eeef7512112e802c85530c51e0a971ee521eebe3a0db309621587b4973d.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic-946022163d75ddc00f49883756b189412098aa07b600ee0890655eebcb3c440c.yml diff --git a/src/resources/messages/messages.ts b/src/resources/messages/messages.ts index f1b4fb8e..b6c53e05 100644 --- a/src/resources/messages/messages.ts +++ b/src/resources/messages/messages.ts @@ -404,7 +404,6 @@ export interface Metadata { * details and options. */ export type Model = - | (string & {}) | 'claude-3-5-haiku-latest' | 'claude-3-5-haiku-20241022' | 'claude-3-5-sonnet-latest' @@ -415,7 +414,8 @@ export type Model = | 'claude-3-sonnet-20240229' | 'claude-3-haiku-20240307' | 'claude-2.1' - | 'claude-2.0'; + | 'claude-2.0' + | (string & {}); const DEPRECATED_MODELS: { [K in Model]?: string; diff --git a/tests/api-resources/beta/messages/batches.test.ts b/tests/api-resources/beta/messages/batches.test.ts index b91258ea..cdef5c04 100644 --- a/tests/api-resources/beta/messages/batches.test.ts +++ b/tests/api-resources/beta/messages/batches.test.ts @@ -17,7 +17,7 @@ describe('resource batches', () => { params: { max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-sonnet-20241022', + model: 'claude-3-5-haiku-latest', }, }, ], @@ -39,7 +39,7 @@ describe('resource batches', () => { params: { max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-sonnet-20241022', + model: 'claude-3-5-haiku-latest', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], stream: false, diff --git a/tests/api-resources/beta/messages/messages.test.ts b/tests/api-resources/beta/messages/messages.test.ts index f2f6964b..363634bb 100644 --- a/tests/api-resources/beta/messages/messages.test.ts +++ b/tests/api-resources/beta/messages/messages.test.ts @@ -13,7 +13,7 @@ describe('resource messages', () => { const responsePromise = client.beta.messages.create({ max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-sonnet-20241022', + model: 'claude-3-5-haiku-latest', }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -28,7 +28,7 @@ describe('resource messages', () => { const response = await client.beta.messages.create({ max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-sonnet-20241022', + model: 'claude-3-5-haiku-latest', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], stream: false, @@ -75,7 +75,7 @@ describe('resource messages', () => { test('countTokens: only required params', async () => { const responsePromise = client.beta.messages.countTokens({ messages: [{ content: 'string', role: 'user' }], - model: 'string', + model: 'claude-3-5-haiku-latest', }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -89,7 +89,7 @@ describe('resource messages', () => { test('countTokens: required and optional params', async () => { const response = await client.beta.messages.countTokens({ messages: [{ content: 'string', role: 'user' }], - model: 'string', + model: 'claude-3-5-haiku-latest', system: [ { text: "Today's date is 2024-06-01.", diff --git a/tests/api-resources/completions.test.ts b/tests/api-resources/completions.test.ts index fcd0a68c..e8520d9a 100644 --- a/tests/api-resources/completions.test.ts +++ b/tests/api-resources/completions.test.ts @@ -12,7 +12,7 @@ describe('resource completions', () => { test('create: only required params', async () => { const responsePromise = client.completions.create({ max_tokens_to_sample: 256, - model: 'string', + model: 'claude-3-5-haiku-latest', prompt: '\n\nHuman: Hello, world!\n\nAssistant:', }); const rawResponse = await responsePromise.asResponse(); @@ -27,7 +27,7 @@ describe('resource completions', () => { test('create: required and optional params', async () => { const response = await client.completions.create({ max_tokens_to_sample: 256, - model: 'string', + model: 'claude-3-5-haiku-latest', prompt: '\n\nHuman: Hello, world!\n\nAssistant:', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], diff --git a/tests/api-resources/messages/batches.test.ts b/tests/api-resources/messages/batches.test.ts index c7239648..8a3d3cfa 100644 --- a/tests/api-resources/messages/batches.test.ts +++ b/tests/api-resources/messages/batches.test.ts @@ -17,7 +17,7 @@ describe('resource batches', () => { params: { max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-sonnet-20241022', + model: 'claude-3-5-haiku-latest', }, }, ], @@ -39,7 +39,7 @@ describe('resource batches', () => { params: { max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-sonnet-20241022', + model: 'claude-3-5-haiku-latest', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], system: [ diff --git a/tests/api-resources/messages/messages.test.ts b/tests/api-resources/messages/messages.test.ts index a5b5d328..e39e9972 100644 --- a/tests/api-resources/messages/messages.test.ts +++ b/tests/api-resources/messages/messages.test.ts @@ -13,7 +13,7 @@ describe('resource messages', () => { const responsePromise = client.messages.create({ max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-sonnet-20241022', + model: 'claude-3-5-haiku-latest', }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -28,7 +28,7 @@ describe('resource messages', () => { const response = await client.messages.create({ max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-sonnet-20241022', + model: 'claude-3-5-haiku-latest', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], stream: false, @@ -73,7 +73,7 @@ describe('resource messages', () => { test('countTokens: only required params', async () => { const responsePromise = client.messages.countTokens({ messages: [{ content: 'string', role: 'user' }], - model: 'string', + model: 'claude-3-5-haiku-latest', }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -87,7 +87,7 @@ describe('resource messages', () => { test('countTokens: required and optional params', async () => { const response = await client.messages.countTokens({ messages: [{ content: 'string', role: 'user' }], - model: 'string', + model: 'claude-3-5-haiku-latest', system: [ { text: "Today's date is 2024-06-01.", From 84401b1068a11ae241a03643d32c459d837a82c6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 18:01:21 +0000 Subject: [PATCH 02/11] chore(api): update openapi spec url (#678) --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 3ea9b07f..b1ba6c6a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 21 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic-946022163d75ddc00f49883756b189412098aa07b600ee0890655eebcb3c440c.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic-bda1c6bb3a8f16d4b0a936aa3a7b1618f23d38570547e7ef047a9c95265e6613.yml From 1172430c87ba42acd2e16f4960247fe4003641a5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:39:37 +0000 Subject: [PATCH 03/11] feat(client): send `X-Stainless-Timeout` header (#679) --- src/core.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/core.ts b/src/core.ts index 344bf6ac..67fc51b3 100644 --- a/src/core.ts +++ b/src/core.ts @@ -314,6 +314,7 @@ export abstract class APIClient { options: FinalRequestOptions, { retryCount = 0 }: { retryCount?: number } = {}, ): { req: RequestInit; url: string; timeout: number } { + options = { ...options }; const { method, path, query, headers: headers = {} } = options; const body = @@ -326,9 +327,9 @@ export abstract class APIClient { const url = this.buildURL(path!, query); if ('timeout' in options) validatePositiveInteger('timeout', options.timeout); - const timeout = options.timeout ?? this.timeout; + options.timeout = options.timeout ?? this.timeout; const httpAgent = options.httpAgent ?? this.httpAgent ?? getDefaultAgent(url); - const minAgentTimeout = timeout + 1000; + const minAgentTimeout = options.timeout + 1000; if ( typeof (httpAgent as any)?.options?.timeout === 'number' && minAgentTimeout > ((httpAgent as any).options.timeout ?? 0) @@ -357,7 +358,7 @@ export abstract class APIClient { signal: options.signal ?? null, }; - return { req, url, timeout }; + return { req, url, timeout: options.timeout }; } private buildHeaders({ @@ -385,15 +386,22 @@ export abstract class APIClient { delete reqHeaders['content-type']; } - // Don't set the retry count header if it was already set or removed through default headers or by the - // caller. We check `defaultHeaders` and `headers`, which can contain nulls, instead of `reqHeaders` to - // account for the removal case. + // Don't set theses headers if they were already set or removed through default headers or by the caller. + // We check `defaultHeaders` and `headers`, which can contain nulls, instead of `reqHeaders` to account + // for the removal case. if ( getHeader(defaultHeaders, 'x-stainless-retry-count') === undefined && getHeader(headers, 'x-stainless-retry-count') === undefined ) { reqHeaders['x-stainless-retry-count'] = String(retryCount); } + if ( + getHeader(defaultHeaders, 'x-stainless-timeout') === undefined && + getHeader(headers, 'x-stainless-timeout') === undefined && + options.timeout + ) { + reqHeaders['x-stainless-timeout'] = String(options.timeout); + } this.validateHeaders(reqHeaders, headers); From d4df248ff4eafa15b5f4b21b3da69d1a710052fa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 14:51:24 +0000 Subject: [PATCH 04/11] feat(pagination): avoid fetching when has_more: false (#680) --- src/pagination.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pagination.ts b/src/pagination.ts index ac0d581b..1cf09e2c 100644 --- a/src/pagination.ts +++ b/src/pagination.ts @@ -45,6 +45,14 @@ export class Page extends AbstractPage implements PageResponse return this.data ?? []; } + override hasNextPage() { + if (this.has_more === false) { + return false; + } + + return super.hasNextPage(); + } + // @deprecated Please use `nextPageInfo()` instead nextPageParams(): Partial | null { const info = this.nextPageInfo(); From e369e3d650f2d761c3479935502615cab2a42b8d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 20:09:20 +0000 Subject: [PATCH 05/11] fix: correctly decode multi-byte characters over multiple chunks (#681) --- src/internal/decoders/line.ts | 107 ++++++++++++++++++++++------------ src/streaming.ts | 6 +- tests/streaming.test.ts | 53 ++++++++++++++++- 3 files changed, 126 insertions(+), 40 deletions(-) diff --git a/src/internal/decoders/line.ts b/src/internal/decoders/line.ts index a71f9ea0..f048ed53 100644 --- a/src/internal/decoders/line.ts +++ b/src/internal/decoders/line.ts @@ -13,52 +13,58 @@ export class LineDecoder { static NEWLINE_CHARS = new Set(['\n', '\r']); static NEWLINE_REGEXP = /\r\n|[\n\r]/g; - buffer: string[]; - trailingCR: boolean; + buffer: Uint8Array; + #carriageReturnIndex: number | null; textDecoder: any; // TextDecoder found in browsers; not typed to avoid pulling in either "dom" or "node" types. constructor() { - this.buffer = []; - this.trailingCR = false; + this.buffer = new Uint8Array(); + this.#carriageReturnIndex = null; } decode(chunk: Bytes): string[] { - let text = this.decodeText(chunk); - - if (this.trailingCR) { - text = '\r' + text; - this.trailingCR = false; - } - if (text.endsWith('\r')) { - this.trailingCR = true; - text = text.slice(0, -1); - } - - if (!text) { + if (chunk == null) { return []; } - const trailingNewline = LineDecoder.NEWLINE_CHARS.has(text[text.length - 1] || ''); - let lines = text.split(LineDecoder.NEWLINE_REGEXP); + const binaryChunk = + chunk instanceof ArrayBuffer ? new Uint8Array(chunk) + : typeof chunk === 'string' ? new TextEncoder().encode(chunk) + : chunk; + + let newData = new Uint8Array(this.buffer.length + binaryChunk.length); + newData.set(this.buffer); + newData.set(binaryChunk, this.buffer.length); + this.buffer = newData; + + const lines: string[] = []; + let patternIndex; + while ((patternIndex = findNewlineIndex(this.buffer, this.#carriageReturnIndex)) != null) { + if (patternIndex.carriage && this.#carriageReturnIndex == null) { + // skip until we either get a corresponding `\n`, a new `\r` or nothing + this.#carriageReturnIndex = patternIndex.index; + continue; + } - // if there is a trailing new line then the last entry will be an empty - // string which we don't care about - if (trailingNewline) { - lines.pop(); - } + // we got double \r or \rtext\n + if ( + this.#carriageReturnIndex != null && + (patternIndex.index !== this.#carriageReturnIndex + 1 || patternIndex.carriage) + ) { + lines.push(this.decodeText(this.buffer.slice(0, this.#carriageReturnIndex - 1))); + this.buffer = this.buffer.slice(this.#carriageReturnIndex); + this.#carriageReturnIndex = null; + continue; + } - if (lines.length === 1 && !trailingNewline) { - this.buffer.push(lines[0]!); - return []; - } + const endIndex = + this.#carriageReturnIndex !== null ? patternIndex.preceding - 1 : patternIndex.preceding; - if (this.buffer.length > 0) { - lines = [this.buffer.join('') + lines[0], ...lines.slice(1)]; - this.buffer = []; - } + const line = this.decodeText(this.buffer.slice(0, endIndex)); + lines.push(line); - if (!trailingNewline) { - this.buffer = [lines.pop() || '']; + this.buffer = this.buffer.slice(patternIndex.index); + this.#carriageReturnIndex = null; } return lines; @@ -102,13 +108,38 @@ export class LineDecoder { } flush(): string[] { - if (!this.buffer.length && !this.trailingCR) { + if (!this.buffer.length) { return []; } + return this.decode('\n'); + } +} - const lines = [this.buffer.join('')]; - this.buffer = []; - this.trailingCR = false; - return lines; +/** + * This function searches the buffer for the end patterns, (\r or \n) + * and returns an object with the index preceding the matched newline and the + * index after the newline char. `null` is returned if no new line is found. + * + * ```ts + * findNewLineIndex('abc\ndef') -> { preceding: 2, index: 3 } + * ``` + */ +function findNewlineIndex( + buffer: Uint8Array, + startIndex: number | null, +): { preceding: number; index: number; carriage: boolean } | null { + const newline = 0x0a; // \n + const carriage = 0x0d; // \r + + for (let i = startIndex ?? 0; i < buffer.length; i++) { + if (buffer[i] === newline) { + return { preceding: i, index: i + 1, carriage: false }; + } + + if (buffer[i] === carriage) { + return { preceding: i, index: i + 1, carriage: true }; + } } + + return null; } diff --git a/src/streaming.ts b/src/streaming.ts index ad1b78e6..0fc5a5b6 100644 --- a/src/streaming.ts +++ b/src/streaming.ts @@ -348,13 +348,17 @@ class SSEDecoder { } /** This is an internal helper function that's just used for testing */ -export function _decodeChunks(chunks: string[]): string[] { +export function _decodeChunks(chunks: string[], { flush }: { flush: boolean } = { flush: false }): string[] { const decoder = new LineDecoder(); const lines: string[] = []; for (const chunk of chunks) { lines.push(...decoder.decode(chunk)); } + if (flush) { + lines.push(...decoder.flush()); + } + return lines; } diff --git a/tests/streaming.test.ts b/tests/streaming.test.ts index d82b7484..2bbed27c 100644 --- a/tests/streaming.test.ts +++ b/tests/streaming.test.ts @@ -3,6 +3,7 @@ import { PassThrough } from 'stream'; import assert from 'assert'; import { Stream, _iterSSEMessages, _decodeChunks as decodeChunks } from '@anthropic-ai/sdk/streaming'; import { APIConnectionError } from '@anthropic-ai/sdk/error'; +import { LineDecoder } from '@anthropic-ai/sdk/internal/decoders/line'; describe('line decoder', () => { test('basic', () => { @@ -11,8 +12,8 @@ describe('line decoder', () => { }); test('basic with \\r', () => { - // baz is not included because the line hasn't ended yet expect(decodeChunks(['foo', ' bar\r\nbaz'])).toEqual(['foo bar']); + expect(decodeChunks(['foo', ' bar\r\nbaz'], { flush: true })).toEqual(['foo bar', 'baz']); }); test('trailing new lines', () => { @@ -30,6 +31,56 @@ describe('line decoder', () => { test('escaped new lines with \\r', () => { expect(decodeChunks(['foo', ' bar\\r\\nbaz\n'])).toEqual(['foo bar\\r\\nbaz']); }); + + test('\\r & \\n split across multiple chunks', () => { + expect(decodeChunks(['foo\r', '\n', 'bar'], { flush: true })).toEqual(['foo', 'bar']); + }); + + test('single \\r', () => { + expect(decodeChunks(['foo\r', 'bar'], { flush: true })).toEqual(['foo', 'bar']); + }); + + test('double \\r', () => { + expect(decodeChunks(['foo\r', 'bar\r'], { flush: true })).toEqual(['foo', 'bar']); + expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); + // implementation detail that we don't yield the single \r line until a new \r or \n is encountered + expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: false })).toEqual(['foo']); + }); + + test('double \\r then \\r\\n', () => { + expect(decodeChunks(['foo\r', '\r', '\r', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']); + expect(decodeChunks(['foo\n', '\n', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']); + }); + + test('double newline', () => { + expect(decodeChunks(['foo\n\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); + expect(decodeChunks(['foo', '\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); + expect(decodeChunks(['foo\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); + expect(decodeChunks(['foo', '\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); + }); + + test('multi-byte characters across chunks', () => { + const decoder = new LineDecoder(); + + // bytes taken from the string 'известни' and arbitrarily split + // so that some multi-byte characters span multiple chunks + expect(decoder.decode(new Uint8Array([0xd0]))).toHaveLength(0); + expect(decoder.decode(new Uint8Array([0xb8, 0xd0, 0xb7, 0xd0]))).toHaveLength(0); + expect( + decoder.decode(new Uint8Array([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8])), + ).toHaveLength(0); + + const decoded = decoder.decode(new Uint8Array([0xa])); + expect(decoded).toEqual(['известни']); + }); + + test('flushing trailing newlines', () => { + expect(decodeChunks(['foo\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); + }); + + test('flushing empty buffer', () => { + expect(decodeChunks([], { flush: true })).toEqual([]); + }); }); describe('streaming decoding', () => { From 56d9c7ab269af132d2ac374b6b7b9e5e523e0720 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Feb 2025 22:39:50 +0000 Subject: [PATCH 06/11] fix(client): fix export map for index exports (#684) --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 6fb9c0c4..db237811 100644 --- a/package.json +++ b/package.json @@ -107,17 +107,17 @@ "default": "./dist/index.mjs" }, "./*.mjs": { - "types": "./dist/*.d.ts", - "default": "./dist/*.mjs" + "types": ["./dist/*.d.ts", "./dist/*/index.d.ts"], + "default": ["./dist/*.mjs", "./dist/*/index.mjs"] }, "./*.js": { - "types": "./dist/*.d.ts", - "default": "./dist/*.js" + "types": ["./dist/*.d.ts", "./dist/*/index.d.ts"], + "default": ["./dist/*.js", "./dist/*/index.js"] }, "./*": { - "types": "./dist/*.d.ts", - "require": "./dist/*.js", - "default": "./dist/*.mjs" + "types": ["./dist/*.d.ts", "./dist/*/index.d.ts"], + "require": ["./dist/*.js", "./dist/*/index.js"], + "default": ["./dist/*.mjs", "./dist/*/index.mjs"] } } } From a8862b9d39f688707ecf2142b002aa27a3cbd09b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Feb 2025 10:26:51 +0000 Subject: [PATCH 07/11] chore(internal): add missing return type annotation (#685) --- src/pagination.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pagination.ts b/src/pagination.ts index 1cf09e2c..2541eddb 100644 --- a/src/pagination.ts +++ b/src/pagination.ts @@ -45,7 +45,7 @@ export class Page extends AbstractPage implements PageResponse return this.data ?? []; } - override hasNextPage() { + override hasNextPage(): boolean { if (this.has_more === false) { return false; } From 53669af8507c503dfd109ea34896dd018fbb1fc8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:08:07 +0000 Subject: [PATCH 08/11] fix: optimize sse chunk reading off-by-one error (#686) --- src/internal/decoders/line.ts | 31 +++++++ src/streaming.ts | 48 +--------- tests/internal/decoders/line.test.ts | 128 +++++++++++++++++++++++++++ tests/streaming.test.ts | 81 +---------------- 4 files changed, 161 insertions(+), 127 deletions(-) create mode 100644 tests/internal/decoders/line.test.ts diff --git a/src/internal/decoders/line.ts b/src/internal/decoders/line.ts index f048ed53..14b7f1c4 100644 --- a/src/internal/decoders/line.ts +++ b/src/internal/decoders/line.ts @@ -143,3 +143,34 @@ function findNewlineIndex( return null; } + +export function findDoubleNewlineIndex(buffer: Uint8Array): number { + // This function searches the buffer for the end patterns (\r\r, \n\n, \r\n\r\n) + // and returns the index right after the first occurrence of any pattern, + // or -1 if none of the patterns are found. + const newline = 0x0a; // \n + const carriage = 0x0d; // \r + + for (let i = 0; i < buffer.length - 1; i++) { + if (buffer[i] === newline && buffer[i + 1] === newline) { + // \n\n + return i + 2; + } + if (buffer[i] === carriage && buffer[i + 1] === carriage) { + // \r\r + return i + 2; + } + if ( + buffer[i] === carriage && + buffer[i + 1] === newline && + i + 3 < buffer.length && + buffer[i + 2] === carriage && + buffer[i + 3] === newline + ) { + // \r\n\r\n + return i + 4; + } + } + + return -1; +} diff --git a/src/streaming.ts b/src/streaming.ts index 0fc5a5b6..7029f749 100644 --- a/src/streaming.ts +++ b/src/streaming.ts @@ -1,6 +1,6 @@ import { ReadableStream, type Response } from './_shims/index'; import { AnthropicError } from './error'; -import { LineDecoder } from './internal/decoders/line'; +import { findDoubleNewlineIndex, LineDecoder } from './internal/decoders/line'; import { ReadableStreamToAsyncIterable } from './internal/stream-utils'; import { createResponseHeaders } from './core'; @@ -261,37 +261,6 @@ async function* iterSSEChunks(iterator: AsyncIterableIterator): AsyncGene } } -function findDoubleNewlineIndex(buffer: Uint8Array): number { - // This function searches the buffer for the end patterns (\r\r, \n\n, \r\n\r\n) - // and returns the index right after the first occurrence of any pattern, - // or -1 if none of the patterns are found. - const newline = 0x0a; // \n - const carriage = 0x0d; // \r - - for (let i = 0; i < buffer.length - 2; i++) { - if (buffer[i] === newline && buffer[i + 1] === newline) { - // \n\n - return i + 2; - } - if (buffer[i] === carriage && buffer[i + 1] === carriage) { - // \r\r - return i + 2; - } - if ( - buffer[i] === carriage && - buffer[i + 1] === newline && - i + 3 < buffer.length && - buffer[i + 2] === carriage && - buffer[i + 3] === newline - ) { - // \r\n\r\n - return i + 4; - } - } - - return -1; -} - class SSEDecoder { private data: string[]; private event: string | null; @@ -347,21 +316,6 @@ class SSEDecoder { } } -/** This is an internal helper function that's just used for testing */ -export function _decodeChunks(chunks: string[], { flush }: { flush: boolean } = { flush: false }): string[] { - const decoder = new LineDecoder(); - const lines: string[] = []; - for (const chunk of chunks) { - lines.push(...decoder.decode(chunk)); - } - - if (flush) { - lines.push(...decoder.flush()); - } - - return lines; -} - function partition(str: string, delimiter: string): [string, string, string] { const index = str.indexOf(delimiter); if (index !== -1) { diff --git a/tests/internal/decoders/line.test.ts b/tests/internal/decoders/line.test.ts new file mode 100644 index 00000000..8d288c85 --- /dev/null +++ b/tests/internal/decoders/line.test.ts @@ -0,0 +1,128 @@ +import { findDoubleNewlineIndex, LineDecoder } from '@anthropic-ai/sdk/internal/decoders/line'; + +function decodeChunks(chunks: string[], { flush }: { flush: boolean } = { flush: false }): string[] { + const decoder = new LineDecoder(); + const lines: string[] = []; + for (const chunk of chunks) { + lines.push(...decoder.decode(chunk)); + } + + if (flush) { + lines.push(...decoder.flush()); + } + + return lines; +} + +describe('line decoder', () => { + test('basic', () => { + // baz is not included because the line hasn't ended yet + expect(decodeChunks(['foo', ' bar\nbaz'])).toEqual(['foo bar']); + }); + + test('basic with \\r', () => { + expect(decodeChunks(['foo', ' bar\r\nbaz'])).toEqual(['foo bar']); + expect(decodeChunks(['foo', ' bar\r\nbaz'], { flush: true })).toEqual(['foo bar', 'baz']); + }); + + test('trailing new lines', () => { + expect(decodeChunks(['foo', ' bar', 'baz\n', 'thing\n'])).toEqual(['foo barbaz', 'thing']); + }); + + test('trailing new lines with \\r', () => { + expect(decodeChunks(['foo', ' bar', 'baz\r\n', 'thing\r\n'])).toEqual(['foo barbaz', 'thing']); + }); + + test('escaped new lines', () => { + expect(decodeChunks(['foo', ' bar\\nbaz\n'])).toEqual(['foo bar\\nbaz']); + }); + + test('escaped new lines with \\r', () => { + expect(decodeChunks(['foo', ' bar\\r\\nbaz\n'])).toEqual(['foo bar\\r\\nbaz']); + }); + + test('\\r & \\n split across multiple chunks', () => { + expect(decodeChunks(['foo\r', '\n', 'bar'], { flush: true })).toEqual(['foo', 'bar']); + }); + + test('single \\r', () => { + expect(decodeChunks(['foo\r', 'bar'], { flush: true })).toEqual(['foo', 'bar']); + }); + + test('double \\r', () => { + expect(decodeChunks(['foo\r', 'bar\r'], { flush: true })).toEqual(['foo', 'bar']); + expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); + // implementation detail that we don't yield the single \r line until a new \r or \n is encountered + expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: false })).toEqual(['foo']); + }); + + test('double \\r then \\r\\n', () => { + expect(decodeChunks(['foo\r', '\r', '\r', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']); + expect(decodeChunks(['foo\n', '\n', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']); + }); + + test('double newline', () => { + expect(decodeChunks(['foo\n\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); + expect(decodeChunks(['foo', '\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); + expect(decodeChunks(['foo\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); + expect(decodeChunks(['foo', '\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); + }); + + test('multi-byte characters across chunks', () => { + const decoder = new LineDecoder(); + + // bytes taken from the string 'известни' and arbitrarily split + // so that some multi-byte characters span multiple chunks + expect(decoder.decode(new Uint8Array([0xd0]))).toHaveLength(0); + expect(decoder.decode(new Uint8Array([0xb8, 0xd0, 0xb7, 0xd0]))).toHaveLength(0); + expect( + decoder.decode(new Uint8Array([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8])), + ).toHaveLength(0); + + const decoded = decoder.decode(new Uint8Array([0xa])); + expect(decoded).toEqual(['известни']); + }); + + test('flushing trailing newlines', () => { + expect(decodeChunks(['foo\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); + }); + + test('flushing empty buffer', () => { + expect(decodeChunks([], { flush: true })).toEqual([]); + }); +}); + +describe('findDoubleNewlineIndex', () => { + test('finds \\n\\n', () => { + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\n\nbar'))).toBe(5); + expect(findDoubleNewlineIndex(new TextEncoder().encode('\n\nbar'))).toBe(2); + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\n\n'))).toBe(5); + expect(findDoubleNewlineIndex(new TextEncoder().encode('\n\n'))).toBe(2); + }); + + test('finds \\r\\r', () => { + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\rbar'))).toBe(5); + expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\rbar'))).toBe(2); + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\r'))).toBe(5); + expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\r'))).toBe(2); + }); + + test('finds \\r\\n\\r\\n', () => { + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r\nbar'))).toBe(7); + expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\n\r\nbar'))).toBe(4); + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r\n'))).toBe(7); + expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\n\r\n'))).toBe(4); + }); + + test('returns -1 when no double newline found', () => { + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\nbar'))).toBe(-1); + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\rbar'))).toBe(-1); + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\nbar'))).toBe(-1); + expect(findDoubleNewlineIndex(new TextEncoder().encode(''))).toBe(-1); + }); + + test('handles incomplete patterns', () => { + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r'))).toBe(-1); + expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n'))).toBe(-1); + }); +}); diff --git a/tests/streaming.test.ts b/tests/streaming.test.ts index 2bbed27c..fcd6585c 100644 --- a/tests/streaming.test.ts +++ b/tests/streaming.test.ts @@ -1,87 +1,8 @@ import { Response } from 'node-fetch'; import { PassThrough } from 'stream'; import assert from 'assert'; -import { Stream, _iterSSEMessages, _decodeChunks as decodeChunks } from '@anthropic-ai/sdk/streaming'; +import { Stream, _iterSSEMessages } from '@anthropic-ai/sdk/streaming'; import { APIConnectionError } from '@anthropic-ai/sdk/error'; -import { LineDecoder } from '@anthropic-ai/sdk/internal/decoders/line'; - -describe('line decoder', () => { - test('basic', () => { - // baz is not included because the line hasn't ended yet - expect(decodeChunks(['foo', ' bar\nbaz'])).toEqual(['foo bar']); - }); - - test('basic with \\r', () => { - expect(decodeChunks(['foo', ' bar\r\nbaz'])).toEqual(['foo bar']); - expect(decodeChunks(['foo', ' bar\r\nbaz'], { flush: true })).toEqual(['foo bar', 'baz']); - }); - - test('trailing new lines', () => { - expect(decodeChunks(['foo', ' bar', 'baz\n', 'thing\n'])).toEqual(['foo barbaz', 'thing']); - }); - - test('trailing new lines with \\r', () => { - expect(decodeChunks(['foo', ' bar', 'baz\r\n', 'thing\r\n'])).toEqual(['foo barbaz', 'thing']); - }); - - test('escaped new lines', () => { - expect(decodeChunks(['foo', ' bar\\nbaz\n'])).toEqual(['foo bar\\nbaz']); - }); - - test('escaped new lines with \\r', () => { - expect(decodeChunks(['foo', ' bar\\r\\nbaz\n'])).toEqual(['foo bar\\r\\nbaz']); - }); - - test('\\r & \\n split across multiple chunks', () => { - expect(decodeChunks(['foo\r', '\n', 'bar'], { flush: true })).toEqual(['foo', 'bar']); - }); - - test('single \\r', () => { - expect(decodeChunks(['foo\r', 'bar'], { flush: true })).toEqual(['foo', 'bar']); - }); - - test('double \\r', () => { - expect(decodeChunks(['foo\r', 'bar\r'], { flush: true })).toEqual(['foo', 'bar']); - expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); - // implementation detail that we don't yield the single \r line until a new \r or \n is encountered - expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: false })).toEqual(['foo']); - }); - - test('double \\r then \\r\\n', () => { - expect(decodeChunks(['foo\r', '\r', '\r', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']); - expect(decodeChunks(['foo\n', '\n', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']); - }); - - test('double newline', () => { - expect(decodeChunks(['foo\n\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); - expect(decodeChunks(['foo', '\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); - expect(decodeChunks(['foo\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); - expect(decodeChunks(['foo', '\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']); - }); - - test('multi-byte characters across chunks', () => { - const decoder = new LineDecoder(); - - // bytes taken from the string 'известни' and arbitrarily split - // so that some multi-byte characters span multiple chunks - expect(decoder.decode(new Uint8Array([0xd0]))).toHaveLength(0); - expect(decoder.decode(new Uint8Array([0xb8, 0xd0, 0xb7, 0xd0]))).toHaveLength(0); - expect( - decoder.decode(new Uint8Array([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8])), - ).toHaveLength(0); - - const decoded = decoder.decode(new Uint8Array([0xa])); - expect(decoded).toEqual(['известни']); - }); - - test('flushing trailing newlines', () => { - expect(decodeChunks(['foo\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']); - }); - - test('flushing empty buffer', () => { - expect(decodeChunks([], { flush: true })).toEqual([]); - }); -}); describe('streaming decoding', () => { test('basic', async () => { From 8665946ded8472e892301449569aae30613175fa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:08:01 +0000 Subject: [PATCH 09/11] chore(internal): fix devcontainers setup (#689) --- .devcontainer/Dockerfile | 23 ----------------------- .devcontainer/devcontainer.json | 27 ++++++++++++--------------- 2 files changed, 12 insertions(+), 38 deletions(-) delete mode 100644 .devcontainer/Dockerfile diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 8ea34be9..00000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM debian:bookworm-slim AS stainless - -RUN apt-get update && apt-get install -y \ - nodejs \ - npm \ - yarnpkg \ - && apt-get clean autoclean - -# Ensure UTF-8 encoding -ENV LANG=C.UTF-8 -ENV LC_ALL=C.UTF-8 - -# Yarn -RUN ln -sf /usr/bin/yarnpkg /usr/bin/yarn - -WORKDIR /workspace - -COPY package.json yarn.lock /workspace/ - -RUN yarn install - -COPY . /workspace diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d55fc4d6..763462fa 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,20 +1,17 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/debian { - "name": "Debian", - "build": { - "dockerfile": "Dockerfile" + "name": "Development", + "image": "mcr.microsoft.com/devcontainers/typescript-node:latest", + "features": { + "ghcr.io/devcontainers/features/node:1": {} + }, + "postCreateCommand": "yarn install", + "customizations": { + "vscode": { + "extensions": [ + "esbenp.prettier-vscode" + ] + } } - - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Configure tool-specific properties. - // "customizations": {}, - - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" } From ffab3113ddb042951a35d71e571727f8cce184ee Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 24 Feb 2025 17:59:22 +0000 Subject: [PATCH 10/11] feat(api): add claude-3.7 + support for thinking --- README.md | 33 ++- api.md | 25 ++ packages/vertex-sdk/yarn.lock | 6 - src/core.ts | 25 ++ src/index.ts | 26 ++ src/lib/BetaMessageStream.ts | 26 ++ src/lib/MessageStream.ts | 26 ++ src/resources/beta/beta.ts | 29 ++- src/resources/beta/index.ts | 12 + src/resources/beta/messages/batches.ts | 18 ++ src/resources/beta/messages/index.ts | 12 + src/resources/beta/messages/messages.ts | 226 ++++++++++++++++-- src/resources/index.ts | 13 + src/resources/messages/batches.ts | 18 ++ src/resources/messages/index.ts | 13 + src/resources/messages/messages.ts | 188 ++++++++++++++- tests/api-resources/MessageStream.test.ts | 6 + .../beta/messages/batches.test.ts | 23 +- .../beta/messages/messages.test.ts | 40 ++-- tests/api-resources/completions.test.ts | 4 +- tests/api-resources/messages/batches.test.ts | 23 +- tests/api-resources/messages/messages.test.ts | 40 +--- 22 files changed, 703 insertions(+), 129 deletions(-) diff --git a/README.md b/README.md index fac24b92..cafba323 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -> [!IMPORTANT] +> [!IMPORTANT] > We're actively working on a new alpha version that migrates from `node-fetch` to builtin fetch. -> +> > Please try it out and let us know if you run into any issues! > https://github.com/anthropics/anthropic-sdk-typescript/issues/645 @@ -268,7 +268,16 @@ await client.messages.create({ max_tokens: 1024, messages: [{ role: 'user', cont ### Timeouts -Requests time out after 10 minutes by default. You can configure this with a `timeout` option: +By default requests time out after 10 minutes. However if you have specified a large `max_tokens` value and are +*not* streaming, the default timeout will be calculated dynamically using the formula: +```typescript +const minimum = 10 * 60; +const calculated = (60 * 60 * maxTokens) / 128_000; +return calculated < minimum ? minimum * 1000 : calculated * 1000; +``` +which will result in a timeout up to 60 minutes, scaled by the `max_tokens` parameter, unless overriden at the request or client level. + +You can configure this with a `timeout` option: ```ts @@ -287,6 +296,24 @@ On timeout, an `APIConnectionTimeoutError` is thrown. Note that requests which time out will be [retried twice by default](#retries). +### Long Requests + +> [!IMPORTANT] +> We highly encourage you use the streaming [Messages API](#streaming-responses) for longer running requests. + +We do not recommend setting a large `max_tokens` values without using streaming. +Some networks may drop idle connections after a certain period of time, which +can cause the request to fail or [timeout](#timeouts) without receiving a response from Anthropic. + +This SDK will also throw an error if a non-streaming request is expected to be above roughly 10 minutes long. +Passing `stream: true` or [overriding](#timeouts) the `timeout` option at the client or request level disables this error. + +An expected request latency longer than the [timeout](#timeouts) for a non-streaming request +will result in the client terminating the connection and retrying without receiving a response. + +When supported by the `fetch` implementation, we set a [TCP socket keep-alive](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) +option in order to reduce the impact of idle connection timeouts on some networks. + ## Auto-pagination List methods in the Anthropic API are paginated. diff --git a/api.md b/api.md index 1a3d66bc..8a582f20 100644 --- a/api.md +++ b/api.md @@ -41,6 +41,7 @@ Types: - ImageBlockParam - InputJSONDelta - Message +- MessageCountTokensTool - MessageDeltaEvent - MessageDeltaUsage - MessageParam @@ -58,17 +59,29 @@ Types: - RawMessageStartEvent - RawMessageStopEvent - RawMessageStreamEvent +- RedactedThinkingBlock +- RedactedThinkingBlockParam +- SignatureDelta - TextBlock - TextBlockParam - TextCitation - TextCitationParam - TextDelta +- ThinkingBlock +- ThinkingBlockParam +- ThinkingConfigDisabled +- ThinkingConfigEnabled +- ThinkingConfigParam +- ThinkingDelta - Tool +- ToolBash20250124 - ToolChoice - ToolChoiceAny - ToolChoiceAuto - ToolChoiceTool - ToolResultBlockParam +- ToolTextEditor20250124 +- ToolUnion - ToolUseBlock - ToolUseBlockParam - Usage @@ -175,20 +188,32 @@ Types: - BetaRawMessageStartEvent - BetaRawMessageStopEvent - BetaRawMessageStreamEvent +- BetaRedactedThinkingBlock +- BetaRedactedThinkingBlockParam +- BetaSignatureDelta - BetaTextBlock - BetaTextBlockParam - BetaTextCitation - BetaTextCitationParam - BetaTextDelta +- BetaThinkingBlock +- BetaThinkingBlockParam +- BetaThinkingConfigDisabled +- BetaThinkingConfigEnabled +- BetaThinkingConfigParam +- BetaThinkingDelta - BetaTool - BetaToolBash20241022 +- BetaToolBash20250124 - BetaToolChoice - BetaToolChoiceAny - BetaToolChoiceAuto - BetaToolChoiceTool - BetaToolComputerUse20241022 +- BetaToolComputerUse20250124 - BetaToolResultBlockParam - BetaToolTextEditor20241022 +- BetaToolTextEditor20250124 - BetaToolUnion - BetaToolUseBlock - BetaToolUseBlockParam diff --git a/packages/vertex-sdk/yarn.lock b/packages/vertex-sdk/yarn.lock index a4394b8a..cf6ca9c5 100644 --- a/packages/vertex-sdk/yarn.lock +++ b/packages/vertex-sdk/yarn.lock @@ -27,7 +27,6 @@ form-data-encoder "1.7.2" formdata-node "^4.3.2" node-fetch "^2.6.7" - web-streams-polyfill "^3.2.1" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": version "7.23.5" @@ -3257,11 +3256,6 @@ web-streams-polyfill@4.0.0-beta.3: resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== -web-streams-polyfill@^3.2.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz#32e26522e05128203a7de59519be3c648004343b" - integrity sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" diff --git a/src/core.ts b/src/core.ts index 67fc51b3..eac9d602 100644 --- a/src/core.ts +++ b/src/core.ts @@ -408,6 +408,18 @@ export abstract class APIClient { return reqHeaders; } + _calculateNonstreamingTimeout(maxTokens: number): number { + const defaultTimeout = 10 * 60; + const expectedTimeout = (60 * 60 * maxTokens) / 128_000; + if (expectedTimeout > defaultTimeout) { + throw new AnthropicError( + 'Streaming is strongly recommended for operations that may take longer than 10 minutes. ' + + 'See https://github.com/anthropics/anthropic-sdk-python#streaming-responses for more details', + ); + } + return defaultTimeout * 1000; + } + /** * Used as a callback for mutating the given `FinalRequestOptions` object. */ @@ -574,10 +586,23 @@ export abstract class APIClient { fetchOptions.method = fetchOptions.method.toUpperCase(); } + // turn on TCP keep-alive for the sockets, if the runtime supports it + const socketKeepAliveInterval = 60 * 1000; + const keepAliveTimeout = setTimeout(() => { + if (fetchOptions && (fetchOptions as any)?.agent?.sockets) { + for (const socket of Object.values((fetchOptions as any)?.agent?.sockets).flat()) { + if ((socket as any)?.setKeepAlive) { + (socket as any).setKeepAlive(true, socketKeepAliveInterval); + } + } + } + }, socketKeepAliveInterval); + return ( // use undefined this binding; fetch errors if bound to something else in browser/cloudflare this.fetch.call(undefined, url, fetchOptions).finally(() => { clearTimeout(timeout); + clearTimeout(keepAliveTimeout); }) ); } diff --git a/src/index.ts b/src/index.ts index 0872e7a7..90965c8f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,6 +53,7 @@ import { InputJSONDelta, Message, MessageCountTokensParams, + MessageCountTokensTool, MessageCreateParams, MessageCreateParamsNonStreaming, MessageCreateParamsStreaming, @@ -75,17 +76,29 @@ import { RawMessageStartEvent, RawMessageStopEvent, RawMessageStreamEvent, + RedactedThinkingBlock, + RedactedThinkingBlockParam, + SignatureDelta, TextBlock, TextBlockParam, TextCitation, TextCitationParam, TextDelta, + ThinkingBlock, + ThinkingBlockParam, + ThinkingConfigDisabled, + ThinkingConfigEnabled, + ThinkingConfigParam, + ThinkingDelta, Tool, + ToolBash20250124, ToolChoice, ToolChoiceAny, ToolChoiceAuto, ToolChoiceTool, ToolResultBlockParam, + ToolTextEditor20250124, + ToolUnion, ToolUseBlock, ToolUseBlockParam, Usage, @@ -354,6 +367,7 @@ export declare namespace Anthropic { type ImageBlockParam as ImageBlockParam, type InputJSONDelta as InputJSONDelta, type Message as Message, + type MessageCountTokensTool as MessageCountTokensTool, type MessageDeltaEvent as MessageDeltaEvent, type MessageDeltaUsage as MessageDeltaUsage, type MessageParam as MessageParam, @@ -371,17 +385,29 @@ export declare namespace Anthropic { type RawMessageStartEvent as RawMessageStartEvent, type RawMessageStopEvent as RawMessageStopEvent, type RawMessageStreamEvent as RawMessageStreamEvent, + type RedactedThinkingBlock as RedactedThinkingBlock, + type RedactedThinkingBlockParam as RedactedThinkingBlockParam, + type SignatureDelta as SignatureDelta, type TextBlock as TextBlock, type TextBlockParam as TextBlockParam, type TextCitation as TextCitation, type TextCitationParam as TextCitationParam, type TextDelta as TextDelta, + type ThinkingBlock as ThinkingBlock, + type ThinkingBlockParam as ThinkingBlockParam, + type ThinkingConfigDisabled as ThinkingConfigDisabled, + type ThinkingConfigEnabled as ThinkingConfigEnabled, + type ThinkingConfigParam as ThinkingConfigParam, + type ThinkingDelta as ThinkingDelta, type Tool as Tool, + type ToolBash20250124 as ToolBash20250124, type ToolChoice as ToolChoice, type ToolChoiceAny as ToolChoiceAny, type ToolChoiceAuto as ToolChoiceAuto, type ToolChoiceTool as ToolChoiceTool, type ToolResultBlockParam as ToolResultBlockParam, + type ToolTextEditor20250124 as ToolTextEditor20250124, + type ToolUnion as ToolUnion, type ToolUseBlock as ToolUseBlock, type ToolUseBlockParam as ToolUseBlockParam, type Usage as Usage, diff --git a/src/lib/BetaMessageStream.ts b/src/lib/BetaMessageStream.ts index 5de54f06..fd096926 100644 --- a/src/lib/BetaMessageStream.ts +++ b/src/lib/BetaMessageStream.ts @@ -21,6 +21,8 @@ export interface MessageStreamEvents { text: (textDelta: string, textSnapshot: string) => void; citation: (citation: BetaTextCitation, citationsSnapshot: BetaTextCitation[]) => void; inputJson: (partialJson: string, jsonSnapshot: unknown) => void; + thinking: (thinkingDelta: string, thinkingSnapshot: string) => void; + signature: (signature: string) => void; message: (message: BetaMessage) => void; contentBlock: (content: BetaContentBlock) => void; finalMessage: (message: BetaMessage) => void; @@ -434,6 +436,18 @@ export class BetaMessageStream implements AsyncIterable } break; } + case 'thinking_delta': { + if (content.type === 'thinking') { + this._emit('thinking', event.delta.thinking, content.thinking); + } + break; + } + case 'signature_delta': { + if (content.type === 'thinking') { + this._emit('signature', content.signature); + } + break; + } default: checkNever(event.delta); } @@ -557,6 +571,18 @@ export class BetaMessageStream implements AsyncIterable } break; } + case 'thinking_delta': { + if (snapshotContent?.type === 'thinking') { + snapshotContent.thinking += event.delta.thinking; + } + break; + } + case 'signature_delta': { + if (snapshotContent?.type === 'thinking') { + snapshotContent.signature = event.delta.signature; + } + break; + } default: checkNever(event.delta); } diff --git a/src/lib/MessageStream.ts b/src/lib/MessageStream.ts index 4ce3a382..712a34ff 100644 --- a/src/lib/MessageStream.ts +++ b/src/lib/MessageStream.ts @@ -21,6 +21,8 @@ export interface MessageStreamEvents { text: (textDelta: string, textSnapshot: string) => void; citation: (citation: TextCitation, citationsSnapshot: TextCitation[]) => void; inputJson: (partialJson: string, jsonSnapshot: unknown) => void; + thinking: (thinkingDelta: string, thinkingSnapshot: string) => void; + signature: (signature: string) => void; message: (message: Message) => void; contentBlock: (content: ContentBlock) => void; finalMessage: (message: Message) => void; @@ -434,6 +436,18 @@ export class MessageStream implements AsyncIterable { } break; } + case 'thinking_delta': { + if (content.type === 'thinking') { + this._emit('thinking', event.delta.thinking, content.thinking); + } + break; + } + case 'signature_delta': { + if (content.type === 'thinking') { + this._emit('signature', content.signature); + } + break; + } default: checkNever(event.delta); } @@ -557,6 +571,18 @@ export class MessageStream implements AsyncIterable { } break; } + case 'thinking_delta': { + if (snapshotContent?.type === 'thinking') { + snapshotContent.thinking += event.delta.thinking; + } + break; + } + case 'signature_delta': { + if (snapshotContent?.type === 'thinking') { + snapshotContent.signature = event.delta.signature; + } + break; + } default: checkNever(event.delta); } diff --git a/src/resources/beta/beta.ts b/src/resources/beta/beta.ts index a1f3f0d4..69a716b5 100644 --- a/src/resources/beta/beta.ts +++ b/src/resources/beta/beta.ts @@ -35,20 +35,32 @@ import { BetaRawMessageStartEvent, BetaRawMessageStopEvent, BetaRawMessageStreamEvent, + BetaRedactedThinkingBlock, + BetaRedactedThinkingBlockParam, + BetaSignatureDelta, BetaTextBlock, BetaTextBlockParam, BetaTextCitation, BetaTextCitationParam, BetaTextDelta, + BetaThinkingBlock, + BetaThinkingBlockParam, + BetaThinkingConfigDisabled, + BetaThinkingConfigEnabled, + BetaThinkingConfigParam, + BetaThinkingDelta, BetaTool, BetaToolBash20241022, + BetaToolBash20250124, BetaToolChoice, BetaToolChoiceAny, BetaToolChoiceAuto, BetaToolChoiceTool, BetaToolComputerUse20241022, + BetaToolComputerUse20250124, BetaToolResultBlockParam, BetaToolTextEditor20241022, + BetaToolTextEditor20250124, BetaToolUnion, BetaToolUseBlock, BetaToolUseBlockParam, @@ -70,8 +82,11 @@ export type AnthropicBeta = | 'message-batches-2024-09-24' | 'prompt-caching-2024-07-31' | 'computer-use-2024-10-22' + | 'computer-use-2025-01-24' | 'pdfs-2024-09-25' - | 'token-counting-2024-11-01'; + | 'token-counting-2024-11-01' + | 'token-efficient-tools-2025-02-19' + | 'output-128k-2025-02-19'; export interface BetaAPIError { message: string; @@ -203,20 +218,32 @@ export declare namespace Beta { type BetaRawMessageStartEvent as BetaRawMessageStartEvent, type BetaRawMessageStopEvent as BetaRawMessageStopEvent, type BetaRawMessageStreamEvent as BetaRawMessageStreamEvent, + type BetaRedactedThinkingBlock as BetaRedactedThinkingBlock, + type BetaRedactedThinkingBlockParam as BetaRedactedThinkingBlockParam, + type BetaSignatureDelta as BetaSignatureDelta, type BetaTextBlock as BetaTextBlock, type BetaTextBlockParam as BetaTextBlockParam, type BetaTextCitation as BetaTextCitation, type BetaTextCitationParam as BetaTextCitationParam, type BetaTextDelta as BetaTextDelta, + type BetaThinkingBlock as BetaThinkingBlock, + type BetaThinkingBlockParam as BetaThinkingBlockParam, + type BetaThinkingConfigDisabled as BetaThinkingConfigDisabled, + type BetaThinkingConfigEnabled as BetaThinkingConfigEnabled, + type BetaThinkingConfigParam as BetaThinkingConfigParam, + type BetaThinkingDelta as BetaThinkingDelta, type BetaTool as BetaTool, type BetaToolBash20241022 as BetaToolBash20241022, + type BetaToolBash20250124 as BetaToolBash20250124, type BetaToolChoice as BetaToolChoice, type BetaToolChoiceAny as BetaToolChoiceAny, type BetaToolChoiceAuto as BetaToolChoiceAuto, type BetaToolChoiceTool as BetaToolChoiceTool, type BetaToolComputerUse20241022 as BetaToolComputerUse20241022, + type BetaToolComputerUse20250124 as BetaToolComputerUse20250124, type BetaToolResultBlockParam as BetaToolResultBlockParam, type BetaToolTextEditor20241022 as BetaToolTextEditor20241022, + type BetaToolTextEditor20250124 as BetaToolTextEditor20250124, type BetaToolUnion as BetaToolUnion, type BetaToolUseBlock as BetaToolUseBlock, type BetaToolUseBlockParam as BetaToolUseBlockParam, diff --git a/src/resources/beta/index.ts b/src/resources/beta/index.ts index 743f9c42..6fd387b7 100644 --- a/src/resources/beta/index.ts +++ b/src/resources/beta/index.ts @@ -48,20 +48,32 @@ export { type BetaRawMessageStartEvent, type BetaRawMessageStopEvent, type BetaRawMessageStreamEvent, + type BetaRedactedThinkingBlock, + type BetaRedactedThinkingBlockParam, + type BetaSignatureDelta, type BetaTextBlock, type BetaTextBlockParam, type BetaTextCitation, type BetaTextCitationParam, type BetaTextDelta, + type BetaThinkingBlock, + type BetaThinkingBlockParam, + type BetaThinkingConfigDisabled, + type BetaThinkingConfigEnabled, + type BetaThinkingConfigParam, + type BetaThinkingDelta, type BetaTool, type BetaToolBash20241022, + type BetaToolBash20250124, type BetaToolChoice, type BetaToolChoiceAny, type BetaToolChoiceAuto, type BetaToolChoiceTool, type BetaToolComputerUse20241022, + type BetaToolComputerUse20250124, type BetaToolResultBlockParam, type BetaToolTextEditor20241022, + type BetaToolTextEditor20250124, type BetaToolUnion, type BetaToolUseBlock, type BetaToolUseBlockParam, diff --git a/src/resources/beta/messages/batches.ts b/src/resources/beta/messages/batches.ts index 6370119c..b56143fd 100644 --- a/src/resources/beta/messages/batches.ts +++ b/src/resources/beta/messages/batches.ts @@ -16,6 +16,9 @@ export class Batches extends APIResource { * The Message Batches API can be used to process multiple Messages API requests at * once. Once a Message Batch is created, it begins processing immediately. Batches * can take up to 24 hours to complete. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ create(params: BatchCreateParams, options?: Core.RequestOptions): Core.APIPromise { const { betas, ...body } = params; @@ -33,6 +36,9 @@ export class Batches extends APIResource { * This endpoint is idempotent and can be used to poll for Message Batch * completion. To access the results of a Message Batch, make a request to the * `results_url` field in the response. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ retrieve( messageBatchId: string, @@ -61,6 +67,9 @@ export class Batches extends APIResource { /** * List all Message Batches within a Workspace. Most recently created batches are * returned first. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ list( params?: BatchListParams, @@ -90,6 +99,9 @@ export class Batches extends APIResource { * * Message Batches can only be deleted once they've finished processing. If you'd * like to delete an in-progress batch, you must first cancel it. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ delete( messageBatchId: string, @@ -125,6 +137,9 @@ export class Batches extends APIResource { * which requests were canceled, check the individual results within the batch. * Note that cancellation may not result in any canceled requests if they were * non-interruptible. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ cancel( messageBatchId: string, @@ -156,6 +171,9 @@ export class Batches extends APIResource { * Each line in the file is a JSON object containing the result of a single request * in the Message Batch. Results are not guaranteed to be in the same order as * requests. Use the `custom_id` field to match results to requests. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ async results( messageBatchId: string, diff --git a/src/resources/beta/messages/index.ts b/src/resources/beta/messages/index.ts index 51a0f935..c2673e8b 100644 --- a/src/resources/beta/messages/index.ts +++ b/src/resources/beta/messages/index.ts @@ -51,20 +51,32 @@ export { type BetaRawMessageStartEvent, type BetaRawMessageStopEvent, type BetaRawMessageStreamEvent, + type BetaRedactedThinkingBlock, + type BetaRedactedThinkingBlockParam, + type BetaSignatureDelta, type BetaTextBlock, type BetaTextBlockParam, type BetaTextCitation, type BetaTextCitationParam, type BetaTextDelta, + type BetaThinkingBlock, + type BetaThinkingBlockParam, + type BetaThinkingConfigDisabled, + type BetaThinkingConfigEnabled, + type BetaThinkingConfigParam, + type BetaThinkingDelta, type BetaTool, type BetaToolBash20241022, + type BetaToolBash20250124, type BetaToolChoice, type BetaToolChoiceAny, type BetaToolChoiceAuto, type BetaToolChoiceTool, type BetaToolComputerUse20241022, + type BetaToolComputerUse20250124, type BetaToolResultBlockParam, type BetaToolTextEditor20241022, + type BetaToolTextEditor20250124, type BetaToolUnion, type BetaToolUseBlock, type BetaToolUseBlockParam, diff --git a/src/resources/beta/messages/messages.ts b/src/resources/beta/messages/messages.ts index 3b35717a..32587bf2 100644 --- a/src/resources/beta/messages/messages.ts +++ b/src/resources/beta/messages/messages.ts @@ -52,6 +52,8 @@ export class Messages extends APIResource { * * The Messages API can be used for either single queries or stateless multi-turn * conversations. + * + * Learn more about the Messages API in our [user guide](/en/docs/initial-setup) */ create(params: MessageCreateParamsNonStreaming, options?: Core.RequestOptions): APIPromise; create( @@ -78,7 +80,9 @@ export class Messages extends APIResource { return this._client.post('/v1/messages?beta=true', { body, - timeout: (this._client as any)._options.timeout ?? 600000, + timeout: + (this._client as any)._options.timeout ?? + (body.stream ? 600000 : this._client._calculateNonstreamingTimeout(body.max_tokens)), ...options, headers: { ...(betas?.toString() != null ? { 'anthropic-beta': betas?.toString() } : undefined), @@ -100,6 +104,9 @@ export class Messages extends APIResource { * * The Token Count API can be used to count the number of tokens in a Message, * including tools, images, and documents, without creating it. + * + * Learn more about token counting in our + * [user guide](/en/docs/build-with-claude/token-counting) */ countTokens( params: MessageCountTokensParams, @@ -239,14 +246,20 @@ export interface BetaCitationsDelta { type: 'citations_delta'; } -export type BetaContentBlock = BetaTextBlock | BetaToolUseBlock; +export type BetaContentBlock = + | BetaTextBlock + | BetaToolUseBlock + | BetaThinkingBlock + | BetaRedactedThinkingBlock; export type BetaContentBlockParam = | BetaTextBlockParam | BetaImageBlockParam | BetaToolUseBlockParam | BetaToolResultBlockParam - | BetaBase64PDFBlock; + | BetaBase64PDFBlock + | BetaThinkingBlockParam + | BetaRedactedThinkingBlockParam; export interface BetaContentBlockSource { content: string | Array; @@ -381,6 +394,9 @@ export interface BetaMessage { * * For example, `output_tokens` will be non-zero, even for an empty string response * from Claude. + * + * Total input tokens in a request is the summation of `input_tokens`, + * `cache_creation_input_tokens`, and `cache_read_input_tokens`. */ usage: BetaUsage; } @@ -426,7 +442,7 @@ export interface BetaPlainTextSource { } export interface BetaRawContentBlockDeltaEvent { - delta: BetaTextDelta | BetaInputJSONDelta | BetaCitationsDelta; + delta: BetaTextDelta | BetaInputJSONDelta | BetaCitationsDelta | BetaThinkingDelta | BetaSignatureDelta; index: number; @@ -434,7 +450,7 @@ export interface BetaRawContentBlockDeltaEvent { } export interface BetaRawContentBlockStartEvent { - content_block: BetaTextBlock | BetaToolUseBlock; + content_block: BetaTextBlock | BetaToolUseBlock | BetaThinkingBlock | BetaRedactedThinkingBlock; index: number; @@ -465,6 +481,9 @@ export interface BetaRawMessageDeltaEvent { * * For example, `output_tokens` will be non-zero, even for an empty string response * from Claude. + * + * Total input tokens in a request is the summation of `input_tokens`, + * `cache_creation_input_tokens`, and `cache_read_input_tokens`. */ usage: BetaMessageDeltaUsage; } @@ -495,6 +514,24 @@ export type BetaRawMessageStreamEvent = | BetaRawContentBlockDeltaEvent | BetaRawContentBlockStopEvent; +export interface BetaRedactedThinkingBlock { + data: string; + + type: 'redacted_thinking'; +} + +export interface BetaRedactedThinkingBlockParam { + data: string; + + type: 'redacted_thinking'; +} + +export interface BetaSignatureDelta { + signature: string; + + type: 'signature_delta'; +} + export interface BetaTextBlock { /** * Citations supporting the text block. @@ -536,9 +573,65 @@ export interface BetaTextDelta { type: 'text_delta'; } +export interface BetaThinkingBlock { + signature: string; + + thinking: string; + + type: 'thinking'; +} + +export interface BetaThinkingBlockParam { + signature: string; + + thinking: string; + + type: 'thinking'; +} + +export interface BetaThinkingConfigDisabled { + type: 'disabled'; +} + +export interface BetaThinkingConfigEnabled { + /** + * Determines how many tokens Claude can use for its internal reasoning process. + * Larger budgets can enable more thorough analysis for complex problems, improving + * response quality. + * + * Must be ≥1024 and less than `max_tokens`. + * + * See + * [extended thinking](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) + * for details. + */ + budget_tokens: number; + + type: 'enabled'; +} + +/** + * Configuration for enabling Claude's extended thinking. + * + * When enabled, responses include `thinking` content blocks showing Claude's + * thinking process before the final answer. Requires a minimum budget of 1,024 + * tokens and counts towards your `max_tokens` limit. + * + * See + * [extended thinking](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) + * for details. + */ +export type BetaThinkingConfigParam = BetaThinkingConfigEnabled | BetaThinkingConfigDisabled; + +export interface BetaThinkingDelta { + thinking: string; + + type: 'thinking_delta'; +} + export interface BetaTool { /** - * [JSON schema](https://json-schema.org/) for this tool's input. + * [JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. * * This defines the shape of the `input` that your tool accepts and that the model * will produce. @@ -569,7 +662,7 @@ export interface BetaTool { export namespace BetaTool { /** - * [JSON schema](https://json-schema.org/) for this tool's input. + * [JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. * * This defines the shape of the `input` that your tool accepts and that the model * will produce. @@ -595,6 +688,19 @@ export interface BetaToolBash20241022 { cache_control?: BetaCacheControlEphemeral | null; } +export interface BetaToolBash20250124 { + /** + * Name of the tool. + * + * This is how the tool will be called by the model and in tool_use blocks. + */ + name: 'bash'; + + type: 'bash_20250124'; + + cache_control?: BetaCacheControlEphemeral | null; +} + /** * How the model should use the provided tools. The model can use a specific tool, * any available tool, or decide by itself. @@ -679,6 +785,34 @@ export interface BetaToolComputerUse20241022 { display_number?: number | null; } +export interface BetaToolComputerUse20250124 { + /** + * The height of the display in pixels. + */ + display_height_px: number; + + /** + * The width of the display in pixels. + */ + display_width_px: number; + + /** + * Name of the tool. + * + * This is how the tool will be called by the model and in tool_use blocks. + */ + name: 'computer'; + + type: 'computer_20250124'; + + cache_control?: BetaCacheControlEphemeral | null; + + /** + * The X11 display number (e.g. 0, 1) for the display. + */ + display_number?: number | null; +} + export interface BetaToolResultBlockParam { tool_use_id: string; @@ -704,11 +838,27 @@ export interface BetaToolTextEditor20241022 { cache_control?: BetaCacheControlEphemeral | null; } +export interface BetaToolTextEditor20250124 { + /** + * Name of the tool. + * + * This is how the tool will be called by the model and in tool_use blocks. + */ + name: 'str_replace_editor'; + + type: 'text_editor_20250124'; + + cache_control?: BetaCacheControlEphemeral | null; +} + export type BetaToolUnion = - | BetaTool | BetaToolComputerUse20241022 | BetaToolBash20241022 - | BetaToolTextEditor20241022; + | BetaToolTextEditor20241022 + | BetaToolComputerUse20250124 + | BetaToolBash20250124 + | BetaToolTextEditor20250124 + | BetaTool; export interface BetaToolUseBlock { id: string; @@ -913,6 +1063,19 @@ export interface MessageCreateParamsBase { */ temperature?: number; + /** + * Body param: Configuration for enabling Claude's extended thinking. + * + * When enabled, responses include `thinking` content blocks showing Claude's + * thinking process before the final answer. Requires a minimum budget of 1,024 + * tokens and counts towards your `max_tokens` limit. + * + * See + * [extended thinking](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) + * for details. + */ + thinking?: BetaThinkingConfigParam; + /** * Body param: How the model should use the provided tools. The model can use a * specific tool, any available tool, or decide by itself. @@ -931,8 +1094,9 @@ export interface MessageCreateParamsBase { * * - `name`: Name of the tool. * - `description`: Optional, but strongly-recommended description of the tool. - * - `input_schema`: [JSON schema](https://json-schema.org/) for the tool `input` - * shape that the model will produce in `tool_use` output content blocks. + * - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the + * tool `input` shape that the model will produce in `tool_use` output content + * blocks. * * For example, if you defined `tools` as: * @@ -1155,6 +1319,19 @@ export interface MessageCountTokensParams { */ system?: string | Array; + /** + * Body param: Configuration for enabling Claude's extended thinking. + * + * When enabled, responses include `thinking` content blocks showing Claude's + * thinking process before the final answer. Requires a minimum budget of 1,024 + * tokens and counts towards your `max_tokens` limit. + * + * See + * [extended thinking](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) + * for details. + */ + thinking?: BetaThinkingConfigParam; + /** * Body param: How the model should use the provided tools. The model can use a * specific tool, any available tool, or decide by itself. @@ -1173,8 +1350,9 @@ export interface MessageCountTokensParams { * * - `name`: Name of the tool. * - `description`: Optional, but strongly-recommended description of the tool. - * - `input_schema`: [JSON schema](https://json-schema.org/) for the tool `input` - * shape that the model will produce in `tool_use` output content blocks. + * - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the + * tool `input` shape that the model will produce in `tool_use` output content + * blocks. * * For example, if you defined `tools` as: * @@ -1231,7 +1409,15 @@ export interface MessageCountTokensParams { * * See our [guide](https://docs.anthropic.com/en/docs/tool-use) for more details. */ - tools?: Array; + tools?: Array< + | BetaToolComputerUse20241022 + | BetaToolBash20241022 + | BetaToolTextEditor20241022 + | BetaToolComputerUse20250124 + | BetaToolBash20250124 + | BetaToolTextEditor20250124 + | BetaTool + >; /** * Header param: Optional header to specify the beta version(s) you want to use. @@ -1274,20 +1460,32 @@ export declare namespace Messages { type BetaRawMessageStartEvent as BetaRawMessageStartEvent, type BetaRawMessageStopEvent as BetaRawMessageStopEvent, type BetaRawMessageStreamEvent as BetaRawMessageStreamEvent, + type BetaRedactedThinkingBlock as BetaRedactedThinkingBlock, + type BetaRedactedThinkingBlockParam as BetaRedactedThinkingBlockParam, + type BetaSignatureDelta as BetaSignatureDelta, type BetaTextBlock as BetaTextBlock, type BetaTextBlockParam as BetaTextBlockParam, type BetaTextCitation as BetaTextCitation, type BetaTextCitationParam as BetaTextCitationParam, type BetaTextDelta as BetaTextDelta, + type BetaThinkingBlock as BetaThinkingBlock, + type BetaThinkingBlockParam as BetaThinkingBlockParam, + type BetaThinkingConfigDisabled as BetaThinkingConfigDisabled, + type BetaThinkingConfigEnabled as BetaThinkingConfigEnabled, + type BetaThinkingConfigParam as BetaThinkingConfigParam, + type BetaThinkingDelta as BetaThinkingDelta, type BetaTool as BetaTool, type BetaToolBash20241022 as BetaToolBash20241022, + type BetaToolBash20250124 as BetaToolBash20250124, type BetaToolChoice as BetaToolChoice, type BetaToolChoiceAny as BetaToolChoiceAny, type BetaToolChoiceAuto as BetaToolChoiceAuto, type BetaToolChoiceTool as BetaToolChoiceTool, type BetaToolComputerUse20241022 as BetaToolComputerUse20241022, + type BetaToolComputerUse20250124 as BetaToolComputerUse20250124, type BetaToolResultBlockParam as BetaToolResultBlockParam, type BetaToolTextEditor20241022 as BetaToolTextEditor20241022, + type BetaToolTextEditor20250124 as BetaToolTextEditor20250124, type BetaToolUnion as BetaToolUnion, type BetaToolUseBlock as BetaToolUseBlock, type BetaToolUseBlockParam as BetaToolUseBlockParam, diff --git a/src/resources/index.ts b/src/resources/index.ts index 3d4b385a..c5be633b 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -47,6 +47,7 @@ export { type InputJsonDelta, type InputJSONDelta, type Message, + type MessageCountTokensTool, type MessageDeltaEvent, type MessageDeltaUsage, type MessageParam, @@ -65,17 +66,29 @@ export { type RawMessageStartEvent, type RawMessageStopEvent, type RawMessageStreamEvent, + type RedactedThinkingBlock, + type RedactedThinkingBlockParam, + type SignatureDelta, type TextBlock, type TextBlockParam, type TextCitation, type TextCitationParam, type TextDelta, + type ThinkingBlock, + type ThinkingBlockParam, + type ThinkingConfigDisabled, + type ThinkingConfigEnabled, + type ThinkingConfigParam, + type ThinkingDelta, type Tool, + type ToolBash20250124, type ToolChoice, type ToolChoiceAny, type ToolChoiceAuto, type ToolChoiceTool, type ToolResultBlockParam, + type ToolTextEditor20250124, + type ToolUnion, type ToolUseBlock, type ToolUseBlockParam, type Usage, diff --git a/src/resources/messages/batches.ts b/src/resources/messages/batches.ts index 46bc64ed..3d8b889c 100644 --- a/src/resources/messages/batches.ts +++ b/src/resources/messages/batches.ts @@ -16,6 +16,9 @@ export class Batches extends APIResource { * The Message Batches API can be used to process multiple Messages API requests at * once. Once a Message Batch is created, it begins processing immediately. Batches * can take up to 24 hours to complete. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ create(body: BatchCreateParams, options?: Core.RequestOptions): Core.APIPromise { return this._client.post('/v1/messages/batches', { body, ...options }); @@ -25,6 +28,9 @@ export class Batches extends APIResource { * This endpoint is idempotent and can be used to poll for Message Batch * completion. To access the results of a Message Batch, make a request to the * `results_url` field in the response. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ retrieve(messageBatchId: string, options?: Core.RequestOptions): Core.APIPromise { return this._client.get(`/v1/messages/batches/${messageBatchId}`, options); @@ -33,6 +39,9 @@ export class Batches extends APIResource { /** * List all Message Batches within a Workspace. Most recently created batches are * returned first. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ list( query?: BatchListParams, @@ -54,6 +63,9 @@ export class Batches extends APIResource { * * Message Batches can only be deleted once they've finished processing. If you'd * like to delete an in-progress batch, you must first cancel it. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ delete(messageBatchId: string, options?: Core.RequestOptions): Core.APIPromise { return this._client.delete(`/v1/messages/batches/${messageBatchId}`, options); @@ -69,6 +81,9 @@ export class Batches extends APIResource { * which requests were canceled, check the individual results within the batch. * Note that cancellation may not result in any canceled requests if they were * non-interruptible. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ cancel(messageBatchId: string, options?: Core.RequestOptions): Core.APIPromise { return this._client.post(`/v1/messages/batches/${messageBatchId}/cancel`, options); @@ -80,6 +95,9 @@ export class Batches extends APIResource { * Each line in the file is a JSON object containing the result of a single request * in the Message Batch. Results are not guaranteed to be in the same order as * requests. Use the `custom_id` field to match results to requests. + * + * Learn more about the Message Batches API in our + * [user guide](/en/docs/build-with-claude/batch-processing) */ async results( messageBatchId: string, diff --git a/src/resources/messages/index.ts b/src/resources/messages/index.ts index 2f7a0a7f..9b629ca8 100644 --- a/src/resources/messages/index.ts +++ b/src/resources/messages/index.ts @@ -38,6 +38,7 @@ export { type ImageBlockParam, type InputJSONDelta, type Message, + type MessageCountTokensTool, type MessageDeltaEvent, type MessageDeltaUsage, type MessageParam, @@ -55,17 +56,29 @@ export { type RawMessageStartEvent, type RawMessageStopEvent, type RawMessageStreamEvent, + type RedactedThinkingBlock, + type RedactedThinkingBlockParam, + type SignatureDelta, type TextBlock, type TextBlockParam, type TextCitation, type TextCitationParam, type TextDelta, + type ThinkingBlock, + type ThinkingBlockParam, + type ThinkingConfigDisabled, + type ThinkingConfigEnabled, + type ThinkingConfigParam, + type ThinkingDelta, type Tool, + type ToolBash20250124, type ToolChoice, type ToolChoiceAny, type ToolChoiceAuto, type ToolChoiceTool, type ToolResultBlockParam, + type ToolTextEditor20250124, + type ToolUnion, type ToolUseBlock, type ToolUseBlockParam, type Usage, diff --git a/src/resources/messages/messages.ts b/src/resources/messages/messages.ts index b6c53e05..f8466100 100644 --- a/src/resources/messages/messages.ts +++ b/src/resources/messages/messages.ts @@ -34,6 +34,8 @@ export class Messages extends APIResource { * * The Messages API can be used for either single queries or stateless multi-turn * conversations. + * + * Learn more about the Messages API in our [user guide](/en/docs/initial-setup) */ create(body: MessageCreateParamsNonStreaming, options?: Core.RequestOptions): APIPromise; create( @@ -57,7 +59,9 @@ export class Messages extends APIResource { } return this._client.post('/v1/messages', { body, - timeout: (this._client as any)._options.timeout ?? 600000, + timeout: + (this._client as any)._options.timeout ?? + (body.stream ? 600000 : this._client._calculateNonstreamingTimeout(body.max_tokens)), ...options, stream: body.stream ?? false, }) as APIPromise | APIPromise>; @@ -75,6 +79,9 @@ export class Messages extends APIResource { * * The Token Count API can be used to count the number of tokens in a Message, * including tools, images, and documents, without creating it. + * + * Learn more about token counting in our + * [user guide](/en/docs/build-with-claude/token-counting) */ countTokens( body: MessageCountTokensParams, @@ -190,7 +197,7 @@ export interface CitationsDelta { type: 'citations_delta'; } -export type ContentBlock = TextBlock | ToolUseBlock; +export type ContentBlock = TextBlock | ToolUseBlock | ThinkingBlock | RedactedThinkingBlock; export type ContentBlockDeltaEvent = RawContentBlockDeltaEvent; @@ -199,7 +206,9 @@ export type ContentBlockParam = | ImageBlockParam | ToolUseBlockParam | ToolResultBlockParam - | DocumentBlockParam; + | DocumentBlockParam + | ThinkingBlockParam + | RedactedThinkingBlockParam; export interface ContentBlockSource { content: string | Array; @@ -354,10 +363,15 @@ export interface Message { * * For example, `output_tokens` will be non-zero, even for an empty string response * from Claude. + * + * Total input tokens in a request is the summation of `input_tokens`, + * `cache_creation_input_tokens`, and `cache_read_input_tokens`. */ usage: Usage; } +export type MessageCountTokensTool = ToolBash20250124 | ToolTextEditor20250124 | Tool; + export type MessageDeltaEvent = RawMessageDeltaEvent; export interface MessageDeltaUsage { @@ -404,6 +418,8 @@ export interface Metadata { * details and options. */ export type Model = + | 'claude-3-7-sonnet-latest' + | 'claude-3-7-sonnet-20250219' | 'claude-3-5-haiku-latest' | 'claude-3-5-haiku-20241022' | 'claude-3-5-sonnet-latest' @@ -439,7 +455,7 @@ export interface PlainTextSource { } export interface RawContentBlockDeltaEvent { - delta: TextDelta | InputJSONDelta | CitationsDelta; + delta: TextDelta | InputJSONDelta | CitationsDelta | ThinkingDelta | SignatureDelta; index: number; @@ -447,7 +463,7 @@ export interface RawContentBlockDeltaEvent { } export interface RawContentBlockStartEvent { - content_block: TextBlock | ToolUseBlock; + content_block: TextBlock | ToolUseBlock | ThinkingBlock | RedactedThinkingBlock; index: number; @@ -478,6 +494,9 @@ export interface RawMessageDeltaEvent { * * For example, `output_tokens` will be non-zero, even for an empty string response * from Claude. + * + * Total input tokens in a request is the summation of `input_tokens`, + * `cache_creation_input_tokens`, and `cache_read_input_tokens`. */ usage: MessageDeltaUsage; } @@ -508,6 +527,24 @@ export type RawMessageStreamEvent = | RawContentBlockDeltaEvent | RawContentBlockStopEvent; +export interface RedactedThinkingBlock { + data: string; + + type: 'redacted_thinking'; +} + +export interface RedactedThinkingBlockParam { + data: string; + + type: 'redacted_thinking'; +} + +export interface SignatureDelta { + signature: string; + + type: 'signature_delta'; +} + export interface TextBlock { /** * Citations supporting the text block. @@ -546,9 +583,65 @@ export interface TextDelta { type: 'text_delta'; } +export interface ThinkingBlock { + signature: string; + + thinking: string; + + type: 'thinking'; +} + +export interface ThinkingBlockParam { + signature: string; + + thinking: string; + + type: 'thinking'; +} + +export interface ThinkingConfigDisabled { + type: 'disabled'; +} + +export interface ThinkingConfigEnabled { + /** + * Determines how many tokens Claude can use for its internal reasoning process. + * Larger budgets can enable more thorough analysis for complex problems, improving + * response quality. + * + * Must be ≥1024 and less than `max_tokens`. + * + * See + * [extended thinking](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) + * for details. + */ + budget_tokens: number; + + type: 'enabled'; +} + +/** + * Configuration for enabling Claude's extended thinking. + * + * When enabled, responses include `thinking` content blocks showing Claude's + * thinking process before the final answer. Requires a minimum budget of 1,024 + * tokens and counts towards your `max_tokens` limit. + * + * See + * [extended thinking](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) + * for details. + */ +export type ThinkingConfigParam = ThinkingConfigEnabled | ThinkingConfigDisabled; + +export interface ThinkingDelta { + thinking: string; + + type: 'thinking_delta'; +} + export interface Tool { /** - * [JSON schema](https://json-schema.org/) for this tool's input. + * [JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. * * This defines the shape of the `input` that your tool accepts and that the model * will produce. @@ -577,7 +670,7 @@ export interface Tool { export namespace Tool { /** - * [JSON schema](https://json-schema.org/) for this tool's input. + * [JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. * * This defines the shape of the `input` that your tool accepts and that the model * will produce. @@ -590,6 +683,19 @@ export namespace Tool { } } +export interface ToolBash20250124 { + /** + * Name of the tool. + * + * This is how the tool will be called by the model and in tool_use blocks. + */ + name: 'bash'; + + type: 'bash_20250124'; + + cache_control?: CacheControlEphemeral | null; +} + /** * How the model should use the provided tools. The model can use a specific tool, * any available tool, or decide by itself. @@ -658,6 +764,21 @@ export interface ToolResultBlockParam { is_error?: boolean; } +export interface ToolTextEditor20250124 { + /** + * Name of the tool. + * + * This is how the tool will be called by the model and in tool_use blocks. + */ + name: 'str_replace_editor'; + + type: 'text_editor_20250124'; + + cache_control?: CacheControlEphemeral | null; +} + +export type ToolUnion = ToolBash20250124 | ToolTextEditor20250124 | Tool; + export interface ToolUseBlock { id: string; @@ -860,6 +981,19 @@ export interface MessageCreateParamsBase { */ temperature?: number; + /** + * Configuration for enabling Claude's extended thinking. + * + * When enabled, responses include `thinking` content blocks showing Claude's + * thinking process before the final answer. Requires a minimum budget of 1,024 + * tokens and counts towards your `max_tokens` limit. + * + * See + * [extended thinking](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) + * for details. + */ + thinking?: ThinkingConfigParam; + /** * How the model should use the provided tools. The model can use a specific tool, * any available tool, or decide by itself. @@ -878,8 +1012,9 @@ export interface MessageCreateParamsBase { * * - `name`: Name of the tool. * - `description`: Optional, but strongly-recommended description of the tool. - * - `input_schema`: [JSON schema](https://json-schema.org/) for the tool `input` - * shape that the model will produce in `tool_use` output content blocks. + * - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the + * tool `input` shape that the model will produce in `tool_use` output content + * blocks. * * For example, if you defined `tools` as: * @@ -936,7 +1071,7 @@ export interface MessageCreateParamsBase { * * See our [guide](https://docs.anthropic.com/en/docs/tool-use) for more details. */ - tools?: Array; + tools?: Array; /** * Only sample from the top K options for each subsequent token. @@ -1117,6 +1252,19 @@ export interface MessageCountTokensParams { */ system?: string | Array; + /** + * Configuration for enabling Claude's extended thinking. + * + * When enabled, responses include `thinking` content blocks showing Claude's + * thinking process before the final answer. Requires a minimum budget of 1,024 + * tokens and counts towards your `max_tokens` limit. + * + * See + * [extended thinking](https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking) + * for details. + */ + thinking?: ThinkingConfigParam; + /** * How the model should use the provided tools. The model can use a specific tool, * any available tool, or decide by itself. @@ -1135,8 +1283,9 @@ export interface MessageCountTokensParams { * * - `name`: Name of the tool. * - `description`: Optional, but strongly-recommended description of the tool. - * - `input_schema`: [JSON schema](https://json-schema.org/) for the tool `input` - * shape that the model will produce in `tool_use` output content blocks. + * - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the + * tool `input` shape that the model will produce in `tool_use` output content + * blocks. * * For example, if you defined `tools` as: * @@ -1193,7 +1342,7 @@ export interface MessageCountTokensParams { * * See our [guide](https://docs.anthropic.com/en/docs/tool-use) for more details. */ - tools?: Array; + tools?: Array; } Messages.Batches = Batches; @@ -1223,6 +1372,7 @@ export declare namespace Messages { type InputJsonDelta as InputJsonDelta, type InputJSONDelta as InputJSONDelta, type Message as Message, + type MessageCountTokensTool as MessageCountTokensTool, type MessageDeltaEvent as MessageDeltaEvent, type MessageDeltaUsage as MessageDeltaUsage, type MessageParam as MessageParam, @@ -1240,17 +1390,29 @@ export declare namespace Messages { type RawMessageStartEvent as RawMessageStartEvent, type RawMessageStopEvent as RawMessageStopEvent, type RawMessageStreamEvent as RawMessageStreamEvent, + type RedactedThinkingBlock as RedactedThinkingBlock, + type RedactedThinkingBlockParam as RedactedThinkingBlockParam, + type SignatureDelta as SignatureDelta, type TextBlock as TextBlock, type TextBlockParam as TextBlockParam, type TextCitation as TextCitation, type TextCitationParam as TextCitationParam, type TextDelta as TextDelta, + type ThinkingBlock as ThinkingBlock, + type ThinkingBlockParam as ThinkingBlockParam, + type ThinkingConfigDisabled as ThinkingConfigDisabled, + type ThinkingConfigEnabled as ThinkingConfigEnabled, + type ThinkingConfigParam as ThinkingConfigParam, + type ThinkingDelta as ThinkingDelta, type Tool as Tool, + type ToolBash20250124 as ToolBash20250124, type ToolChoice as ToolChoice, type ToolChoiceAny as ToolChoiceAny, type ToolChoiceAuto as ToolChoiceAuto, type ToolChoiceTool as ToolChoiceTool, type ToolResultBlockParam as ToolResultBlockParam, + type ToolTextEditor20250124 as ToolTextEditor20250124, + type ToolUnion as ToolUnion, type ToolUseBlock as ToolUseBlock, type ToolUseBlockParam as ToolUseBlockParam, type Usage as Usage, diff --git a/tests/api-resources/MessageStream.test.ts b/tests/api-resources/MessageStream.test.ts index 21407524..0788821a 100644 --- a/tests/api-resources/MessageStream.test.ts +++ b/tests/api-resources/MessageStream.test.ts @@ -30,6 +30,8 @@ async function* messageIterable(message: Message): AsyncGenerator { params: { max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', }, }, ], @@ -39,7 +39,7 @@ describe('resource batches', () => { params: { max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], stream: false, @@ -61,23 +61,16 @@ describe('resource batches', () => { }, ], temperature: 1, + thinking: { budget_tokens: 1024, type: 'enabled' }, tool_choice: { type: 'auto', disable_parallel_tool_use: true }, tools: [ { - input_schema: { - type: 'object', - properties: { - location: { description: 'The city and state, e.g. San Francisco, CA', type: 'string' }, - unit: { - description: 'Unit for the output - one of (celsius, fahrenheit)', - type: 'string', - }, - }, - }, - name: 'name', + display_height_px: 1, + display_width_px: 1, + name: 'computer', + type: 'computer_20241022', cache_control: { type: 'ephemeral' }, - description: 'Get the current weather in a given location', - type: 'custom', + display_number: 0, }, ], top_k: 5, diff --git a/tests/api-resources/beta/messages/messages.test.ts b/tests/api-resources/beta/messages/messages.test.ts index 363634bb..55bd3c79 100644 --- a/tests/api-resources/beta/messages/messages.test.ts +++ b/tests/api-resources/beta/messages/messages.test.ts @@ -13,7 +13,7 @@ describe('resource messages', () => { const responsePromise = client.beta.messages.create({ max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -28,7 +28,7 @@ describe('resource messages', () => { const response = await client.beta.messages.create({ max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], stream: false, @@ -50,20 +50,16 @@ describe('resource messages', () => { }, ], temperature: 1, + thinking: { budget_tokens: 1024, type: 'enabled' }, tool_choice: { type: 'auto', disable_parallel_tool_use: true }, tools: [ { - input_schema: { - type: 'object', - properties: { - location: { description: 'The city and state, e.g. San Francisco, CA', type: 'string' }, - unit: { description: 'Unit for the output - one of (celsius, fahrenheit)', type: 'string' }, - }, - }, - name: 'name', + display_height_px: 1, + display_width_px: 1, + name: 'computer', + type: 'computer_20241022', cache_control: { type: 'ephemeral' }, - description: 'Get the current weather in a given location', - type: 'custom', + display_number: 0, }, ], top_k: 5, @@ -75,7 +71,7 @@ describe('resource messages', () => { test('countTokens: only required params', async () => { const responsePromise = client.beta.messages.countTokens({ messages: [{ content: 'string', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -89,7 +85,7 @@ describe('resource messages', () => { test('countTokens: required and optional params', async () => { const response = await client.beta.messages.countTokens({ messages: [{ content: 'string', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', system: [ { text: "Today's date is 2024-06-01.", @@ -107,20 +103,16 @@ describe('resource messages', () => { ], }, ], + thinking: { budget_tokens: 1024, type: 'enabled' }, tool_choice: { type: 'auto', disable_parallel_tool_use: true }, tools: [ { - input_schema: { - type: 'object', - properties: { - location: { description: 'The city and state, e.g. San Francisco, CA', type: 'string' }, - unit: { description: 'Unit for the output - one of (celsius, fahrenheit)', type: 'string' }, - }, - }, - name: 'name', + display_height_px: 1, + display_width_px: 1, + name: 'computer', + type: 'computer_20241022', cache_control: { type: 'ephemeral' }, - description: 'Get the current weather in a given location', - type: 'custom', + display_number: 0, }, ], betas: ['string'], diff --git a/tests/api-resources/completions.test.ts b/tests/api-resources/completions.test.ts index e8520d9a..e3ebaa07 100644 --- a/tests/api-resources/completions.test.ts +++ b/tests/api-resources/completions.test.ts @@ -12,7 +12,7 @@ describe('resource completions', () => { test('create: only required params', async () => { const responsePromise = client.completions.create({ max_tokens_to_sample: 256, - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', prompt: '\n\nHuman: Hello, world!\n\nAssistant:', }); const rawResponse = await responsePromise.asResponse(); @@ -27,7 +27,7 @@ describe('resource completions', () => { test('create: required and optional params', async () => { const response = await client.completions.create({ max_tokens_to_sample: 256, - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', prompt: '\n\nHuman: Hello, world!\n\nAssistant:', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], diff --git a/tests/api-resources/messages/batches.test.ts b/tests/api-resources/messages/batches.test.ts index 8a3d3cfa..e98f02f4 100644 --- a/tests/api-resources/messages/batches.test.ts +++ b/tests/api-resources/messages/batches.test.ts @@ -17,7 +17,7 @@ describe('resource batches', () => { params: { max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', }, }, ], @@ -39,7 +39,7 @@ describe('resource batches', () => { params: { max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], system: [ @@ -60,24 +60,9 @@ describe('resource batches', () => { }, ], temperature: 1, + thinking: { budget_tokens: 1024, type: 'enabled' }, tool_choice: { type: 'auto', disable_parallel_tool_use: true }, - tools: [ - { - input_schema: { - type: 'object', - properties: { - location: { description: 'The city and state, e.g. San Francisco, CA', type: 'string' }, - unit: { - description: 'Unit for the output - one of (celsius, fahrenheit)', - type: 'string', - }, - }, - }, - name: 'name', - cache_control: { type: 'ephemeral' }, - description: 'Get the current weather in a given location', - }, - ], + tools: [{ name: 'bash', type: 'bash_20250124', cache_control: { type: 'ephemeral' } }], top_k: 5, top_p: 0.7, }, diff --git a/tests/api-resources/messages/messages.test.ts b/tests/api-resources/messages/messages.test.ts index e39e9972..70743ca3 100644 --- a/tests/api-resources/messages/messages.test.ts +++ b/tests/api-resources/messages/messages.test.ts @@ -13,7 +13,7 @@ describe('resource messages', () => { const responsePromise = client.messages.create({ max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -28,7 +28,7 @@ describe('resource messages', () => { const response = await client.messages.create({ max_tokens: 1024, messages: [{ content: 'Hello, world', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', metadata: { user_id: '13803d75-b4b5-4c3e-b2a2-6f21399b021b' }, stop_sequences: ['string'], stream: false, @@ -50,21 +50,9 @@ describe('resource messages', () => { }, ], temperature: 1, + thinking: { budget_tokens: 1024, type: 'enabled' }, tool_choice: { type: 'auto', disable_parallel_tool_use: true }, - tools: [ - { - input_schema: { - type: 'object', - properties: { - location: { description: 'The city and state, e.g. San Francisco, CA', type: 'string' }, - unit: { description: 'Unit for the output - one of (celsius, fahrenheit)', type: 'string' }, - }, - }, - name: 'name', - cache_control: { type: 'ephemeral' }, - description: 'Get the current weather in a given location', - }, - ], + tools: [{ name: 'bash', type: 'bash_20250124', cache_control: { type: 'ephemeral' } }], top_k: 5, top_p: 0.7, }); @@ -73,7 +61,7 @@ describe('resource messages', () => { test('countTokens: only required params', async () => { const responsePromise = client.messages.countTokens({ messages: [{ content: 'string', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', }); const rawResponse = await responsePromise.asResponse(); expect(rawResponse).toBeInstanceOf(Response); @@ -87,7 +75,7 @@ describe('resource messages', () => { test('countTokens: required and optional params', async () => { const response = await client.messages.countTokens({ messages: [{ content: 'string', role: 'user' }], - model: 'claude-3-5-haiku-latest', + model: 'claude-3-7-sonnet-latest', system: [ { text: "Today's date is 2024-06-01.", @@ -105,21 +93,9 @@ describe('resource messages', () => { ], }, ], + thinking: { budget_tokens: 1024, type: 'enabled' }, tool_choice: { type: 'auto', disable_parallel_tool_use: true }, - tools: [ - { - input_schema: { - type: 'object', - properties: { - location: { description: 'The city and state, e.g. San Francisco, CA', type: 'string' }, - unit: { description: 'Unit for the output - one of (celsius, fahrenheit)', type: 'string' }, - }, - }, - name: 'name', - cache_control: { type: 'ephemeral' }, - description: 'Get the current weather in a given location', - }, - ], + tools: [{ name: 'bash', type: 'bash_20250124', cache_control: { type: 'ephemeral' } }], }); }); }); From 571542881e001651c30f9ffd1492c640bbf22017 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 18:02:14 +0000 Subject: [PATCH 11/11] chore: release main --- .release-please-manifest.json | 4 ++-- CHANGELOG.md | 26 ++++++++++++++++++++++ package.json | 37 +++++++++++++++++++++++++------- packages/bedrock-sdk/yarn.lock | 2 +- packages/vertex-sdk/CHANGELOG.md | 8 +++++++ packages/vertex-sdk/package.json | 2 +- packages/vertex-sdk/yarn.lock | 2 +- src/version.ts | 2 +- 8 files changed, 69 insertions(+), 14 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e6222438..bef6f55d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,5 +1,5 @@ { - ".": "0.36.3", - "packages/vertex-sdk": "0.6.4", + ".": "0.37.0", + "packages/vertex-sdk": "0.7.0", "packages/bedrock-sdk": "0.12.4" } diff --git a/CHANGELOG.md b/CHANGELOG.md index da99f256..fff78b9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## 0.37.0 (2025-02-24) + +Full Changelog: [sdk-v0.36.3...sdk-v0.37.0](https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.36.3...sdk-v0.37.0) + +### Features + +* **api:** add claude-3.7 + support for thinking ([ffab311](https://github.com/anthropics/anthropic-sdk-typescript/commit/ffab3113ddb042951a35d71e571727f8cce184ee)) +* **client:** send `X-Stainless-Timeout` header ([#679](https://github.com/anthropics/anthropic-sdk-typescript/issues/679)) ([1172430](https://github.com/anthropics/anthropic-sdk-typescript/commit/1172430c87ba42acd2e16f4960247fe4003641a5)) +* **pagination:** avoid fetching when has_more: false ([#680](https://github.com/anthropics/anthropic-sdk-typescript/issues/680)) ([d4df248](https://github.com/anthropics/anthropic-sdk-typescript/commit/d4df248ff4eafa15b5f4b21b3da69d1a710052fa)) + + +### Bug Fixes + +* **client:** fix export map for index exports ([#684](https://github.com/anthropics/anthropic-sdk-typescript/issues/684)) ([56d9c7a](https://github.com/anthropics/anthropic-sdk-typescript/commit/56d9c7ab269af132d2ac374b6b7b9e5e523e0720)) +* correctly decode multi-byte characters over multiple chunks ([#681](https://github.com/anthropics/anthropic-sdk-typescript/issues/681)) ([e369e3d](https://github.com/anthropics/anthropic-sdk-typescript/commit/e369e3d650f2d761c3479935502615cab2a42b8d)) +* optimize sse chunk reading off-by-one error ([#686](https://github.com/anthropics/anthropic-sdk-typescript/issues/686)) ([53669af](https://github.com/anthropics/anthropic-sdk-typescript/commit/53669af8507c503dfd109ea34896dd018fbb1fc8)) + + +### Chores + +* **api:** update openapi spec url ([#678](https://github.com/anthropics/anthropic-sdk-typescript/issues/678)) ([84401b1](https://github.com/anthropics/anthropic-sdk-typescript/commit/84401b1068a11ae241a03643d32c459d837a82c6)) +* **internal:** add missing return type annotation ([#685](https://github.com/anthropics/anthropic-sdk-typescript/issues/685)) ([a8862b9](https://github.com/anthropics/anthropic-sdk-typescript/commit/a8862b9d39f688707ecf2142b002aa27a3cbd09b)) +* **internal:** fix devcontainers setup ([#689](https://github.com/anthropics/anthropic-sdk-typescript/issues/689)) ([8665946](https://github.com/anthropics/anthropic-sdk-typescript/commit/8665946ded8472e892301449569aae30613175fa)) +* **internal:** reorder model constants ([#676](https://github.com/anthropics/anthropic-sdk-typescript/issues/676)) ([52a2a11](https://github.com/anthropics/anthropic-sdk-typescript/commit/52a2a11467e4c117b50516104eb9b29aca86e232)) +* **internal:** update models used in tests ([52a2a11](https://github.com/anthropics/anthropic-sdk-typescript/commit/52a2a11467e4c117b50516104eb9b29aca86e232)) + ## 0.36.3 (2025-01-27) Full Changelog: [sdk-v0.36.2...sdk-v0.36.3](https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.36.2...sdk-v0.36.3) diff --git a/package.json b/package.json index db237811..8b040d6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@anthropic-ai/sdk", - "version": "0.36.3", + "version": "0.37.0", "description": "The official TypeScript library for the Anthropic API", "author": "Anthropic ", "types": "dist/index.d.ts", @@ -107,17 +107,38 @@ "default": "./dist/index.mjs" }, "./*.mjs": { - "types": ["./dist/*.d.ts", "./dist/*/index.d.ts"], - "default": ["./dist/*.mjs", "./dist/*/index.mjs"] + "types": [ + "./dist/*.d.ts", + "./dist/*/index.d.ts" + ], + "default": [ + "./dist/*.mjs", + "./dist/*/index.mjs" + ] }, "./*.js": { - "types": ["./dist/*.d.ts", "./dist/*/index.d.ts"], - "default": ["./dist/*.js", "./dist/*/index.js"] + "types": [ + "./dist/*.d.ts", + "./dist/*/index.d.ts" + ], + "default": [ + "./dist/*.js", + "./dist/*/index.js" + ] }, "./*": { - "types": ["./dist/*.d.ts", "./dist/*/index.d.ts"], - "require": ["./dist/*.js", "./dist/*/index.js"], - "default": ["./dist/*.mjs", "./dist/*/index.mjs"] + "types": [ + "./dist/*.d.ts", + "./dist/*/index.d.ts" + ], + "require": [ + "./dist/*.js", + "./dist/*/index.js" + ], + "default": [ + "./dist/*.mjs", + "./dist/*/index.mjs" + ] } } } diff --git a/packages/bedrock-sdk/yarn.lock b/packages/bedrock-sdk/yarn.lock index e38e12b0..e65e4a9e 100644 --- a/packages/bedrock-sdk/yarn.lock +++ b/packages/bedrock-sdk/yarn.lock @@ -17,7 +17,7 @@ "@anthropic-ai/sdk@file:../../dist": # x-release-please-start-version - version "0.36.3" + version "0.37.0" # x-release-please-end-version dependencies: "@types/node" "^18.11.18" diff --git a/packages/vertex-sdk/CHANGELOG.md b/packages/vertex-sdk/CHANGELOG.md index 9e83b1e8..a51016dc 100644 --- a/packages/vertex-sdk/CHANGELOG.md +++ b/packages/vertex-sdk/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.7.0 (2025-02-24) + +Full Changelog: [vertex-sdk-v0.6.4...vertex-sdk-v0.7.0](https://github.com/anthropics/anthropic-sdk-typescript/compare/vertex-sdk-v0.6.4...vertex-sdk-v0.7.0) + +### Features + +* **api:** add claude-3.7 + support for thinking ([ffab311](https://github.com/anthropics/anthropic-sdk-typescript/commit/ffab3113ddb042951a35d71e571727f8cce184ee)) + ## 0.6.4 (2025-01-23) Full Changelog: [vertex-sdk-v0.6.3...vertex-sdk-v0.6.4](https://github.com/anthropics/anthropic-sdk-typescript/compare/vertex-sdk-v0.6.3...vertex-sdk-v0.6.4) diff --git a/packages/vertex-sdk/package.json b/packages/vertex-sdk/package.json index ac5bda51..2315bba3 100644 --- a/packages/vertex-sdk/package.json +++ b/packages/vertex-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@anthropic-ai/vertex-sdk", - "version": "0.6.4", + "version": "0.7.0", "description": "The official TypeScript library for the Anthropic Vertex API", "author": "Anthropic ", "types": "dist/index.d.ts", diff --git a/packages/vertex-sdk/yarn.lock b/packages/vertex-sdk/yarn.lock index cf6ca9c5..0f2ae3be 100644 --- a/packages/vertex-sdk/yarn.lock +++ b/packages/vertex-sdk/yarn.lock @@ -17,7 +17,7 @@ "@anthropic-ai/sdk@file:../../dist": # x-release-please-start-version - version "0.36.3" + version "0.37.0" # x-release-please-end-version dependencies: "@types/node" "^18.11.18" diff --git a/src/version.ts b/src/version.ts index 0d285762..16bacf66 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.36.3'; // x-release-please-version +export const VERSION = '0.37.0'; // x-release-please-version