loopctl
A trait-based framework for building agent loops with pluggable LLM clients, tools, and memory.
Overview
loopctl provides the core infrastructure for building LLM-based agent loops: a streaming
API client abstraction, tool registry, loop detection and convergence, fallback model chains,
cancellation, and a default loop engine (BareLoop). You bring your own LLM provider client
and tool implementations; the framework handles the rest.
Modules
| Module | Description |
|---|---|
api |
ApiClient trait for LLM provider communication (streaming + non-streaming) |
api::error |
API error types with retry classification |
cancel |
Cooperative cancellation via CancelSignal (AtomicBool + tokio::Notify) |
capabilities |
Capability traits (Observable, Detectable, Compactable, etc.) |
compact |
Context compaction: ContextCompactor trait, TruncatingCompactor, TokenSplitter |
config |
Session configuration (LoopConfig) |
detection |
Loop detection, convergence detection, DetectionManager |
engine |
BareLoop<C> — the default agent loop engine (stream → accumulate → dispatch tools → repeat) |
error |
Central LoopError enum for all framework operations |
fallback |
Circuit-breaker pattern for automatic API model fallback (FallbackManager) |
memory |
LoopMemory trait and entry types; memory::builtin provides InMemoryStore |
message |
Conversation types: Message, MessagePart, ToolContent, roles |
middleware |
Tool dispatch pipeline: timeouts, permissions, output limits, unknown-tool handling |
observer |
LoopObserver trait and ObserverHost for lifecycle event observation |
reflection |
Failure reflection and recovery strategies (Reflector, RecoveryStrategy) |
runtime |
LoopRuntime — the default infrastructure bundle |
stream |
Streaming event types, accumulator, stop reasons, usage tracking |
tool |
Tool trait, ToolRegistry, ToolSchema, ToolOutput, FnTool adapter |
hooks |
Bidirectional lifecycle control (allow/block/ask before tool use). Requires hooks feature. |
testing |
Mock API client, mock tools, and test fixture factories. Requires testing feature. |
Quick Start
Implement a Tool
use ;
use ;
use Pin;
use Future;
;
Run an Agent Loop
use BareLoop;
use Loop;
use ToolRegistry;
use LoopConfig;
use Arc;
// 1. Bring your own API client (implements ApiClient trait)
# ;
# use ApiClient;
#
let client = new;
// 2. Register tools
let mut registry = new;
// registry.register(EchoTool);
// 3. Configure
let config = LoopConfig ;
// 4. Run
let agent = new;
// let result = agent.run("Use the echo tool to say hello").await?;
// println!("Completed in {} turns", result.total_turns);
Use the Testing Module
[]
= { = "0.1", = ["testing"] }
use ;
use BareLoop;
use Loop;
use ToolRegistry;
use Arc;
let mut client = new;
client = client.with_text_response;
let mut registry = new;
registry.register;
let agent = new;
// let result = agent.run("test input").await?;
Feature Flags
| Feature | Default | Depends on | Description |
|---|---|---|---|
hooks |
No | — | Bidirectional lifecycle hooks (allow/block/ask before tool use, compaction) |
testing |
No | — | Mock clients, tools, and test fixtures |
tool_health |
No | — | Per-tool health monitoring, circuit breakers, and self-healing routing |
tool_shield |
No | tool_health |
Tool permission shielding and access control |
providers |
No | — | Base provider support (reqwest + async-stream); enables provider module |
openai |
No | providers |
OpenAI-compatible API client (provider::openai) |
anthropic |
No | providers |
Anthropic Claude API client (provider::anthropic) |
ollama |
No | providers, openai |
Ollama local model client (OpenAI-compatible) |
deepseek |
No | providers, openai |
DeepSeek API client (OpenAI-compatible) |
grok |
No | providers, openai |
Grok (xAI) API client (OpenAI-compatible) |
gemini |
No | providers |
Google Gemini API client (provider::gemini) |
zai |
No | providers, anthropic |
Z.AI API client (Anthropic-compatible) |
Architecture
At the center is BareLoop, the default agent loop. Each turn it streams a response from an ApiClient (your LLM provider), accumulates the result, and dispatches any requested tool calls through a ToolRegistry. Results are fed back into the conversation and the cycle repeats until the model ends its turn or a configured limit is reached.
Two cross-cutting concerns run alongside the main loop:
- Detection & Fallback — convergence detection, loop detection, and automatic model/API fallback when requests fail.
- ToolRegistry — holds your registered tools and routes tool calls to them.
Development
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.