| name | langertha |
| description | Langertha LLM framework — Engine creation, Raider autonomous agents, MCP tool integration, plugin system |
Langertha is a Perl LLM framework with provider-agnostic engines, autonomous Raider agents, MCP tool integration, and a plugin pipeline. Use Future::AsyncAwait for async operations.
## Engine Creation
use Langertha::Engine::Anthropic;
my $claude = Langertha::Engine::Anthropic->new(
api_key => $ENV{ANTHROPIC_API_KEY},
model => 'claude-sonnet-4-6',
system_prompt => 'You are helpful.',
);
use Langertha::Engine::OpenAI;
my $gpt = Langertha::Engine::OpenAI->new(
api_key => $ENV{OPENAI_API_KEY},
model => 'gpt-4o',
);
my $local = Langertha::Engine::OllamaOpenAI->new(
url => 'http://localhost:11434/v1',
model => 'llama3',
);
my $proxy = Langertha::Engine::OpenAI->new(
url => 'http://127.0.0.1:5000/api/v1',
model => $model_key,
api_key => 'proxy',
);
Available Engine Families
| Base | Engines |
|---|
| AnthropicBase | Anthropic, MiniMax, LMStudioAnthropic |
| OpenAIBase | OpenAI, DeepSeek, Groq, Mistral, Cerebras, OpenRouter, Replicate, HuggingFace, Perplexity, OllamaOpenAI, vLLM, SGLang, LlamaCpp, AKIOpenAI |
| Other | Gemini (Google), Ollama (native), AKI (EU) |
## Simple Chat
my $response = $engine->simple_chat('What is Perl?');
print $response;
use Future::AsyncAwait;
my $response = await $engine->simple_chat_f('Tell me a story.');
say $response->model;
say $response->prompt_tokens;
say $response->completion_tokens;
say $response->thinking;
Langertha::Response overloads "" so it works in string contexts.
## Tool Calling with MCP
Step 1: Create MCP Server with tools
use MCP::Server;
my $server = MCP::Server->new(name => 'my-tools', version => '1.0');
$server->tool(
name => 'search_files',
description => 'Search for files matching a pattern',
input_schema => {
type => 'object',
properties => {
pattern => { type => 'string', description => 'Glob pattern' },
path => { type => 'string', description => 'Directory to search' },
},
required => ['pattern'],
},
code => sub {
my ($tool, $args) = @_;
my @files = glob("$args->{path}/$args->{pattern}");
return $tool->text_result(join("\n", @files));
},
);
Step 2: Create MCP client
use IO::Async::Loop;
use Net::Async::MCP;
my $loop = IO::Async::Loop->new;
my $mcp = Net::Async::MCP->new(server => $server);
$loop->add($mcp);
await $mcp->initialize;
Step 3: Engine with MCP
my $engine = Langertha::Engine::Anthropic->new(
api_key => $ENV{ANTHROPIC_API_KEY},
model => 'claude-sonnet-4-6',
mcp_servers => [$mcp],
);
my $response = await $engine->chat_with_tools_f('Find all .pm files in lib/');
say $response;
## Raider — Autonomous Agent
use Langertha::Raider;
my $raider = Langertha::Raider->new(
engine => $engine,
mission => 'You are a code reviewer.',
max_iterations => 10,
max_context_tokens => 4000,
context_compress_threshold => 0.75,
compression_engine => $cheap_model,
raider_mcp => 1,
plugins => ['Langfuse'],
);
my $result = await $raider->raid_f('Review lib/App.pm');
say $result;
say $result->is_question;
say $result->is_abort;
my $r2 = await $raider->raid_f('Now suggest improvements.');
if ($result->is_question) {
my $next = await $raider->respond_f('Yes, go ahead.');
}
$raider->add_history('user', $content);
$raider->clear_history;
my $m = $raider->metrics;
say "Iterations: $m->{iterations}";
say "Tool calls: $m->{tool_calls}";
Raid Loop (simplified)
- Auto-compress history if context threshold exceeded
- Gather tools from MCP servers + inline tools + self-tools
- Build conversation: mission + history + new messages
- Call LLM with tools
- If tool calls: execute via MCP, add results to conversation, loop
- If no tool calls: extract final text, persist to history, return result
- Max iterations safety limit
## Plugin System
package Langertha::Plugin::MyGuardrails;
use Langertha qw( Plugin );
async sub plugin_before_tool_call {
my ($self, $name, $input) = @_;
return if $name eq 'dangerous_tool';
return ($name, $input);
}
async sub plugin_after_raid {
my ($self, $result) = @_;
return $result;
}
__PACKAGE__->meta->make_immutable;
my $raider = Langertha::Raider->new(
engine => $engine,
plugins => ['MyGuardrails', 'Langfuse'],
);
Plugin Hooks (all async sub)
| Hook | Purpose |
|---|
plugin_before_raid(@messages) | Transform input |
plugin_build_conversation(@conv) | Transform assembled conversation |
plugin_before_llm_call(@conv, $iter) | Transform before each LLM call |
plugin_after_llm_response($data, $iter) | Inspect LLM response |
plugin_before_tool_call($name, $input) | Allow/block tool (empty = skip) |
plugin_after_tool_call($name, $input, $result) | Transform tool result |
plugin_after_raid($result) | Transform final result |
## Composable Roles
Engines compose feature roles:
| Role | Feature |
|---|
Langertha::Role::Chat | simple_chat, simple_chat_f |
Langertha::Role::Tools | chat_with_tools_f (MCP loop) |
Langertha::Role::Streaming | SSE/NDJSON streaming |
Langertha::Role::Embedding | Vector embeddings |
Langertha::Role::Transcription | Audio-to-text |
Langertha::Role::ImageGeneration | Image generation |
Langertha::Role::SystemPrompt | System prompt management |
Langertha::Role::Temperature | Generation parameters |
Langertha::Role::ResponseFormat | JSON mode / structured output |
Langertha::Role::Models | Model listing |
Langertha::Role::Langfuse | Observability |
Langertha::Role::HermesTools | XML tag tool calling |
Langertha::Role::ThinkTag | Chain-of-thought filtering |