NAME
Langertha::Role::AnthropicCompatible - Role for Anthropic-compatible API format
VERSION
version 0.503
SYNOPSIS
# This role is not used directly - it's composed by engines
# that implement the Anthropic-compatible /v1/messages API format.
package My::Engine;
use Moose;
extends 'Langertha::Engine::AnthropicBase';
sub _build_api_key { $ENV{MY_API_KEY} || die "needs api_key" }
sub default_model { 'my-model' }
__PACKAGE__->meta->make_immutable;
DESCRIPTION
This role provides the Anthropic /v1/messages wire-envelope methods for chat, streaming, tool calling, structured-output emulation, model listing, and rate-limit parsing. Engines that speak the Anthropic-compatible API format (Anthropic itself, MiniMax's legacy shim, Moonshot Kimi, LM Studio's Anthropic-compatible endpoint) compose this role via Langertha::Engine::AnthropicBase, which supplies the url / HTTP / JSON infrastructure from Langertha::Engine::Remote.
As with Langertha::Role::OpenAICompatible, this role is only self-contained in isolation: it assumes the composer brings the Langertha::Engine::Remote infrastructure (url, generate_http_request, parse_response, json, user_agent, chat_model, get_response_size, has_temperature, reasoning_kwargs_for, prompt_cache_kwargs_for, tool_wire_format, has_parallel_tool_use / parallel_tool_use, has_response_format / response_format) — normally provided by extending Langertha::Engine::Remote plus the universal roles composed in Langertha::Engine::AnthropicBase.
The wire envelope mirrors Langertha::Role::OpenAICompatible: this role owns the Anthropic request/response/stream/auth/rate-limit envelope, and the base class stays a thin composition shell.
api_key
Anthropic-compatible API key sent as x-api-key. Subclasses typically override _build_api_key to read a provider-specific environment variable.
api_version
The Anthropic API version header sent with every request. Defaults to 2023-06-01.
effort
Back-compat alias of "reasoning_effort" in Langertha::Role::ReasoningEffort. Controls the depth of thinking for reasoning models. When set (and reasoning_effort is not), it seeds reasoning_effort, which is serialized via Langertha::Reasoning to output_config.effort plus thinking: { type => 'adaptive' } (the current Messages-API shape) rather than the legacy top-level effort key.
my $claude = Langertha::Engine::Anthropic->new(
api_key => $ENV{ANTHROPIC_API_KEY},
model => 'claude-opus-4-8',
effort => 'high', # same as reasoning_effort => 'high'
);
inference_geo
Controls data residency for inference on the first-party Claude API. The API accepts exactly two values: global (the default) and us. There is no EU inference-geo on the first-party API; a value such as eu is not part of the enum and is rejected. It is only honoured on Claude 4.6+ models; older models return a 400 regardless of the value.
my $claude = Langertha::Engine::Anthropic->new(
api_key => $ENV{ANTHROPIC_API_KEY},
inference_geo => 'us',
);
The response reports where the request actually ran via usage.inference_geo, and us residency is billed at 1.1x the base rate.
EU data residency is not available this way. For EU-hosted inference use one of the EU engines Langertha already ships — Langertha::Engine::AKI, Langertha::Engine::Mistral, Langertha::Engine::Scaleway, Langertha::Engine::TSystems or Langertha::Engine::Hetzner — or reach Claude through the regional endpoints of Amazon Bedrock or Google Vertex AI, where inference_geo does not apply.
update_request
$self->update_request($http_request);
Adds the x-api-key, content-type: application/json, and anthropic-version headers to outgoing requests.
chat_request
my $request = $engine->chat_request($messages, %extra);
Generates an Anthropic-format message request (POST /v1/messages). Includes model, messages, max_tokens, temperature, reasoning-effort and prompt-cache controls, and optional system. Returns an HTTP request object.
_native_structured_output
Internal predicate. True when the engine's wire supports native structured output via output_config.format (the first-party Claude Messages API); false (the default) for the legacy /anthropic shim engines, which fall back to the ADR 0005 synthesized-tool rewrite. Langertha::Engine::Anthropic overrides it to a true value. It describes the endpoint and so decides the manifest dialect (anthropic vs anthropic-compat, Langertha::Manifest::Builder); the request builders ask "_native_structured_output_for_model" instead.
_native_structured_output_for_model
Internal predicate the request builders use to pick the structured-output path for the current chat_model: native output_config.format when true, the ADR 0005 synthesized-tool rewrite when false. Defaults to "_native_structured_output"; Langertha::Engine::MoonshotAnthropic overrides it to be true on kimi-k3 only.
_translate_response_format
Internal: turns a response_format hash into a synthesized tool plus a tool_choice, returning the synthetic tool name. Returns undef when no usable structure is present. Used by the legacy /anthropic shim engines and, for a bare json_object, by first-party Langertha::Engine::Anthropic (which routes a json_schema natively via output_config.format instead). The synthesized tool is forced via a named tool_choice where the model supports forced tool use, and degraded to tool_choice auto where it does not (claude-fable-5-1 / claude-mythos-5-1).
_normalize_tool_params
Internal: normalizes tool_choice to Anthropic's native format and folds parallel_tool_use into the tool_choice block as disable_parallel_tool_use.
chat_response
my $response = $role->chat_response($http_response, $rf_routed);
Parses an Anthropic-format message response into a Langertha::Response object. When $rf_routed (a synthetic tool name, or truthy for the attribute path) and tool calls are present, lifts the first tool_use arguments back into content as JSON.
A 200 body that is an error envelope ({"type":"error","error":{...}}), or carries an error object and no content, croaks with <engine class> response carried an error: MESSAGE, as the OpenAI-compatible parser does.
_usage_input_includes_cache
Internal hook. Returns whether this endpoint counts the prompt-cache reads and writes it reports (cache_read_input_tokens / cache_creation_input_tokens) inside usage.input_tokens. The default returns undef, which keeps the inference of "from_hash" in Langertha::Usage: the flat Anthropic keys are counted beside input_tokens, as first-party Anthropic reports them. An Anthropic-compatible shim that counts them inside overrides it with sub _usage_input_includes_cache { 1 }, as Langertha::Engine::AKIAnthropic does. The answer reaches "input_includes_cache" in Langertha::Usage on both the "chat_response" path and the streamed final chunk, as an input_includes_cache key in a copy of the usage block.
stream_format
my $format = $engine->stream_format;
Returns 'sse' (Server-Sent Events), the streaming format used by Anthropic-compatible APIs.
chat_stream_request
my $request = $engine->chat_stream_request($messages, %extra);
Generates an Anthropic-format streaming request (SSE, stream => true). Returns an HTTP request object for use with streaming execution.
parse_stream_chunk
my $chunk = $engine->parse_stream_chunk($data, $event, \%state);
Parses a single SSE data payload from an Anthropic-format stream by event type. A content_block_delta of type thinking_delta surfaces its thinking text onto the chunk's thinking attribute. Anthropic splits the terminal metadata across two events — message_delta carries finish_reason (stop_reason) and usage while message_stop is the is_final event — so the message_delta metadata is held in \%state and replayed onto the is_final message_stop chunk, matching the cross-dialect contract where finish_reason and usage land on the same chunk that is is_final. That usage is complete: the input side message_start reports (input_tokens, cache_read_input_tokens, cache_creation_input_tokens) merged with the message_delta usage, whose keys win; both chunks also carry the model from message_start. Returns a Langertha::Stream::Chunk, or undef for event types that carry no content.
A tool_use content block is assembled from its content_block_start and input_json_delta fragments in \%state (one HashRef per stream, reset on message_start; the stream paths pass it, a direct caller may omit it and share the engine's fallback; $event is set only on the "process_stream_data" in Langertha::Role::Streaming path) and lands as a Langertha::ToolCall on the chunk for its content_block_stop, read by the same "extract" in Langertha::ToolCall as "chat_response". Collect the calls with "aggregate_tool_calls" in Langertha::Role::Chat. The content_block_stop of any other block still returns undef.
list_models_request
my $request = $engine->list_models_request;
my $request = $engine->list_models_request(after_id => $last_id);
Generates an HTTP GET request for the Anthropic /v1/models endpoint, optionally with pagination params. Returns an HTTP request object.
list_models_response
my $data = $engine->list_models_response($http_response);
Parses the Anthropic /v1/models response. Returns the full response hashref.
list_models
my $model_ids = $engine->list_models;
my $models = $engine->list_models(full => 1);
my $models = $engine->list_models(force_refresh => 1);
Fetches available models from the Anthropic API using cursor pagination. Returns an ArrayRef of model ID strings by default, or full model objects when full = 1> is passed. Results are cached for models_cache_ttl seconds (default: 3600). Pass force_refresh = 1> to bypass the cache.
_parse_rate_limit_headers
Parses anthropic-ratelimit-* headers from the HTTP response into a Langertha::RateLimit object. Collects the full raw superset via "_collect_headers" in Langertha::RateLimit — capturing extras like input-tokens-limit, output-tokens-limit and the anthropic-priority-* / anthropic-fast-* families — then normalizes the RFC 3339 reset instants into "requests_reset_at" in Langertha::RateLimit / "tokens_reset_at" in Langertha::RateLimit; the *_reset_after durations are derived lazily against "received" in Langertha::RateLimit.
SEE ALSO
Langertha::Engine::AnthropicBase - Composes this role as a thin shell
Langertha::Role::OpenAICompatible - The parallel OpenAI wire-envelope role
https://status.anthropic.com/ - Anthropic service status
https://docs.anthropic.com/ - Official Anthropic documentation
Langertha::Role::Chat - Chat interface methods
Langertha::Role::Tools - MCP tool calling interface
Langertha::Role::Streaming - Streaming support (SSE format)
Langertha::Engine::Gemini - Another non-OpenAI-compatible engine
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.