LlmClient is the provider-agnostic model client at the heart of Anvil’s agent system. It owns driver dispatch, transient retries, fallback chains, cost accumulation, and structured-output enforcement — so your agent handlers call generate, generateObject, or stream without worrying about provider-specific details.
Import it from anvil/llm:
Constructor
One or more driver instances that handle model calls. Each driver advertises which models it supports via
driver.supports(model). At least one driver is required. Add multiple drivers to cover different model families.The model used when a
generate, generateObject, or stream call omits the model field. If omitted here too, the call throws unless the request specifies a model explicitly.An ordered list of model IDs to try after the primary model fails with a transient error (
RetryableModelError). The client walks the list left-to-right, retrying maxRetries times per model before advancing. Each fallback model must be supported by one of the registered drivers.Attempts per model before moving to the next in the fallback chain. Defaults to
2.Base backoff delay in milliseconds between retry attempts. The actual wait before each attempt is
retryBaseMs * attemptNumber. Defaults to 50. Set to 0 in tests to eliminate wait time.Observability hook called after every model call — successful or failed. The
TraceEvent carries the model name, provider, attempt number, token usage, cost, and whether the call hit the fallback chain. Use this to feed your own metrics or logging pipeline.Drivers
Provider SDKs are optional peer dependencies loaded lazily at runtime — the Anvil kernel never pulls them in unless you instantiate the matching driver.- Anthropic
- OpenAI
- Gemini
@anthropic-ai/sdk as a peer dependency. Supports all claude-* models (e.g. claude-opus-4-8, claude-sonnet-4-5, claude-haiku-3-5). Uses the top-level system parameter and adaptive thinking when thinking: true is set on the request.Methods
client.generate(req)
Make a single-turn model call and return the complete result.
The model’s text response.
The model ID that produced the response (may differ from the request if the fallback chain was used).
The provider name, e.g.
"anthropic", "openai", "gemini".Token counts for this call.
Estimated USD cost from the built-in pricing table.
undefined if pricing data for the model isn’t registered.Tool invocations the model requested, when tools were provided and
stopReason is "tool_use".client.generateObject(req, schema, opts?)
Request structured output validated against a Zod schema. On validation failure the client automatically re-prompts the model with the validation error, up to maxRepairs times.
The same request shape as
generate. Do not set responseFormat manually — generateObject sets it automatically from the Zod schema.The Zod schema to validate the response against.
Maximum number of repair attempts after a validation failure. Defaults to
2. Each attempt re-prompts the model with the Zod error message.Optional name for the JSON schema advertised to the model. Helps some providers choose a better response format.
client.stream(req)
Stream tokens from the model as they arrive. Yields StreamEvent objects.
A text fragment from the model. Concatenate these to build the full response.
The final event, carrying the complete
GenerateResult with usage, cost, and stop reason.Streaming does not retry mid-stream — bytes may already be on the wire. The client only falls back to the next model in the chain if the stream fails before yielding any events.
Fallback chain example
Register both Anthropic and OpenAI drivers, set Claude as the primary, and fall back to GPT-4o on transient errors:claude-opus-4-8 up to two times. If both attempts throw a RetryableModelError (rate limit, 5xx, connection error) it moves to gpt-4o and tries that up to two times. Non-retryable errors (bad request, auth failure) stop the chain immediately.