Skip to content

IterativeLoopOptions

NexusLabs.Needlr.AgentFramework

NexusLabs.Needlr.AgentFramework.Iterative

IterativeLoopOptions Class

Configuration for a single run of an IIterativeAgentLoop.

public sealed class IterativeLoopOptions

Inheritance System.Object 🡒 IterativeLoopOptions

Example

var options = new IterativeLoopOptions
{
    LoopName = "article-writer",
    Instructions = "You are a travel article writer...",
    Tools = [searchTool, writeTool, outlineTool],
    PromptFactory = ctx =>
    {
        var article = ctx.Workspace.FileExists("article.md")
            ? ctx.Workspace.ReadFile("article.md")
            : "(empty)";
        return $"Continue writing. Current article:\n{article}";
    },
    MaxIterations = 20,
    ToolResultMode = ToolResultMode.OneRoundTrip,
};

var result = await iterativeLoop.RunAsync(options, cancellationToken);

Remarks

At minimum, callers must provide Instructions (the system prompt), Tools (available tool functions), and PromptFactory (builds the user message each iteration from workspace state).

The loop terminates when any of these conditions is met (checked in order): 1. The System.Threading.CancellationToken is cancelled. 2. MaxIterations is reached. 3. IsComplete returns true. 4. The model produces a text response without requesting tool calls (natural completion).

Properties

IterativeLoopOptions.BudgetPressureInstruction Property

Gets or sets the instruction prepended to the user message on the budget-pressure finalization iteration.

public string BudgetPressureInstruction { get; set; }

Property Value

System.String

IterativeLoopOptions.BudgetPressureThreshold Property

Gets or sets the fraction of the token budget at which the loop injects a finalization instruction. When CurrentTokens divided by MaxTokens reaches this value, the loop prepends BudgetPressureInstruction to the next iteration's prompt and runs one final iteration before terminating with BudgetPressure.

public System.Nullable<double> BudgetPressureThreshold { get; set; }

Property Value

System.Nullable<System.Double>

Exceptions

System.ArgumentOutOfRangeException
Value is not between 0 and 1.

Remarks

Defaults to null (disabled). Set to e.g. 0.8 (80%) to give the agent one iteration to finalize cleanly before the hard budget limit cancels the chat client. Must be between 0.0 and 1.0 (exclusive) when set.

IterativeLoopOptions.ChatClientFactory Property

Optional factory that wraps the Microsoft.Extensions.AI.IChatClient used for LLM calls within this loop run. Use this to inject per-loop middleware such as ReducingChatClient to cap within-iteration conversation growth. When null, the global chat client from IChatClientAccessor is used unmodified.

public System.Func<Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient>? ChatClientFactory { get; set; }

Property Value

System.Func<Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient>

Example

var loopOptions = new IterativeLoopOptions
{
    ChatClientFactory = inner => new ChatClientBuilder(inner)
        .UseChatReducer(new MessageCountingChatReducer(maxNonSystemMessages: 20))
        .Build(),
};

IterativeLoopOptions.ExecutionContext Property

An optional IAgentExecutionContext to use for bridging workspace state to DI-resolved tools.

public NexusLabs.Needlr.AgentFramework.Context.IAgentExecutionContext? ExecutionContext { get; set; }

Property Value

IAgentExecutionContext

Remarks

When set, the loop uses this context (and its workspace) for the IAgentExecutionContextAccessor scope. When null (the default), the loop auto-creates a context from the Workspace if an accessor is available via DI.

This is the bootstrap execution context only — it is NOT the same as the full application execution context. It exists solely so that DI-resolved tool classes can call accessor.Current.GetRequiredWorkspace() during loop execution.

IterativeLoopOptions.Instructions Property

Gets or sets the system prompt (instructions) for the agent. Sent as the system message on every LLM call. This is constant across all iterations.

public string Instructions { get; set; }

Property Value

System.String

IterativeLoopOptions.IsComplete Property

Gets or sets an optional predicate evaluated after each iteration. When it returns true, the loop terminates. The predicate receives the IterativeContext with updated workspace and tool results.

public System.Func<NexusLabs.Needlr.AgentFramework.Iterative.IterativeContext,bool>? IsComplete { get; set; }

Property Value

System.Func<IterativeContext,System.Boolean>

Remarks

Use this for domain-specific termination conditions. For example, checking whether a required workspace file exists, or whether a word count target has been reached.

IterativeLoopOptions.LoopName Property

Gets or sets a human-readable name for this loop run, used in diagnostics and progress events. Defaults to "iterative-loop".

public string LoopName { get; set; }

Property Value

System.String

IterativeLoopOptions.MaxIterations Property

Gets or sets the maximum number of iterations before the loop terminates. Defaults to 25. Set to a lower value for cost-sensitive workloads.

public int MaxIterations { get; set; }

Property Value

System.Int32

IterativeLoopOptions.MaxToolRoundsPerIteration Property

Gets or sets the maximum number of tool-calling rounds within a single iteration when ToolResultMode is MultiRound. Ignored for other modes. Defaults to 5.

public int MaxToolRoundsPerIteration { get; set; }

Property Value

System.Int32

Remarks

This is a safety valve to prevent unbounded within-iteration growth. After this many rounds of tool calls within one iteration, any remaining tool call requests are executed and stored in LastToolResults for the next iteration.

IterativeLoopOptions.MaxTotalToolCalls Property

Gets or sets the maximum cumulative tool call count across all iterations. When exceeded, the loop terminates with MaxToolCallsReached. Defaults to null (unlimited).

public System.Nullable<int> MaxTotalToolCalls { get; set; }

Property Value

System.Nullable<System.Int32>

Remarks

This is a safety guard against runaway tool-calling loops that stay under MaxIterations but make an excessive number of tool calls per iteration. Set this when token cost is proportional to tool call volume (e.g., web search or fetch tools).

IterativeLoopOptions.OnIterationEnd Property

Gets or sets an optional async callback invoked after each iteration completes. Receives the IterationRecord containing tool calls, tokens, and timing.

public System.Func<NexusLabs.Needlr.AgentFramework.Iterative.IterationRecord,System.Threading.Tasks.Task>? OnIterationEnd { get; set; }

Property Value

System.Func<IterationRecord,System.Threading.Tasks.Task>

Remarks

Fired after the IterationRecord is built and context is updated. Hook exceptions propagate to the caller.

IterativeLoopOptions.OnIterationStart Property

Gets or sets an optional async callback invoked at the start of each iteration, before the prompt factory runs. Receives the zero-based iteration number and the current IterativeContext.

public System.Func<int,NexusLabs.Needlr.AgentFramework.Iterative.IterativeContext,System.Threading.Tasks.Task>? OnIterationStart { get; set; }

Property Value

System.Func<System.Int32,IterativeContext,System.Threading.Tasks.Task>

Remarks

Use this for progress reporting (e.g., updating a SignalR client). Hook exceptions propagate directly to the caller — they are not caught by the loop's internal error handling.

IterativeLoopOptions.OnToolCall Property

Gets or sets an optional async callback invoked after each tool call completes. Receives the zero-based iteration number and the ToolCallResult.

public System.Func<int,NexusLabs.Needlr.AgentFramework.Iterative.ToolCallResult,System.Threading.Tasks.Task>? OnToolCall { get; set; }

Property Value

System.Func<System.Int32,ToolCallResult,System.Threading.Tasks.Task>

Remarks

Fired once per tool call, in execution order. Use for real-time progress updates such as streaming tool activity to a UI. Hook exceptions propagate to the caller.

IterativeLoopOptions.PromptFactory Property

Gets or sets the factory that builds the user message for each iteration. Called once at the start of every iteration with the current IterativeContext (which includes workspace state and last tool results).

public System.Func<NexusLabs.Needlr.AgentFramework.Iterative.IterativeContext,string> PromptFactory { get; set; }

Property Value

System.Func<IterativeContext,System.String>

Remarks

This is the core extensibility point. The prompt factory reads workspace files to understand current state, optionally includes data from LastToolResults, and returns a fresh user message.

The returned string becomes the sole user message — there is no conversation history. The workspace IS the memory.

IterativeLoopOptions.StallDetection Property

Gets or sets the stall detection configuration. When set, the loop compares consecutive iterations and terminates with StallDetected if ConsecutiveThreshold iterations in a row have total token counts within TolerancePercent of each other.

public NexusLabs.Needlr.AgentFramework.Iterative.StallDetectionOptions? StallDetection { get; set; }

Property Value

StallDetectionOptions

Remarks

Stall detection catches loops where the LLM repeats identical work every iteration because it has no cross-iteration memory. Without stall detection, these loops burn through MaxIterations or MaxTotalToolCalls with zero useful output.

When null (the default), no stall detection is performed — the loop relies on existing guards.

IterativeLoopOptions.ToolFilter Property

Gets or sets an optional filter that narrows the tool list on each iteration. Receives the zero-based iteration number, the current IterativeContext, and the full Tools list. Returns the subset of tools the model should see for that iteration.

public System.Func<int,NexusLabs.Needlr.AgentFramework.Iterative.IterativeContext,System.Collections.Generic.IReadOnlyList<Microsoft.Extensions.AI.AITool>,System.Collections.Generic.IReadOnlyList<Microsoft.Extensions.AI.AITool>>? ToolFilter { get; set; }

Property Value

System.Func<System.Int32,IterativeContext,System.Collections.Generic.IReadOnlyList<Microsoft.Extensions.AI.AITool>,System.Collections.Generic.IReadOnlyList<Microsoft.Extensions.AI.AITool>>

Example

ToolFilter = (iteration, ctx, allTools) =>
{
    var phase = ctx.Workspace.ReadFile("status.json");
    var allowed = phase.Contains("research")
        ? new[] { "search" }
        : new[] { "add_leg", "book_hotel", "validate_trip" };
    return allTools.Where(t => allowed.Contains(t.Name)).ToList();
};

Remarks

Use this for phase-gating — restricting which tools are available based on the current workspace state. For example, a trip planner might offer only search during the research phase and only add_leg/book_hotel during the build phase.

When null (the default), all Tools are available on every iteration.

IterativeLoopOptions.ToolResultMode Property

Gets or sets how tool call results are fed back to the model within a single iteration. Defaults to OneRoundTrip.

public NexusLabs.Needlr.AgentFramework.Iterative.ToolResultMode ToolResultMode { get; set; }

Property Value

ToolResultMode

See Also

IterativeLoopOptions.Tools Property

Gets or sets the tools available to the model. The loop matches tool call requests from the model against this list by function name.

public System.Collections.Generic.IReadOnlyList<Microsoft.Extensions.AI.AITool> Tools { get; set; }

Property Value

System.Collections.Generic.IReadOnlyList<Microsoft.Extensions.AI.AITool>

Remarks

Tools are typically created via Microsoft.Extensions.AI.AIFunctionFactory or obtained from the agent framework's function discovery. The same tool instances are reused across all iterations.