NAME
Langertha::Knarr - LLM Proxy with Langfuse Tracing
VERSION
version 0.003
SYNOPSIS
# Docker (recommended)
docker run \
-e OPENAI_API_KEY=sk-... \
-p 8080:8080 \
raudssus/langertha-knarr
# Or with Langfuse tracing
docker run \
-e OPENAI_API_KEY=sk-... \
-e LANGFUSE_PUBLIC_KEY=pk-lf-... \
-e LANGFUSE_SECRET_KEY=sk-lf-... \
-p 8080:8080 \
raudssus/langertha-knarr
# Local usage
knarr init > knarr.yaml
knarr start
# Programmatic usage
use Langertha::Knarr;
my $app = Langertha::Knarr->build_app(config_file => '/etc/knarr/knarr.yaml');
DESCRIPTION
Knarr is an LLM proxy that accepts requests in OpenAI, Anthropic, or Ollama format, routes them to any Langertha backend engine, and automatically records every request and response in Langfuse for observability and cost tracking.
Named after the Norse cargo ship, Knarr carries your LLM calls safely to their destination.
Request Flow
┌─────────────────────────────────┐
Client │ Knarr Proxy │ Backend
────── │ ──────────── │ ───────
OpenAI format ───► │ /v1/chat/completions │
Anthropic format───► │ /v1/messages ──Router──► │ ──► Langertha Engine ──► API
Ollama format ───► │ /api/chat │
│ │ │
│ ▼ │
│ Langfuse Tracing │
└─────────────────────────────────┘
Every request is traced: the model name, engine used, full message input, output text, token usage, and any errors are sent to Langfuse automatically.
API Formats and Routes
Knarr listens on port 8080 for OpenAI and Anthropic requests, and port 11434 for Ollama requests (matching the Ollama default).
OpenAI format (port 8080):
POST /v1/chat/completions— Chat completionsPOST /v1/embeddings— EmbeddingsGET /v1/models— List available models
Anthropic format (port 8080):
POST /v1/messages— Messages API
Ollama format (port 11434):
POST /api/chat— ChatGET /api/tags— List modelsGET /api/ps— Running models (always returns empty)
Health check (any port):
GET /health— Returns{"status":"ok","proxy":"knarr"}
Passthrough Mode
By default, when passthrough: true is set (the default in container mode), requests are forwarded transparently to the upstream API using the client's own API key. This means you can point any OpenAI or Anthropic client at Knarr and it will just work, while Knarr adds Langfuse tracing on top.
Passthrough defaults:
openaipassthrough →https://api.openai.comanthropicpassthrough →https://api.anthropic.com
Ollama requests are never passed through (no upstream Ollama passthrough URL).
Engine Routing
When a model is explicitly configured in the config file (or discovered via auto_discover), Knarr routes requests through the corresponding Langertha engine. This allows routing to alternative backends, local models, or services that do not natively speak the protocol the client is using.
Example: an Ollama client can request gpt-4o, and Knarr will route it through the OpenAI Langertha engine, returning an Ollama-formatted response.
Routing Priority
For each incoming request, Knarr resolves the target in this order:
- 1. Explicit model config or auto-discovered model → route via Langertha engine
- 2. Passthrough enabled for this format → forward to upstream API
- 3. Default engine configured → route via default Langertha engine
- 4. None of the above → 404 error
Streaming
All three formats support streaming:
OpenAI — SSE (Server-Sent Events), ends with
data: [DONE]Anthropic — SSE, ends with
event: message_stopOllama — NDJSON (newline-delimited JSON), ends with
{"done":true}
For passthrough requests, the stream is piped byte-for-byte from the upstream API to the client with no buffering.
Docker Usage
The primary way to run Knarr is with Docker. The image is published at raudssus/langertha-knarr.
Zero-config start with just an API key:
docker run -e OPENAI_API_KEY=sk-... -p 8080:8080 raudssus/langertha-knarr
With multiple providers and Langfuse:
docker run \
-e OPENAI_API_KEY=sk-... \
-e ANTHROPIC_API_KEY=sk-ant-... \
-e LANGFUSE_PUBLIC_KEY=pk-lf-... \
-e LANGFUSE_SECRET_KEY=sk-lf-... \
-e LANGFUSE_URL=https://cloud.langfuse.com \
-p 8080:8080 \
-p 11434:11434 \
raudssus/langertha-knarr
With a mounted config file:
docker run \
-v ./knarr.yaml:/app/knarr.yaml \
-p 8080:8080 \
raudssus/langertha-knarr \
knarr start -c /app/knarr.yaml
Configuration File
The config file is YAML. All string values support ${ENV_VAR} interpolation.
listen:
- "127.0.0.1:8080"
- "127.0.0.1:11434"
models:
gpt-4o:
engine: OpenAI
model: gpt-4o
api_key_env: OPENAI_API_KEY
local:
engine: OllamaOpenAI
url: http://localhost:11434/v1
model: llama3.2
default:
engine: OpenAI
auto_discover: true
passthrough:
openai: https://api.openai.com
anthropic: https://api.anthropic.com
proxy_api_key: ${KNARR_API_KEY}
langfuse:
url: https://cloud.langfuse.com
public_key: ${LANGFUSE_PUBLIC_KEY}
secret_key: ${LANGFUSE_SECRET_KEY}
trace_name: my-app
Model config keys:
engine(required) — Langertha engine name (e.g.OpenAI,Anthropic,OllamaOpenAI)model— Model name to pass to the engineapi_key_env— Environment variable name holding the API keyapi_key— Literal API key (preferapi_key_env)url— Custom base URL (for self-hosted or OpenAI-compatible endpoints)system_prompt— Default system prompt for all requests to this modeltemperature— Default temperatureresponse_size— Default max response tokens
Langfuse Tracing
When LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY are set (or configured in the config file), Knarr automatically traces every request:
Trace created with model name, engine, format, and input messages
Generation recorded with start time, end time, output, and token usage
Errors recorded with level ERROR and the error message
Tags:
knarradded to every trace
Traces are sent synchronously after each request. Configure the trace name with KNARR_TRACE_NAME or langfuse.trace_name in the config file.
Programmatic Usage
use Langertha::Knarr;
use Langertha::Knarr::Config;
# From a config file
my $app = Langertha::Knarr->build_app(config_file => 'knarr.yaml');
# From a pre-built config object
my $config = Langertha::Knarr::Config->new(file => 'knarr.yaml');
my $app = Langertha::Knarr->build_app(config => $config);
# Use with any Mojolicious server
use Mojo::Server::Daemon;
my $daemon = Mojo::Server::Daemon->new(
app => $app,
listen => ['http://127.0.0.1:8080'],
);
$daemon->run;
Environment Variables
OPENAI_API_KEY— OpenAI API key (auto-detected byknarr container)ANTHROPIC_API_KEY— Anthropic API key (auto-detected)GROQ_API_KEY— Groq API key (auto-detected)MISTRAL_API_KEY— Mistral API key (auto-detected)DEEPSEEK_API_KEY— DeepSeek API key (auto-detected)GEMINI_API_KEY— Google Gemini API key (auto-detected)OPENROUTER_API_KEY— OpenRouter API key (auto-detected)LANGFUSE_PUBLIC_KEY— Langfuse public key (enables tracing)LANGFUSE_SECRET_KEY— Langfuse secret key (enables tracing)LANGFUSE_URL— Langfuse server URL (default:https://cloud.langfuse.com)KNARR_TRACE_NAME— Name for Langfuse traces (default:knarr-proxy)KNARR_API_KEY— Require this key inAuthorizationorx-api-keyheaders
For CLI documentation, see knarr.
SEE ALSO
knarr — Command-line interface
Langertha::Knarr::Config — Configuration loading and validation
Langertha::Knarr::Router — Model-to-engine routing
Langertha::Knarr::Tracing — Langfuse tracing
Langertha::Knarr::Proxy::OpenAI — OpenAI format handler
Langertha::Knarr::Proxy::Anthropic — Anthropic format handler
Langertha::Knarr::Proxy::Ollama — Ollama format handler
Langertha::Knarr::CLI — CLI entry point
build_app
my $app = Langertha::Knarr->build_app(%opts);
Build and return a Mojolicious application with all proxy routes wired up.
Options:
config— A pre-built Langertha::Knarr::Config objectconfig_file— Path to a YAML config file (used ifconfignot given)
Returns a Mojolicious application ready to be passed to Mojo::Server::Daemon or any other Mojolicious-compatible server.
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha-knarr/issues.
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.