Changes for version 0.500 - 2026-04-26
- !!! Heads-up for callers upgrading from 0.404 — items marked [BREAKING] !!! below may need code changes; everything else is additive.
- [BREAKING] Langertha::Response->tool_calls is now ArrayRef[Langertha:: ToolCall] (was ArrayRef[HashRef]). Code that read $r->tool_calls->[0] ->{name}/->{arguments}/->{id}/->{synthetic} as hash keys must switch to ->name / ->arguments / ->id / ->synthetic method calls. The Response constructor still accepts the old HashRef form and upgrades transparently (BUILDARGS), so passing tool_calls in is unchanged; only consumption changed. tool_call_args() is unchanged.
- [BREAKING] Langertha::Engine::Whisper no longer extends Langertha::Engine::OpenAI. It now extends the new Langertha::Engine::TranscriptionBase, so a Whisper instance no longer has simple_chat / chat_f / chat_with_tools_f / embedding / simple_image / Tools / ImageGeneration / Embedding methods. Existing code that called only transcription methods is unaffected. To get a Whisper handle from an OpenAI engine without restating credentials use the new $openai->whisper attribute.
- [BREAKING] Langertha::Role::ResponseFormat::decode_loose_json is now a method on the role, not a free function. Code that called Langertha::Role::ResponseFormat::decode_loose_json($text) directly must switch to $engine->decode_loose_json($text). This makes it overridable per engine for providers that need a custom strategy. The standalone Langertha::Util that briefly existed has been removed for the same reason.
- New Langertha::Engine::TranscriptionBase: slim base class for OpenAI-shape transcription-only engines (composes OpenAICompatible, OpenAPI, Models, Transcription, Capabilities — no Chat / Tools / Embedding / ImageGeneration). Whisper now extends it.
- Langertha::Engine::OpenAI gained a `whisper` lazy attribute that returns a Langertha::Engine::TranscriptionBase configured with the parent's api_key/url and `whisper-1` as transcription_model. `$openai->whisper->simple_transcription($file)` is the canonical way to use OpenAI's hosted Whisper from a chat-side engine.
- New Langertha::Role::Capabilities, composed by Langertha::Role:: Chat (and therefore present on every engine via composition). One central role-to-flag map drives engine_capabilities; engines override via `around engine_capabilities` for wire-reality corrections. Capabilities reported by each role: Chat -> chat Streaming -> streaming Tools -> tools_native + tool_choice_{auto,any,none,named} HermesTools -> tools_hermes ResponseFormat -> response_format_json_object/json_schema Embedding -> embedding Transcription -> transcription ImageGeneration -> image_generation Temperature -> temperature Seed -> seed ContextSize -> context_size ResponseSize -> response_size SystemPrompt -> system_prompt ParallelToolUse -> parallel_tool_use The earlier `does()`-based heuristic in Role::Chat is gone; `$engine->supports($cap)` is the canonical query.
- Langertha::Tool gained from_mcp (camelCase inputSchema), from_gemini (flat `parameters`), to_gemini, to_mcp, to_json_schema. from_hash now auto-detects MCP / Anthropic / Gemini shapes in addition to OpenAI. This kills the input_schema/inputSchema/parameters/ function.parameters chaos that used to live in chat_f.
- Langertha::ToolCall gained a `synthetic` boolean attribute (false by default) and a from_gemini constructor; ToolCall->extract now pulls Gemini functionCall parts out of candidates[0].content.parts.
- Langertha::Response.tool_calls is now populated by every native tool-calling engine (OpenAICompatible, AnthropicBase, Gemini, Ollama) as well as the chat_f synthetic-tool fallback path. Single source of truth — same shape regardless of provider. Langertha::Response gained tool_call($name) returning the matching Langertha::ToolCall object (vs. tool_call_args returning args).
- Langertha::Stream::Chunk gained an optional tool_calls attribute (ArrayRef[Langertha::ToolCall]). Langertha::Role::Chat got aggregate_tool_calls($chunks) for collecting them after a stream ends. Per-engine streaming tool-call delta accumulation will land incrementally; the structures are in place.
- Langertha::Engine::AnthropicBase, Langertha::Engine::Gemini, and Langertha::Engine::Ollama now compose Langertha::Role:: ResponseFormat. Anthropic emulates response_format via a synthesized tool plus forced tool_choice (the chat_response parser lifts the resulting tool_use input back into Response.content as JSON). Gemini translates response_format into generationConfig (responseMimeType + responseSchema). Ollama translates into the `format` parameter (string 'json' for json_object, schema HashRef for json_schema). The legacy Ollama json_format attribute still works as a fallback when response_format isn't set.
- Langertha::Engine::OpenAIBase now composes Langertha::Role::ResponseFormat, so every OpenAI-compatible engine (Perplexity, DeepSeek, Groq, Mistral, MiniMax, Cerebras, OpenRouter, Replicate, HuggingFace, AKIOpenAI, TSystems, Scaleway, Ollama-OpenAI, vLLM, SGLang, LlamaCpp, NousResearch) accepts a response_format constructor argument. Removed redundant individual ResponseFormat composition from those engines.
- Langertha::ToolChoice gained to_perplexity (string-only API: auto/none/ required, named coerces to required) and to_gemini (toolConfig. functionCallingConfig with mode AUTO/ANY/NONE plus allowed_function_names for named forcing) serializers.
- Langertha::Engine::Gemini chat_request and chat_stream_request now translate tool_choice in any input shape (canonical / OpenAI / Anthropic) into Gemini's toolConfig payload.
- Langertha::Role::Chat got chat_f, a named-arguments async entry point: $engine->chat_f(messages => [...], tools => [...], tool_choice => ..., response_format => ...). simple_chat_f delegates to it; existing @messages-style call sites are unchanged. Forced-named tool calls on engines that lack native named-tool-forcing but support json_schema response_format (currently Perplexity) are auto-rewritten through the response_format path; the response text is loose-parsed (handles ```json fences and prose-wrapped JSON) and a synthetic tool_calls entry is attached so callers see the same shape regardless of provider.
- Langertha::Response gained a tool_calls attribute and tool_call_args accessor; clone_with carries tool_calls through.
- Langertha::Role::Chat exposes engine_capabilities (default derived from role composition) and a supports($cap) helper so software can query what the engine can honour before sending parameters.
- Langertha::Role::ResponseFormat gained decode_loose_json($text), a tolerant decoder for structured-output responses that may be wrapped in code fences or prose.
- New Langertha::Engine::TSystems for the T-Systems AI Foundation Services / LLM Hub OpenAI-compatible endpoint (https://llm-server.llmhub.t-systems.net/v2). Bearer auth via LANGERTHA_TSYSTEMS_API_KEY, default model gpt-oss-120b (T-Cloud, Germany; reliable tool calling), supports chat, streaming, tool calling, embeddings (default text-embedding-bge-m3) and structured output. GDPR-compliant; T-Cloud models are processed in Germany, hyperscaler models in the EU.
- New Langertha::Engine::Scaleway for Scaleway Generative APIs (https://api.scaleway.ai/v1) — EU-hosted, drop-in OpenAI-compatible replacement. Bearer auth via LANGERTHA_SCALEWAY_API_KEY, default model llama-3.1-8b-instruct, supports chat, streaming, tool calling, embeddings and structured output.
Documentation
Simple chat with Ollama
Simple chat with OpenAI
Simple script to check the model list on an OpenAI compatible API
Simple transcription with a Whisper compatible server or OpenAI
Modules
The clan of fierce vikings with 🪓 and 🛡️ to AId your rAId
Chat abstraction wrapping an engine with optional overrides
Base role for canonical multimodal content blocks with cross-provider serialization
Canonical image content block with cross-provider conversion (OpenAI / Anthropic / Gemini)
Immutable value object for the monetary cost of a single LLM call
Embedding abstraction wrapping an engine with optional model override
AKI.IO native API
AKI.IO via OpenAI-compatible API
Anthropic API
Base class for Anthropic-compatible engines
Cerebras Inference API
DeepSeek API
Google Gemini API
GroqCloud API
HuggingFace Inference Providers API
LM Studio native REST API
LM Studio via Anthropic-compatible API
LM Studio via OpenAI-compatible API
llama.cpp server
MiniMax API (OpenAI-compatible)
MiniMax API via Anthropic-compatible endpoint (legacy)
Mistral API
Nous Research Inference API
Ollama API
Ollama via OpenAI-compatible API
OpenAI API
Base class for OpenAI-compatible engines
OpenRouter API
Perplexity Sonar API
Base class for all remote engines
Replicate API
SGLang inference server
Scaleway Generative APIs
T-Systems AI Foundation Services (LLM Hub)
Base class for OpenAI-compatible transcription-only engines
Whisper compatible transcription server
vLLM inference server
Image generation abstraction wrapping an engine with optional overrides
Request input transformation helpers
Backwards-compat facade over Langertha::Tool / Langertha::ToolChoice
Backwards-compat facade over Langertha::Usage / Pricing / Cost / UsageRecord
Response output transformation helpers
Backwards-compat facade over Langertha::ToolCall
Base class for plugins
Langfuse observability plugin for any PluginHost
Model→price catalog producing Langertha::Cost from Langertha::Usage
Base class for orchestrating Runnable steps
Looping Raid orchestrator
Parallel Raid orchestrator with branched context isolation
Sequential Raid orchestrator
Autonomous agent with conversation history and MCP tools
Result object from a Raider raid
Rate limit information from API response headers
A HTTP Request inside of Langertha
LLM response with metadata
Common result object for Raider and Raid execution
Engine-capability registry derived from composed roles
Role for APIs with normal chat functionality
Role for an engine where you can specify the context size (in tokens)
Role for APIs with embedding functionality
Role for HTTP APIs
Hermes-style tool calling via XML tags
Role for engines that support image generation
Role for JSON
Role for engines that support keep-alive duration
Langfuse observability integration
Role for APIs with several models
Role for OpenAI-compatible API format
Role for APIs with OpenAPI definition
Role for an engine that supports parallel tool calling control
Role for objects that host plugins (Raider, Engine)
Role for an engine where you can specify structured output
Role for an engine where you can specify the response size (in tokens)
Common async execution contract for Raider and Raid nodes
Role for an engine that can set a seed
Role for engines with a hardcoded model list
Role for streaming support
Role for APIs with system prompt
Role for an engine that can have a temperature setting
Configurable think tag filtering for reasoning models
Role for MCP tool calling support
Role for APIs with transcription functionality
Shared execution context for Raid and Raider runs
Pre-computed OpenAPI operations for LM Studio native API
Pre-computed OpenAPI operations for Mistral
Pre-computed OpenAPI operations for Ollama
Pre-computed OpenAPI operations for OpenAI
Iterator for streaming responses
Represents a single chunk from a streaming response
Immutable canonical tool definition with cross-provider format conversion
Immutable canonical tool invocation emitted by an LLM
Immutable canonical tool-selection policy with cross-provider conversion
Immutable value object for LLM token usage with cross-provider conversion
Tagged ledger entry combining Usage, Cost, and request metadata
Bring your own viking!
Examples
- ex/async_await.pl
- ex/ctx.pl
- ex/embedding.pl
- ex/hermes_tools.pl
- ex/ircbot.pl
- ex/json_grammar.pl
- ex/langfuse-k8s.yaml
- ex/langfuse.pl
- ex/logic.pl
- ex/mcp_inprocess.pl
- ex/mcp_stdio.pl
- ex/ollama.pl
- ex/ollama_image.pl
- ex/raider.pl
- ex/raider_plugin_sugar.pl
- ex/raider_rag.pl
- ex/raider_run.pl
- ex/response.pl
- ex/sample.ogg
- ex/streaming_anthropic.pl
- ex/streaming_callback.pl
- ex/streaming_future.pl
- ex/streaming_gemini.pl
- ex/streaming_iterator.pl
- ex/streaming_mojo.pl
- ex/structured_code.pl
- ex/structured_output.pl
- ex/structured_sentences.pl
- ex/synopsis.pl
- ex/transcription.pl