NAME

Langertha::Knarr::Handler::Router - Knarr handler that resolves model names via Langertha::Knarr::Router and dispatches to engines

VERSION

version 1.102

SYNOPSIS

use Langertha::Knarr::Config;
use Langertha::Knarr::Router;
use Langertha::Knarr::Handler::Router;
use Langertha::Knarr::Handler::Passthrough;

my $config = Langertha::Knarr::Config->new(file => 'knarr.yaml');
my $router = Langertha::Knarr::Router->new(config => $config);

my $handler = Langertha::Knarr::Handler::Router->new(
    router      => $router,
    passthrough => Langertha::Knarr::Handler::Passthrough->new(
        upstreams => $config->passthrough,
    ),
);

DESCRIPTION

Resolves incoming model names against a Langertha::Knarr::Router (which knows your knarr.yaml) and dispatches to the matched Langertha::Engine. When a passthrough fallback handler is supplied, unknown model names in a protocol it serves tunnel through to it instead of falling back to the default engine — this preserves the classic Knarr behaviour where configured models go via Langertha and everything else passes straight to the upstream API.

Non-streaming answers are labeled with the configured model. For a model config without a model key the provider's default answers, so the response keeps the model the upstream reported, else the engine's chat_model; it is never relabeled with the alias.

The client keeps seeing the configured name -- the id /v1/models lists and the one it can ask for again -- even where the upstream reported a more concrete one (gpt-4o answering as gpt-4o-2024-08-06). No protocol requires otherwise: Ollama echoes the name asked for, and the model of an OpenAI or Anthropic answer, where the provider puts its concrete name, is informational -- clients do not match it against the request. The reported model is kept as "upstream_model" in Langertha::Knarr::Response (on a stream "upstream_model" in Langertha::Knarr::Stream, the configured name as its model), and Langertha::Knarr::Handler::Tracing records it as the Langfuse generation's model, the configured name in its metadata.

A request that names no model (A2A always; ACP without agent_name, and any other protocol whose body leaves the model out) goes to the default engine with the model configured under default:, or with the provider's default when none is configured -- see "resolve" in Langertha::Knarr::Router. A model the client does name reaches the default engine as asked.

Streaming responses are pumped via the engine's chat_stream_realtime_f for native token-by-token delivery, with the same capability-filtered generation parameters as the non-streaming path.

router

Required. A Langertha::Knarr::Router instance.

passthrough

Optional. Any Langertha::Knarr::Handler consumer used as a fallback when the router can't resolve a model. A handler with a serves_protocol method (Langertha::Knarr::Handler::Passthrough) only gets requests in the protocols it can forward; an unknown model in any other protocol goes to the default engine instead.

When the router can't resolve a model, no passthrough serves the request's protocol and there is no default engine, the handler fails with the category model_not_found; Langertha::Knarr answers that with a 404 in the client protocol's error shape.

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.