< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Workflows.Middleware.ToolResultFunctionMiddleware
Assembly: NexusLabs.Needlr.AgentFramework.Workflows
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Workflows/Middleware/ToolResultFunctionMiddleware.cs
Line coverage
65%
Covered lines: 17
Uncovered lines: 9
Coverable lines: 26
Total lines: 71
Line coverage: 65.3%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Configure(...)100%1165.38%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Workflows/Middleware/ToolResultFunctionMiddleware.cs

#LineLine coverage
 1using Microsoft.Agents.AI;
 2using Microsoft.Extensions.AI;
 3
 4using NexusLabs.Needlr.AgentFramework;
 5using NexusLabs.Needlr.AgentFramework.Tools;
 6
 7namespace NexusLabs.Needlr.AgentFramework.Workflows.Middleware;
 8
 9/// <summary>
 10/// MAF function-invocation middleware that intercepts <c>[AgentFunction]</c> return values and
 11/// exceptions, ensuring the LLM always receives a structured JSON response instead of a raw
 12/// stack trace.
 13/// </summary>
 14/// <remarks>
 15/// <para>
 16/// When an <c>[AgentFunction]</c> method returns a <see cref="IToolResult"/>:
 17/// <list type="bullet">
 18/// <item>
 19/// <description>Success — the LLM receives the <see cref="IToolResult.BoxedValue"/> directly.</description>
 20/// </item>
 21/// <item>
 22/// <description>
 23/// Failure — the LLM receives <c>{ "error": { … } }</c> (the <see cref="IToolResult.BoxedError"/>
 24/// wrapped), and the original <see cref="Exception"/> is preserved on <see cref="IToolResult.Exception"/>
 25/// for diagnostics.
 26/// </description>
 27/// </item>
 28/// </list>
 29/// </para>
 30/// <para>
 31/// When an <c>[AgentFunction]</c> throws an <em>unhandled</em> exception, the middleware catches it,
 32/// wraps it in an <see cref="ToolResult.UnhandledFailure"/> result, and returns a safe generic error
 33/// message to the LLM. <see cref="IToolResult.IsTransient"/> is <see langword="null"/> in this case.
 34/// </para>
 35/// <para>
 36/// Non-<see cref="IToolResult"/> return values pass through unchanged.
 37/// </para>
 38/// </remarks>
 39public sealed class ToolResultFunctionMiddleware : IAIAgentBuilderPlugin
 40{
 41    /// <inheritdoc />
 42    public void Configure(AIAgentBuilderPluginOptions options)
 43    {
 744        ArgumentNullException.ThrowIfNull(options);
 45
 646        FunctionInvocationDelegatingAgentBuilderExtensions.Use(
 647            options.AgentBuilder,
 648            async (agent, context, next, cancellationToken) =>
 649            {
 650                object? raw;
 651
 652                try
 653                {
 054                    raw = await next(context, cancellationToken).ConfigureAwait(false);
 055                }
 656                catch (Exception ex)
 657                {
 058                    raw = ToolResult.UnhandledFailure(ex);
 059                }
 660
 061                if (raw is IToolResult result)
 662                {
 063                    return result.IsSuccess
 064                        ? result.BoxedValue
 065                        : new { error = result.BoxedError };
 666                }
 667
 068                return raw;
 669            });
 670    }
 71}