NAME
Langertha - The clan of fierce vikings with 🪓 and 🛡️ to AId your rAId
VERSION
version 0.503
SYNOPSIS
my $system_prompt = 'You are a helpful assistant.';
# Local models via Ollama
use Langertha::Engine::Ollama;
my $ollama = Langertha::Engine::Ollama->new(
url => 'http://127.0.0.1:11434',
model => 'a small chat model you have pulled locally',
system_prompt => $system_prompt,
);
print $ollama->simple_chat('Do you wanna build a snowman?');
# OpenAI
use Langertha::Engine::OpenAI;
my $openai = Langertha::Engine::OpenAI->new(
api_key => $ENV{OPENAI_API_KEY},
model => 'a small fast model from your provider',
system_prompt => $system_prompt,
);
print $openai->simple_chat('Do you wanna build a snowman?');
# Anthropic Claude
use Langertha::Engine::Anthropic;
my $claude = Langertha::Engine::Anthropic->new(
api_key => $ENV{ANTHROPIC_API_KEY},
model => 'a frontier chat model from your provider',
);
print $claude->simple_chat('Generate Perl Moose classes to represent GeoJSON data.');
# Google Gemini
use Langertha::Engine::Gemini;
my $gemini = Langertha::Engine::Gemini->new(
api_key => $ENV{GEMINI_API_KEY},
model => 'a fast model from your provider',
);
print $gemini->simple_chat('Explain the difference between Moose and Moo.');
DESCRIPTION
Langertha provides a unified Perl interface for interacting with various Large Language Model (LLM) APIs. It abstracts away provider-specific differences, giving you a consistent API whether you're using OpenAI, Anthropic Claude, Ollama, Groq, Mistral, or other providers.
THIS API IS WORK IN PROGRESS.
Key Features
35 engines -- unified API across cloud and local LLM providers
Chat, streaming, embeddings, transcription, image generation
MCP tool calling -- automatic multi-round tool loops over any Net::Async::MCP-compatible client (see Langertha::Role::Tools)
Raider -- autonomous agent with history, compression, and plugins, shipped separately in the langertha-raider distribution
Response metadata -- token usage, model, timing, rate limits
Async/await via Future::AsyncAwait, sync via LWP::UserAgent
Langfuse observability -- traces, generations, and tool spans
Dynamic model discovery -- query provider APIs with caching
Chain-of-thought -- native extraction and
<think>tag filteringPlugin system for extending Chat, Embedder, ImageGen, and Raider
Class Sugar
Langertha can set up your package as a Raider subclass or Plugin role:
# Build a custom Raider agent
package MyAgent;
use Langertha qw( Raider );
plugin 'Langfuse';
around plugin_before_llm_call => async sub {
my ($orig, $self, $conversation, $iteration) = @_;
$conversation = await $self->$orig($conversation, $iteration);
# ... custom logic ...
return $conversation;
};
__PACKAGE__->meta->make_immutable;
# Build a custom Plugin
package MyApp::Guardrails;
use Langertha qw( Plugin );
around plugin_before_tool_call => async sub {
my ($orig, $self, $name, $input) = @_;
my @result = await $self->$orig($name, $input);
return unless @result;
return if $name eq 'dangerous_tool';
return @result;
};
use Langertha qw( Raider ) imports Moose and Future::AsyncAwait, sets Langertha::Raider as superclass, and provides the plugin function for applying plugins by short name. Langertha::Raider ships in the separate langertha-raider distribution, which must be installed for this sugar to load.
use Langertha qw( Plugin ) imports Moose and Future::AsyncAwait, and sets Langertha::Plugin as superclass.
Engine Discovery
Langertha discovers engine modules in scope via Module::Pluggable across both namespaces:
Langertha::Engine::*LangerthaX::Engine::*
Useful class methods:
Langertha->available_engine_classesReturns discovered fully-qualified engine class names.
Langertha->available_engine_idsReturns discovered engine IDs (lowercased short names).
Langertha->resolve_engine_class($name_or_class)Resolves short names (for example
OpenAI) with core-first lookup, or accepts fully-qualified class names.Langertha->new_engine($name_or_class, %args)Resolves and constructs an engine instance in one call.
Engine Modules
Each module below is a ready-to-use engine. They are built on the abstract bases Langertha::Engine::Remote, Langertha::Engine::OpenAIBase and Langertha::Engine::AnthropicBase, which are not listed here because they are meant for subclassing (including third-party engines in the LangerthaX namespace) rather than for direct use.
Langertha::Engine::Anthropic - Claude models (Sonnet, Opus, Haiku)
Langertha::Engine::OpenAI - frontier GPT models, embeddings, Whisper transcription
Langertha::Engine::OpenAIResponses - OpenAI Responses API for reasoning models
Langertha::Engine::Ollama - Local LLM hosting via https://ollama.com/
Langertha::Engine::Groq - Fast inference API
Langertha::Engine::Mistral - Mistral AI models, embeddings, Voxtral transcription
Langertha::Engine::DeepSeek - DeepSeek models
Langertha::Engine::MiniMax - MiniMax large language models via OpenAI-compatible endpoint (coding, reasoning, agentic tool use)
Langertha::Engine::MiniMaxAnthropic - MiniMax via legacy Anthropic-compatible endpoint
Langertha::Engine::Moonshot - Moonshot AI Kimi models via OpenAI-compatible endpoint
Langertha::Engine::MoonshotAnthropic - Moonshot AI Kimi via Anthropic-compatible endpoint
Langertha::Engine::Gemini - Google Gemini models (Flash, Pro), embeddings
Langertha::Engine::XAI - xAI Grok models, Imagine image generation
Langertha::Engine::vLLM - vLLM inference server
Langertha::Engine::VLLMHook - vLLM inference server with vLLM-Hook probe capture
Langertha::Engine::SGLang - SGLang inference server (chat, embeddings)
Langertha::Engine::HuggingFace - HuggingFace Inference Providers
Langertha::Engine::Perplexity - Perplexity AI models
Langertha::Engine::NousResearch - Nous Research (Hermes models)
Langertha::Engine::Cerebras - Cerebras (wafer-scale, fastest inference)
Langertha::Engine::OpenRouter - OpenRouter (300+ models, meta-provider)
Langertha::Engine::Replicate - Replicate (thousands of open-source models)
Langertha::Engine::OllamaOpenAI - Ollama via OpenAI-compatible API
Langertha::Engine::LlamaCpp - llama.cpp server (chat, embeddings)
Langertha::Engine::LMStudio - LM Studio native local REST API
Langertha::Engine::LMStudioOpenAI - LM Studio via OpenAI-compatible API
Langertha::Engine::LMStudioAnthropic - LM Studio via Anthropic-compatible API
Langertha::Engine::AKI - AKI.IO native API (EU/Germany)
Langertha::Engine::AKIOpenAI - AKI.IO via OpenAI-compatible API
Langertha::Engine::AKIAnthropic - AKI.IO via Anthropic-compatible API
Langertha::Engine::TSystems - T-Systems AI Foundation Services / LLM Hub (EU/Germany)
Langertha::Engine::Scaleway - Scaleway Generative APIs (EU)
Langertha::Engine::Hetzner - Hetzner Inference API (EU/Germany, OpenAI-compatible)
Langertha::Engine::TranscriptionBase - Slim base for OpenAI-shape transcription-only engines (no chat / tools / embeddings / image generation). Langertha::Engine::OpenAI exposes a
whisperattribute returning an instance of this class bound to the parent'sapi_key/url.Langertha::Engine::Whisper - Self-hosted Whisper-compatible transcription server (extends TranscriptionBase)
Roles
Roles provide composable functionality to engines and to the wrapper classes:
Langertha::Role::Capabilities -
engine_capabilitiesregistry plussupports($cap)helper, composed by Langertha::Role::ChatLangertha::Role::Chat - Synchronous and async chat methods, including
chat_f(messages => [...], tools => [...], tool_choice => ..., response_format => ...)for single-turn structured calls andaggregate_tool_calls(\@chunks)for streamingLangertha::Role::ThinkTag - Configurable
<think>tag filtering for reasoning models, composed by Langertha::Role::ChatLangertha::Role::HTTP - HTTP request/response handling
Langertha::Role::AsyncHTTP - Async HTTP backend selection (injected client / Net::Async::HTTP / synchronous LWP fallback), composed by Langertha::Role::Chat, Langertha::Role::Embedding, Langertha::Role::Transcription, Langertha::Role::ImageGeneration and Langertha::Role::Runtime::MetricsPoll
Langertha::Role::Streaming - Streaming response processing
Langertha::Role::JSON - JSON encode/decode
Langertha::Role::OpenAICompatible - OpenAI-compatible API behaviour
Langertha::Role::AnthropicCompatible - Anthropic-compatible API behaviour
Langertha::Role::ResponsesCompatible - Open-Responses wire envelope (OpenAI /v1/responses, Perplexity Agent API)
Langertha::Role::SystemPrompt - System prompt attribute
Langertha::Role::Temperature - Temperature parameter
Langertha::Role::ResponseSize - Max response size parameter
Langertha::Role::ResponseFormat - Response format (JSON mode)
Langertha::Role::ReasoningEffort - Request-side reasoning-effort control
Langertha::Role::PromptCache - Request-side prompt-caching control
Langertha::Role::CachedContent - Explicit cached-content resource lifecycle (create/get/list/update/delete)
Langertha::Role::ContextSize - Context window size parameter
Langertha::Role::Seed - Deterministic seed parameter
Langertha::Role::Models - Model listing
Langertha::Role::StaticModels - Model listing from a hardcoded list, for providers without a models endpoint
Langertha::Role::Embedding - Embedding generation
Langertha::Role::Transcription - Audio transcription
Langertha::Role::Tools - Tool/function calling
Langertha::Role::HermesTools - Hermes-style tool calling via
<tool_call>XML tags for models without native API tool supportLangertha::Role::ParallelToolUse - Parallel tool calling control
Langertha::Role::ServerTools - Provider-native server-side tools (
server_toolscapability and per-engine defaults)Langertha::Role::ImageGeneration - Image generation
Langertha::Role::ImageInput - Image input (vision), claimed per model
Langertha::Role::KeepAlive - Keep-alive duration for local models
Langertha::Role::RuntimeKnobs - Per-request prefix-cache runtime knobs for self-hosted engines
Langertha::Role::Runtime::MetricsPoll - Async Prometheus
/metricsscraper for self-hosted enginesLangertha::Role::PluginHost - Plugin system for the wrapper classes (and for Langertha::Raider from the langertha-raider distribution)
Langertha::Role::Runnable - Generic
run_f($ctx)execution contract, a dependency-free core primitive (consumed by the Raid/Raider nodes in the langertha-raider distribution)Langertha::Role::Langfuse - Engine-level Langfuse observability, composed by Langertha::Role::Chat
Langertha::Role::OpenAPI - OpenAPI spec support
Wrapper Classes
These classes wrap an engine with optional overrides and plugin lifecycle hooks:
Langertha::Chat - Chat wrapper with system prompt, model, and temperature overrides
Langertha::Embedder - Embedding wrapper with optional model override
Langertha::ImageGen - Image generation wrapper with model, size, and quality overrides
Plugins
Langertha::Plugin - Base class for all plugins
Langertha::Plugin::Langfuse - Langfuse observability (traces, generations, spans)
Data Objects
Langertha::Response - LLM response with content, usage, and rate limit metadata;
tool_callsis an ArrayRef of Langertha::ToolCall and the single source of truth for both native and synthesized tool callsLangertha::Usage - Token usage of one call ("usage" in Langertha::Response), normalized across providers, with cache reads/writes and whether the wire counts them inside
input_tokensLangertha::CallResult - Result of an embedding, transcription or image call (
simple_embedding_result,simple_transcription_call,simple_image_result): the value plus usage, rate limit, model and timingLangertha::Pricing - Model-to-price catalogue that turns a Langertha::Usage into a Langertha::Cost, with optional cache rates
Langertha::Cost - Monetary cost of one call (input, output, cache read/write, total)
Langertha::UsageRecord - Ledger entry combining a Langertha::Usage, its Langertha::Cost and request metadata
Langertha::ToolCall - Canonical tool invocation produced by an LLM, with
syntheticflag for forced-tool fallbacksLangertha::ToolChoice - Canonical tool-selection policy with per-provider serializers (
to_openai,to_anthropic,to_gemini, legacyto_perplexity)Langertha::Tool - Canonical tool definition with cross-provider serializers (
to_openai,to_anthropic,to_gemini,to_mcp,to_json_schema) and accepting constructors (from_openai,from_anthropic,from_mcp,from_gemini,from_hash)Langertha::ServerTool - Provider-native server-side tool (web search, file search, remote MCP, ...), pinned to its
tool_wire_formatLangertha::ServerToolCall - Record of a tool call the provider ran itself, on "server_tool_calls" in Langertha::Response (never on
tool_calls)Langertha::Content / Langertha::Content::Image - Provider-agnostic vision input
Langertha::ModelProbe - Reads model-scoped capability facts (
image_input) from a provider's own model metadata, for "probe_model_capabilities_f" in Langertha::Role::CapabilitiesLangertha::Manifest - Provider manifest (
/.well-known/langertha.json) value object, parser and validatorLangertha::Manifest::Builder - Builds a Langertha::Manifest offline from configured engines, never copying a secret
Langertha::RateLimit - Normalized rate limit data from HTTP response headers
Langertha::Moment - Instant reported by a provider ("created" in Langertha::Response); a Time::Moment subclass that keeps the sub-seconds and numifies to the Unix epoch
Langertha::Stream - Iterator over streaming chunks
Langertha::Stream::Chunk - A single chunk from a streaming response (with optional
tool_callsfor engines that emit them mid-stream)Langertha::Request::HTTP - Internal HTTP request object
Streaming
All engines that implement Langertha::Role::Chat support streaming. There are several ways to consume a stream:
Synchronous with callback:
$engine->simple_chat_stream(sub {
my ($chunk) = @_;
print $chunk->content;
}, 'Tell me a story');
Synchronous with iterator (Langertha::Stream):
my $stream = $engine->simple_chat_stream_iterator('Tell me a story');
while (my $chunk = $stream->next) {
print $chunk->content;
}
Async with Future (traditional):
my $future = $engine->simple_chat_f('Hello');
my $response = $future->get;
my $future = $engine->simple_chat_stream_f('Tell me a story');
my ($content, $chunks) = $future->get;
Async with Future::AsyncAwait (recommended):
use Future::AsyncAwait;
async sub chat_with_ai {
my ($engine) = @_;
my $response = await $engine->simple_chat_f('Hello');
say "AI says: $response";
return $response;
}
async sub stream_chat {
my ($engine) = @_;
my ($content, $chunks) = await $engine->simple_chat_stream_realtime_f(
sub { print shift->content },
'Tell me a story',
);
say "\nReceived ", scalar(@$chunks), " chunks";
return $content;
}
chat_with_ai($engine)->get;
stream_chat($engine)->get;
The _f methods pick their HTTP backend through Langertha::Role::AsyncHTTP: an injected _async_http client, else Net::Async::HTTP on an IO::Async loop (both loaded lazily only when you call them), else a synchronous LWP::UserAgent fallback. The fallback keeps every _f method working and returning a Future, but blocking and sequential, with no concurrency; install Net::Async::HTTP + IO::Async for real async. See examples/async_await_example.pl for complete working examples.
Using with Mojolicious:
use Mojo::Base -strict;
use Future::Mojo;
use Langertha::Engine::OpenAI;
my $openai = Langertha::Engine::OpenAI->new(
api_key => $ENV{OPENAI_API_KEY},
model => 'a small fast model from your provider',
);
my $future = $openai->simple_chat_stream_realtime_f(
sub { print shift->content },
'Hello!',
);
$future->on_done(sub {
my ($content, $chunks) = @_;
say "Done: $content";
});
Mojo::IOLoop->start;
Response Metadata
simple_chat returns Langertha::Response objects that stringify to text content (backward compatible) but carry full metadata:
my $r = $engine->simple_chat('Hello!');
print $r; # prints the text
say $r->model; # actual model used
say $r->prompt_tokens; # input tokens
say $r->completion_tokens; # output tokens
say $r->total_tokens; # total
say $r->finish_reason; # stop, end_turn, tool_calls, ...
say $r->thinking; # chain-of-thought (if available)
Rate Limiting
Rate limit information from HTTP response headers is extracted automatically into Langertha::RateLimit objects. Available per-response and on the engine:
if ($r->has_rate_limit) {
say $r->requests_remaining;
say $r->tokens_remaining;
say $r->rate_limit->requests_reset;
}
# Engine always reflects the latest response
say $engine->rate_limit->requests_remaining
if $engine->has_rate_limit;
Supported: OpenAI, Groq, Cerebras, OpenRouter, Replicate, HuggingFace (x-ratelimit-*) and Anthropic (anthropic-ratelimit-*).
MCP Tool Calling
Integrates with any Net::Async::MCP-compatible client (for example a Net::Async::MCP client as used by the langertha-raider distribution) for automatic multi-round tool calling:
my $engine = Langertha::Engine::OpenAI->new(
api_key => $ENV{OPENAI_API_KEY},
mcp_servers => [$mcp],
);
my $response = await $engine->chat_with_tools_f('Search for Perl modules');
Works with all engines that support tool calling. See Langertha::Role::Tools.
Raider (Autonomous Agent)
Langertha::Raider is a stateful agent with conversation history, MCP tool calling, context compression, session history, and a plugin system. It ships in the separate langertha-raider distribution; install it to use the agent and the use Langertha qw( Raider ) sugar:
my $raider = Langertha::Raider->new(
engine => $engine,
mission => 'You are a code explorer.',
);
my $r1 = await $raider->raid_f('What files are in lib/?');
my $r2 = await $raider->raid_f('Read the main module.');
Langfuse Observability
Langfuse observability comes in two complementary flavours. Both read LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY and the optional LANGFUSE_URL from the environment, and both stay inactive until that key pair is set.
Engine level -- Langertha::Role::Langfuse is composed by Langertha::Role::Chat, so every chat engine carries it. It auto-instruments the synchronous
simple_chatcall and offerslangfuse_trace,langfuse_spanandlangfuse_generationfor instrumenting anything else. Langertha::Raider uses those to trace raids, iterations and tool calls.Plugin level -- Langertha::Plugin::Langfuse attaches to any Langertha::Role::PluginHost, which is what the wrapper classes Langertha::Chat, Langertha::Embedder and Langertha::ImageGen are (as is Langertha::Raider). It needs no engine-level configuration and covers the embedding and image-generation calls the engine role does not see:
my $chat = Langertha::Chat->new( engine => $engine, plugins => ['Langfuse'], );
Transcription-only engines (Langertha::Engine::Whisper and other Langertha::Engine::TranscriptionBase subclasses) compose neither Langertha::Role::Chat nor a plugin host, and so are not instrumented.
Extensions
The LangerthaX namespace is reserved for third-party extensions. See LangerthaX.
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha/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 <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.