Skip to main content

Crate neuron_loop

Crate neuron_loop 

Source
Expand description

§neuron-loop

crates.io docs.rs license

The agentic while-loop for the neuron ecosystem. Composes a Provider, a ToolRegistry, and a ContextStrategy into a loop that sends messages to an LLM, executes tool calls, manages context compaction, and repeats until the model produces a final response or a turn limit is reached.

§Installation

cargo add neuron-loop

§Key Types

  • AgentLoop<P, C> – the core loop, generic over Provider and ContextStrategy
  • AgentLoopBuilder<P, C> – builder for constructing an AgentLoop with optional configuration
  • LoopConfig – system prompt, max turns, parallel tool execution flag
  • AgentResult – final output: response text, all messages, cumulative token usage, turn count
  • TurnResult – per-turn result for step-by-step iteration:
    • ToolsExecuted { calls, results } — tool calls were made and executed
    • FinalResponse(AgentResult) — model produced a final text response
    • CompactionOccurred { old_tokens, new_tokens } — context was compacted
    • MaxTurnsReached — turn limit hit
    • Error(LoopError) — something failed

§Usage

Build an AgentLoop using the builder pattern. Only provider and context are required; tools, config, hooks, and durability are optional with sensible defaults.

use neuron_loop::{AgentLoop, LoopConfig};
use neuron_tool::ToolRegistry;
use neuron_context::SlidingWindowStrategy;
use neuron_types::ToolContext;

// Set up components
let provider = MyProvider::new("claude-sonnet-4-20250514");
let context = SlidingWindowStrategy::new(20, 100_000);

let mut tools = ToolRegistry::new();
tools.register(MyTool);

// Build and run
let mut agent = AgentLoop::builder(provider, context)
    .tools(tools)
    .system_prompt("You are a helpful assistant.")
    .max_turns(10)
    .parallel_tool_execution(true)
    .build();

let tool_ctx = ToolContext { /* ... */ };
let result = agent.run_text("What is 2 + 2?", &tool_ctx).await?;

println!("Response: {}", result.response);
println!("Turns: {}", result.turns);
println!("Tokens used: {} in, {} out",
    result.usage.input_tokens,
    result.usage.output_tokens);

For step-by-step iteration (streaming UIs, custom control flow, or injecting messages between turns):

use neuron_loop::{AgentLoop, TurnResult};
use neuron_types::{Message, Role, ContentBlock, ToolContext};

let mut agent = AgentLoop::builder(provider, context).tools(tools).build();
let user_msg = Message {
    role: Role::User,
    content: vec![ContentBlock::Text("What is 2 + 2?".into())],
};
let tool_ctx = ToolContext::default();
let mut steps = agent.run_step(user_msg, &tool_ctx);

while let Some(turn) = steps.next().await {
    match turn {
        TurnResult::ToolsExecuted { calls, .. } => {
            println!("executed {} tool calls", calls.len());
        }
        TurnResult::FinalResponse(result) => {
            println!("final: {}", result.response);
            break;
        }
        TurnResult::CompactionOccurred { old_tokens, new_tokens } => {
            println!("compacted {old_tokens} -> {new_tokens} tokens");
        }
        TurnResult::MaxTurnsReached => break,
        TurnResult::Error(e) => { eprintln!("error: {e}"); break; }
    }
}

Add observability hooks to log, meter, or control loop execution:

let agent = AgentLoop::builder(provider, context)
    .hook(my_logging_hook)
    .durability(my_temporal_context)
    .build();

§Cancellation

The loop respects ToolContext.cancellation_token (a tokio_util::sync::CancellationToken). Cancellation is checked at the top of each iteration and before tool execution. When cancelled, the loop returns LoopError::Cancelled.

use tokio_util::sync::CancellationToken;

let token = CancellationToken::new();
let tool_ctx = ToolContext {
    cancellation_token: token.clone(),
    // ...
};

// Cancel from another task
tokio::spawn(async move {
    tokio::time::sleep(Duration::from_secs(5)).await;
    token.cancel();
});

let result = agent.run_text("Long task", &tool_ctx).await;
// Returns Err(LoopError::Cancelled) if cancelled

§Parallel Tool Execution

When LoopConfig.parallel_tool_execution is true and the model returns multiple tool calls in a single response, all calls execute concurrently. When false (the default), tool calls execute sequentially in order.

let mut agent = AgentLoop::builder(provider, context)
    .parallel_tool_execution(true)
    .build();

§Architecture

AgentLoop depends on neuron-types (traits) and neuron-tool (ToolRegistry). RPITIT traits (Provider, ObservabilityHook, DurableContext) are type-erased internally via BoxedHook and BoxedDurable wrappers for dyn-compatibility — you never construct these directly; they’re created by .hook() and .durability() on the builder. The ContextStrategy is used as a generic parameter.

§Part of neuron

This crate is part of neuron, a composable building-blocks library for AI agents in Rust.

§License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.

Re-exports§

pub use config::*;
pub use loop_impl::*;
pub use step::*;

Modules§

config
Configuration types for the agentic loop.
loop_impl
Core AgentLoop struct and run methods.
step
Step-by-step iteration types for the agentic loop.