NAME
Langertha::Knarr::Handler::Passthrough - Knarr handler that forwards requests verbatim to an upstream HTTP API
VERSION
version 1.102
SYNOPSIS
use Langertha::Knarr::Handler::Passthrough;
my $handler = Langertha::Knarr::Handler::Passthrough->new(
upstreams => {
openai => 'https://api.openai.com',
anthropic => 'https://api.anthropic.com',
ollama => 'http://localhost:11434',
},
);
DESCRIPTION
Forwards the original wire-format request verbatim to a real upstream API. The protocol's parser already turned the body into a Langertha::Knarr::Request; Passthrough rebuilds the upstream JSON from $request->raw and re-POSTs it.
Both sync and streaming requests are supported. A sync answer carries the upstream's text, its tool calls as Langertha::ToolCall objects and its finish reason (verbatim; the front-side protocol maps it). For streaming, the upstream's protocol-native chunks are extracted into plain text deltas, the upstream's tool calls are assembled into complete Langertha::ToolCall objects on the stream's tool_calls which the front-side protocol then re-frames — keeping symmetry even when client and upstream use the same protocol.
The client's auth headers go along as the protocol's parser captured them in $request->extra->{forward_headers}: Authorization for OpenAI and Ollama, x-api-key, anthropic-version and Authorization for Anthropic. Langertha::Knarr takes its own proxy key out of them first (see "auth_token" in Langertha::Knarr). Each header line the client sent reaches the upstream as a line of its own, in its order (see "forward_header_pairs" in Langertha::Knarr::Request). Under Langertha::Knarr::PSGI the PSGI server has already joined a header sent twice into one value with , , and that value goes on as one line.
This is the building block behind Knarr's classic "configure your API keys once, point everything at me" use case.
upstreams
Required. HashRef mapping protocol name (openai, anthropic, ollama) to upstream base URL. The protocol's default chat path is appended (/v1/chat/completions, /v1/messages, /api/chat); an Ollama /api/generate request goes to the upstream's /api/generate, here and on the raw passthrough of Langertha::Knarr, and its answer is read from response.
default_auth
Optional. An Authorization header value to inject when the client didn't send one. Usually you let the client supply its own key.
model_id
Optional. Defaults to passthrough.
timeout
stall_timeout
Upstream timeouts in seconds from Langertha::Knarr::Role::UpstreamHTTP: timeout (default 300) is the total time of a non-streaming request, stall_timeout (default 120) the time a streaming one may go without data. 0 disables either. They also apply to the raw passthrough of Langertha::Knarr and Langertha::Knarr::PSGI, which answer an expired one with 504 in the client protocol's error shape. When this handler itself times out, its failure carries the category timeout or stall_timeout through the handler chain, and the client gets the same 504 or stream error frame.
is_upstream_for
my $same = $passthrough->is_upstream_for( 'anthropic', $engine->url );
True when $url is the upstream configured for the protocol, as "same_upstream" compares them. Langertha::Knarr uses it to send a model discovered from that very upstream to the raw passthrough.
same_upstream
my $same = Langertha::Knarr::Handler::Passthrough->same_upstream(
'https://api.openai.com', 'https://api.openai.com/v1' ); # 1
True when both URLs name the same upstream: same scheme, host, port and path, host case and default ports normalised, and a trailing /v1 (the API version an engine's base URL carries) ignored. A gateway serving several providers under paths of one host (https://gateway.example/gw/openai, .../gw/groq) is several upstreams. Needs no instance; Langertha::Knarr::Router calls it on the class.
serves_protocol
next unless $passthrough->serves_protocol( $request->protocol );
True when this handler can forward a request of the given protocol: an upstream is configured for it and the protocol has a known chat path (openai, anthropic, ollama). Langertha::Knarr and Langertha::Knarr::Handler::Router only send a request to the passthrough when this is true; any other request goes to the default engine instead.
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.