UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

159 lines • 7.69 kB
/** * AnthropicProvider — wraps `@anthropic-ai/sdk` as an `LLMProvider`. * * Pattern: Adapter (GoF) + Ports-and-Adapters (Cockburn 2005). * Role: Outer ring — translates `LLMRequest`/`LLMResponse` to/from * Anthropic's Messages API. Knows nothing about agents, * recorders, or compositions. * Emits: N/A — providers don't emit; recorders observe via Agent. * * ─── Limitations ──────────────────────────────────────────────────── * * • Multi-modal content (images, video) NOT supported — the framework's * `LLMMessage.content` is `string`. The adapter accepts text-only. * May extend in a future release the message shape; this provider will be updated * in lockstep. * • `responseFormat` (JSON-Schema-coerced output) NOT exposed * — consumers can pass schema instructions via `systemPrompt`. */ import type { LLMCallHooks, LLMChunk, LLMProvider, LLMRequest, LLMResponse, WireRole } from '../../provider-contract/types.js'; import type { ThinkingMode } from '../../thinking/types.js'; import { type AnthropicMessage, type AnthropicMessagesBody } from './anthropicMessagesWire.js'; interface AnthropicClient { messages: { create(params: AnthropicCreateParams, options?: AnthropicRequestOptions): Promise<AnthropicMessage>; stream(params: AnthropicCreateParams, options?: AnthropicRequestOptions): AnthropicStream; }; } /** The SDK's per-request options — the one this adapter sends is the signal, * so an aborted run cancels the HTTP request itself (`lib/abort.ts`). */ interface AnthropicRequestOptions { signal?: AbortSignal; } /** The shared Messages body (anthropicMessagesWire.ts) plus the `model` the * SDK sends — `system`, `tools`, `thinking`, `tool_choice` and the cache * markers are all decided there. */ interface AnthropicCreateParams extends AnthropicMessagesBody { model: string; } interface AnthropicStream { finalMessage(): Promise<AnthropicMessage>; [Symbol.asyncIterator](): AsyncIterator<AnthropicStreamEvent>; /** The SDK's own close: aborts the request and ends the stream. */ abort?(): void; } interface AnthropicStreamEvent { type: string; delta?: { type: string; text?: string; }; } export interface AnthropicProviderOptions { /** API key. Defaults to `ANTHROPIC_API_KEY` env var. */ readonly apiKey?: string; /** * Default model used when `LLMRequest.model` is `'anthropic'` (the * shorthand). When the request specifies a full model id, that wins. */ readonly defaultModel?: string; /** Default max tokens when the request doesn't set it. Default 4096. */ readonly defaultMaxTokens?: number; /** * Per-request timeout in milliseconds, passed to the Anthropic client. * Long non-streaming turns (slow models, long conversations, agent loops) * can exceed the SDK default and surface as "Request timed out" — raise * this for such workloads. Omit to use the SDK default. */ readonly timeout?: number; /** * How many times the Anthropic client retries a failed request (timeouts, * connection errors, 429/5xx) before giving up. Omit to use the SDK default. */ readonly maxRetries?: number; /** * May the model ask for several tools at once in a single reply? * * Anthropic's default is `true`: one assistant message can carry many * `tool_use` blocks, and the agent runs them all inside ONE loop * iteration. Set `false` to cap it at one tool per reply — the model * still chooses which tool (or none), it just cannot batch. * * Set `false` when the SHAPE of the loop is part of what you are * measuring, not only its answer. A batched reply collapses several * tool results into one iteration, so per-iteration analysis * (`localizeContextBug` seeds one `'tool'` suspect per iteration from * `lastToolResult`, and `removableSources` de-duplicates by tool name) * attributes that iteration to the LAST tool of the batch — the others * never appear as separate influence rows and cannot be ablated * individually. One tool per iteration keeps every source separately * attributable, at the cost of one extra round trip per tool. * * Prompting for it is not equivalent: "call one tool at a time" in the * system prompt is a request the model may ignore, whereas this is a * request parameter the API enforces. * * `true` (and omitting the option) sends nothing — Anthropic's own * default already allows batching. Only `false` puts `tool_choice` on * the wire, and only on requests that actually carry tools. * * @default undefined (Anthropic's default — batching allowed) */ readonly parallelToolCalls?: boolean; /** * Which thinking request a model takes, where the built-in table is not * the answer you want: an id it cannot read (a proxy's alias), or a model * that takes both shapes and that you want adaptive (Opus 4.6 and Sonnet * 4.6 get a budget by default; in budget mode Opus 4.6 does not think * between tool calls). Return `undefined` to use the table, which reads an * unknown id as adaptive. The provider declares the result * (`provider.thinkingMode`) AND sends that shape, so the agent's build * check and the wire cannot disagree. * * @example * anthropic({ thinkingMode: (id) => (id.startsWith('claude-opus-4-6') ? 'adaptive' : undefined) }) */ readonly thinkingMode?: (model: string) => ThinkingMode | undefined; /** @internal Pre-built client for testing. Skips SDK import. */ readonly _client?: AnthropicClient; } /** * Build an `LLMProvider` backed by Anthropic's Messages API. * * @example * import { Agent } from 'agentfootprint'; * import { anthropic } from 'agentfootprint/providers'; * * const agent = Agent.create({ * provider: anthropic({ defaultModel: 'claude-sonnet-4-5-20250929' }), * model: 'anthropic', * }) * .tool(weatherTool) * .build(); */ export declare function anthropic(options?: AnthropicProviderOptions): LLMProvider; /** * Class form for consumers who prefer `new AnthropicProvider(...)` over * the `anthropic(...)` factory. Identical behavior; trivial wrapper. */ export declare class AnthropicProvider implements LLMProvider { readonly name = "anthropic"; readonly carriesInMessages: readonly WireRole[]; readonly carriesForcedToolChoice = true; /** The inner declaration resolves this adapter's shorthand before looking up the model. */ readonly supportsForcedToolChoice: (model: string) => boolean; readonly carriesNativeOutputSchema = true; /** The inner declaration resolves this adapter's shorthand before looking up the model. */ readonly supportsNativeOutputSchema: (model: string) => boolean; readonly supportsAutoNativeOutputSchema: (model: string) => boolean; readonly promptCaching: import("../../cache/types.js").PromptCaching; readonly thinkingHandler: import("../../thinking/types.js").ThinkingHandler; /** The inner provider's own function — a closure, safe to forward unbound. */ readonly thinkingMode: (model: string) => ThinkingMode; private readonly inner; constructor(options?: AnthropicProviderOptions); complete(req: LLMRequest, hooks?: LLMCallHooks): Promise<LLMResponse>; stream(req: LLMRequest, hooks?: LLMCallHooks): AsyncIterable<LLMChunk>; } export {}; //# sourceMappingURL=AnthropicProvider.d.ts.map