@anthropic-ai/sdk
Version:
The official TypeScript library for the Anthropic API
270 lines • 13.1 kB
TypeScript
import { BetaRunnableTool } from "./BetaRunnableTool.js";
import { BetaRunnableToolset } from "./BetaRunnableToolset.js";
import { Anthropic } from "../../index.js";
import { BetaCompactionConfig, BetaMessage, BetaMessageParam, BetaToolUnion, BetaToolUseBlock, MessageCreateParams } from "../../resources/beta.js";
import { BetaMessageStream } from "../BetaMessageStream.js";
import { RequestOptions } from "../../internal/request-options.js";
import type { Simplify } from "../internal/types.js";
/**
* A ToolRunner handles the automatic conversation loop between the assistant and tools.
*
* A ToolRunner is an async iterable that yields either BetaMessage or BetaMessageStream objects
* depending on the streaming configuration. With `runToolsEagerly` it starts each tool call while the
* reply is still streaming, unless `deferToolCall()` holds the call.
*/
export declare class BetaToolRunner<Stream extends boolean> {
#private;
private client;
constructor(client: Anthropic, params: BetaToolRunnerParams, options?: BetaToolRunnerRequestOptions);
[Symbol.asyncIterator](): AsyncIterator<Stream extends true ? BetaMessageStream : Stream extends false ? BetaMessage : BetaMessage | BetaMessageStream>;
/**
* Update the parameters for the next API call. This invalidates any cached tool responses. With
* `runToolsEagerly`, a tool call of the reply that has started doesn't run again.
*
* @param paramsOrMutator - Either new parameters or a function to mutate existing parameters
*
* @example
* // Direct parameter update
* runner.setMessagesParams({
* model: 'claude-haiku-4-5',
* max_tokens: 500,
* });
*
* @example
* // Using a mutator function
* runner.setMessagesParams((params) => ({
* ...params,
* max_tokens: 100,
* }));
*/
setMessagesParams(params: BetaToolRunnerParams): void;
setMessagesParams(mutator: (prevParams: BetaToolRunnerParams) => BetaToolRunnerParams): void;
/**
* Update the request options for future API calls.
*
* @param optionsOrMutator - Either new options or a function to mutate existing options
*
* @example
* // Direct options update
* runner.setRequestOptions({
* signal: controller.signal,
* });
*
* @example
* // Using a mutator function
* runner.setRequestOptions((prevOptions) => ({
* ...prevOptions,
* signal: controller.signal,
* }));
*/
setRequestOptions(options: BetaToolRunnerRequestOptions): void;
setRequestOptions(mutator: (prevOptions: BetaToolRunnerRequestOptions) => BetaToolRunnerRequestOptions): void;
/**
* Get the tool response for the last message from the assistant.
* Avoids redundant tool executions by caching results. With `runToolsEagerly`, it reuses the calls of
* the reply that have started and runs the rest, including the ones `deferToolCall()` is holding, so that no
* call runs twice.
*
* @returns A promise that resolves to a BetaUserMessageParam containing tool results, or null if no tools need to be executed
*
* @example
* const toolResponse = await runner.generateToolResponse();
* if (toolResponse) {
* console.log('Tool results:', toolResponse.content);
* }
*/
generateToolResponse(signal?: AbortSignal | null | undefined): Promise<Anthropic.Beta.Messages.BetaUserMessageParam | null>;
/**
* Hold a tool call of the current reply until you are done with the reply, which is when it runs without
* streaming: at the end of the loop body, or when you call `generateToolResponse()`.
*
* With `runToolsEagerly` the runner otherwise starts each call while the reply streams, as soon as
* the model has moved on from it: when the next block starts, or the reply stops with `tool_use`. A call
* never starts before your stream listeners and any `for await` over the stream have handled that event, so
* you can call this from either once you have seen the call. The other calls of the reply still start early,
* except the later calls of the same toolset, which a toolset runs in order.
* It does nothing for a call that has started, outside the loop body, and without `runToolsEagerly`.
*
* @param toolUse - The `tool_use` block of the call, or its id
*
* @example
* for await (const stream of runner) {
* stream.on('contentBlock', (block) => {
* if (block.type === 'tool_use' && block.name === 'delete_file') {
* runner.deferToolCall(block);
* }
* });
* await stream.finalMessage();
* // No `delete_file` call has started yet.
* }
*/
deferToolCall(toolUse: BetaToolUseBlock | string): void;
/**
* The tool calls of the current reply that `deferToolCall()` is holding, in the model's order, as their
* `tool_use` blocks. A call is in the list once its block has finished streaming, so read the list when you
* are done with the stream. A call that has started is not in it. It is empty without
* `runToolsEagerly`.
*
* Held calls are listed whatever the reply's `stop_reason`, because `generateToolResponse()` runs them
* whatever it is. After `max_tokens` the input of the last call can be cut off.
*
* @example
* for await (const stream of runner) {
* stream.on('contentBlock', (block) => {
* if (block.type === 'tool_use' && block.name === 'delete_file') {
* runner.deferToolCall(block);
* }
* });
* await stream.finalMessage();
*
* const held = runner.deferredToolCalls;
* if (held.length > 0 && !(await confirm(held))) break;
* }
*/
get deferredToolCalls(): BetaToolUseBlock[];
/**
* Wait for the async iterator to complete. This works even if the async iterator hasn't yet started, and
* will wait for an instance to start and go to completion.
*
* @returns A promise that resolves to the final BetaMessage when the iterator completes
*
* @example
* // Start consuming the iterator
* for await (const message of runner) {
* console.log('Message:', message.content);
* }
*
* // Meanwhile, wait for completion from another part of the code
* const finalMessage = await runner.done();
* console.log('Final response:', finalMessage.content);
*/
done(): Promise<BetaMessage>;
/**
* Returns a promise indicating that the stream is done. Unlike .done(), this will eagerly read the stream:
* * If the iterator has not been consumed, consume the entire iterator and return the final message from the
* assistant.
* * If the iterator has been consumed, waits for it to complete and returns the final message.
*
* @returns A promise that resolves to the final BetaMessage from the conversation
* @throws {AnthropicError} If no messages were processed during the conversation
*
* @example
* const finalMessage = await runner.runUntilDone();
* console.log('Final response:', finalMessage.content);
*/
runUntilDone(): Promise<BetaMessage>;
/**
* Get the current parameters being used by the ToolRunner.
*
* @returns A readonly view of the current ToolRunnerParams
*
* @example
* const currentParams = runner.params;
* console.log('Current model:', currentParams.model);
* console.log('Message count:', currentParams.messages.length);
*/
get params(): Readonly<BetaToolRunnerParams>;
/**
* Add one or more messages to the conversation history.
*
* @param messages - One or more BetaMessageParam objects to add to the conversation
*
* @example
* runner.pushMessages(
* { role: 'user', content: 'Also, what about the weather in NYC?' }
* );
*
* @example
* // Adding multiple messages
* runner.pushMessages(
* { role: 'user', content: 'What about NYC?' },
* { role: 'user', content: 'And Boston?' }
* );
*/
pushMessages(...messages: BetaMessageParam[]): void;
/**
* Schedule a compaction of the conversation. Once the current turn has finished, including any tool
* calls, the runner requests a summary and replaces the message history with the compaction response,
* which is yielded like any other message. Requires the `compact-2026-09-04` beta.
*
* @param compaction - The config to send, as `messages.create()` takes it. Defaults to `{ type: 'summarize' }`
*
* @example
* for await (const message of runner) {
* if (message.usage.input_tokens > 100_000) {
* runner.compactBeforeNextTurn();
* }
* }
*/
compactBeforeNextTurn(compaction?: BetaCompactionConfig): void;
/**
* Give the model more tools without changing `params.tools`, which would miss the prompt cache.
*
* Each tool's whole definition is sent in a `tool_addition` block with the next request, and a
* runnable tool replaces a runnable tool of the same name straight away, even for a call already in
* the message being handled. A call that started while the reply streamed keeps the old one. A raw definition
* is only sent: the runner never runs it, and stops running a tool of the same name. Passing a runnable toolset,
* or a raw definition of a toolset the runner has, throws `ToolsetContractError` and adds nothing: a runner's
* toolsets come only from `params.tools`. A tool named like one of its toolsets is left to the API, which rejects
* it with the next request. Requires the `inline-tools-2026-09-15` beta, which the runner does not add for you.
*
* @param tools - Runnable tools (for example from `betaZodTool()`) or raw tool definitions
*
* @example
* runner.addTools(queryDatabaseTool);
*/
addTools(...tools: (BetaRunnableTool<any> | BetaToolUnion)[]): void;
/**
* Take tools away from the model without changing `params.tools`, which would miss the prompt cache.
*
* The tools stop being run straight away: a call to one of them, even one in the message being
* handled, gets the same "not found" error result as a call to an unknown tool. A call that started while the
* reply streamed finishes as usual. The model is told in a `tool_removal` block with the next request. Use
* {@link addTools} to bring a tool back. Passing the name of one of the runner's toolsets throws
* `ToolsetContractError` and removes nothing. Requires the `inline-tools-2026-09-15` beta, which the runner does
* not add for you.
*
* @param tools - The tools to remove, or their names
*
* @example
* runner.removeTools('query_database');
*/
removeTools(...tools: (BetaRunnableTool<any> | string)[]): void;
/**
* Makes the ToolRunner directly awaitable, equivalent to calling .runUntilDone()
* This allows using `await runner` instead of `await runner.runUntilDone()`
*/
then<TResult1 = BetaMessage, TResult2 = never>(onfulfilled?: ((value: BetaMessage) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
}
/**
* Parameters for creating a ToolRunner, extending MessageCreateParams with runnable tools.
*
* `compaction` is left out because every request in the loop would compact again; call
* `BetaToolRunner.compactBeforeNextTurn()` instead.
*/
export type BetaToolRunnerParams = Simplify<Omit<MessageCreateParams, 'tools' | 'compaction'> & {
tools: (BetaToolUnion | BetaRunnableTool<any> | BetaRunnableToolset)[];
/**
* Maximum number of iterations (API requests) to make in the tool execution loop.
* Each iteration consists of: assistant response → tool execution → tool results.
* When exceeded, the loop will terminate even if tools are still being requested.
*/
max_iterations?: number;
/**
* Run each tool as soon as its call is complete, before the reply finishes, instead of once you are done
* with the reply. This is optimistic: if the reply is interrupted or changes course, the tool may have
* already run, so use `deferToolCall()` to hold the calls that aren't safe to run twice. Requires
* `stream: true`.
*
* A toolset's call, such as a browser or computer action, starts early too when the toolset's earlier calls
* have ended. Otherwise it waits for the reply, and so do the toolset's later calls. A toolset's `confirm` is
* still asked before each call runs, so it may be asked while the reply streams.
*
* This will be the default in a future version.
*
* @default false
*/
runToolsEagerly?: boolean;
}>;
export type BetaToolRunnerRequestOptions = Pick<RequestOptions, 'headers' | 'signal' | 'fallbackState' | 'openTelemetry'>;
//# sourceMappingURL=BetaToolRunner.d.ts.map