NAME
Langertha::Knarr::Response - Normalized chat response shared across all Knarr handlers and protocol formatters
VERSION
version 1.101
DESCRIPTION
The single shape every Langertha::Knarr::Handler returns and every Langertha::Knarr::Protocol formatter consumes. Mirrors Langertha::Response but is decoupled from it so non-engine handlers (Langertha::Knarr::Handler::Code, Langertha::Knarr::Handler::A2AClient, Langertha::Knarr::Handler::ACPClient) can produce a Knarr response without going through Langertha first.
"coerce" upgrades all the legacy shapes Knarr handlers used to return — a bare string, a { content => ..., model => ... } hashref, or a stringifiable Langertha::Response — into a proper value object. So existing call sites can pass anything they already had and downstream code can rely on a single API.
content
Plain assistant text. Defaults to empty string.
model
The model id that produced the response, if known.
usage
A Langertha::Usage object with token counts, if the engine reported them. undef for handlers that have no usage data (Code, Passthrough).
A plain HashRef is accepted and upgraded with Langertha::Usage's from_hash — Langertha::Response declares its own usage as Maybe[HashRef] and the engines pass the provider's raw JSON hash straight through, so that is the shape every real engine response arrives in. The upgrade normalizes the provider spellings (prompt_tokens / input_tokens / prompt_eval_count, ...), which is what lets the protocol formatters call to_openai_format and friends on whatever any handler produced.
tool_calls
ArrayRef of Langertha::ToolCall objects produced by the engine. Empty arrayref when the response is plain text.
finish_reason
Provider-agnostic stop reason (stop, tool_calls, length, ...). Optional; the protocol formatters fall back to stop / end_turn when undef.
id
The provider-side response id (OpenAI chatcmpl-..., Anthropic msg_...), when the engine reported one. Carried so a Knarr trace can be correlated with the provider's own logs. undef for handlers that have no upstream id.
timing
HashRef of engine-measured durations, mirroring "timing" in Langertha::Response. The two keys every Langertha engine populates are ttft_seconds and total_seconds (Float, seconds); provider-native stage durations (Ollama's load_seconds, prompt_eval_seconds, eval_seconds, ...) may be present too.
Only the routed path has this — it comes from the engine's own measurement inside Langertha. Raw passthrough never produces a Langertha::Response and therefore never a timing; see "Timing sources" in Langertha::Knarr::Tracing for which path reports latency from where.
thinking
Chain-of-thought / reasoning text the engine separated from content (DeepSeek reasoning_content, Anthropic thinking blocks, or Langertha::Role::ThinkTag filtering). Carried because it is model output that content no longer holds — without it the reasoning is lost at the proxy boundary. Recorded into the Langfuse generation metadata; the protocol formatters currently do not emit it.
rate_limit
Optional Langertha::RateLimit object built from the upstream provider's quota headers. Kept as the object; consumers pull the scalar fields they need.
raw
Optional. The provider-native response body, kept around for handlers (passthrough-style) that want to preserve every byte upstream returned.
coerce
my $r = Langertha::Knarr::Response->coerce( $whatever );
Class method. Accepts:
an existing
Langertha::Knarr::Response— returned as-is.a Langertha::Response — fields lifted via
from_langertha_response.any other blessed object that stringifies — used as
content.a HashRef — fed to
newafter key normalization.a plain scalar — used as
content.undef— produces an empty response.
This is the single normalization entry point. Handlers can return whatever shape is convenient and the dispatcher coerces once at the boundary.
from_langertha_response
my $r = Langertha::Knarr::Response->from_langertha_response($lresp);
Builds a Knarr response from a Langertha::Response. Carries content, model, usage, tool_calls, finish_reason, id, timing, thinking, rate_limit, and raw across. usage arrives as the provider's raw HashRef and is upgraded to a Langertha::Usage on the way in; see "usage".
Every field is read behind a can() guard so Knarr keeps working against a Langertha release that predates one of them — the older attributes were added over several Langertha versions and timing / rate_limit / thinking are the most recent.
ttft_seconds
Time-to-first-token in seconds (Float) out of "timing", or undef when the engine did not measure it (non-streaming calls, or any handler that is not engine-backed).
total_seconds
Total engine-measured call duration in seconds (Float) out of "timing", or undef.
has_tool_calls
True when tool_calls contains at least one entry.
clone_with
my $r2 = $r->clone_with( model => 'override' );
Returns a new response with the given fields overridden. All other attributes carry through from $self.
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha-knarr/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 <torsten@raudssus.de> https://raudssus.de/
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.