IterativeLoopOptions
NexusLabs.Needlr.AgentFramework¶
NexusLabs.Needlr.AgentFramework.Iterative¶
IterativeLoopOptions Class¶
Configuration for a single run of an IIterativeAgentLoop.
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.TryReadFile("article.md").Value.Content
: "(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.
Property Value¶
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.
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.CheckCompletionAfterToolCalls Property¶
Gets or sets when to check IsComplete relative to tool call execution within an iteration. Defaults to None (check only between iterations).
public NexusLabs.Needlr.AgentFramework.Iterative.ToolCompletionCheckMode CheckCompletionAfterToolCalls { get; set; }
Property Value¶
Remarks¶
When set to AfterToolRounds, the loop checks IsComplete after each round's batch of tool calls completes. When set to AfterEachToolCall, the check runs after each individual tool call, and remaining tool calls in the batch are skipped if the predicate returns true.
Both modes terminate the loop with CompletedEarlyAfterToolCall when the check fires, allowing callers to distinguish early completion from the standard between-iteration completion (Completed).
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¶
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.
Property Value¶
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".
Property Value¶
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.
Property Value¶
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.
Property Value¶
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).
Property Value¶
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¶
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.TryReadFile("status.json").Value.Content;
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.
Property Value¶
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.
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.