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.