ToolInvocationRunner
NexusLabs.Needlr.AgentFramework.Testing¶
ToolInvocationRunner Class¶
Test harness for invoking [AgentFunction]-decorated tool methods through their
source-generated Microsoft.Extensions.AI.AIFunction wrapper, with the same plumbing
Microsoft.Extensions.AI.FunctionInvokingChatClient uses in production.
Inheritance System.Object 🡒 ToolInvocationRunner
Example¶
Minimal test, full bring-your-own service provider:
var sp = new ServiceCollection()
.AddAgentFrameworkAccessors()
.AddSingleton<GrepTool>()
.BuildServiceProvider();
var runner = new ToolInvocationRunner(sp)
.WithWorkspace(ws => ws.TryWriteFile("a.txt", "hi"));
var result = await runner.InvokeAsync<GrepTool>("grep_files", a =>
{
a["pattern"] = "hi";
a["path"] = "/";
});
result.AssertSuccess();
result.AssertResultContains("a.txt");
Remarks¶
The runner exists to remove the boilerplate consumers face when testing tools they wrote for Needlr's Agent Framework integration: build a service provider, register the tool, look up the source-generated NexusLabs.Needlr.AgentFramework.IAIFunctionProvider, find the right Microsoft.Extensions.AI.AIFunction by name, build Microsoft.Extensions.AI.AIFunctionArguments, establish an ambient NexusLabs.Needlr.AgentFramework.Context.IAgentExecutionContext so the tool can read NexusLabs.Needlr.AgentFramework.Context.IAgentExecutionContextAccessor.Current, and invoke.
By default the runner resolves only via the source-generated NexusLabs.Needlr.AgentFramework.IAIFunctionProvider
registered by the [ModuleInitializer] the Needlr Agent Framework generator emits in
the consuming assembly. This is the same path production agents take. To exercise the
reflection-based Microsoft.Extensions.AI.AIFunctionFactory path instead, call
GetFunctionAllowingReflection<TTool>(string) explicitly — that method carries
System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute annotations because it is incompatible with
NativeAOT.
Instances are immutable: each With* method returns a new runner with the configuration
applied. Each InvokeAsync<TTool>(string, Action<AIFunctionArguments>, CancellationToken)
call creates a fresh Microsoft.Extensions.DependencyInjection.IServiceScope (when an Microsoft.Extensions.DependencyInjection.IServiceScopeFactory
is available) so tools with scoped dependencies behave correctly across invocations.
Constructors¶
ToolInvocationRunner(IServiceProvider) Constructor¶
Creates a runner over an already-built System.IServiceProvider. Use this when the test fixture builds its own DI container (typical for Syringe-based test fixtures, or for tests that need to share a provider across multiple invocations).
Parameters¶
serviceProvider System.IServiceProvider
Provider that has the tool type and its dependencies registered. The Needlr accessors
(NexusLabs.Needlr.AgentFramework.Context.IAgentExecutionContextAccessor, NexusLabs.Needlr.AgentFramework.Diagnostics.IAgentDiagnosticsAccessor)
must also be registered — call
NexusLabs.Needlr.AgentFramework.AgentFrameworkAccessorServiceCollectionExtensions.AddAgentFrameworkAccessors(Microsoft.Extensions.DependencyInjection.IServiceCollection)
or use the broader UsingAgentFramework() Syringe extension.
Properties¶
ToolInvocationRunner.IsGeneratedProviderAvailable Property¶
Whether a source-generated NexusLabs.Needlr.AgentFramework.IAIFunctionProvider is currently registered with NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap. Useful as a precondition assertion in tests that require the source generator to have run for at least one assembly in the process.
Property Value¶
Remarks¶
This property tells you only whether \<em>any\</em> generated provider exists globally; it does not tell you whether \<em>your specific tool\</em> is resolvable. Call GetFunction<TTool>(string) if you need to verify a specific function — the error message it throws when a type or method is missing is the canonical signal.
Methods¶
ToolInvocationRunner.AssertGeneratedProviderAvailable() Method¶
Throws when no source-generated NexusLabs.Needlr.AgentFramework.IAIFunctionProvider is registered. Use this as a fail-fast guard in tests that depend on the generator output for at least one assembly in the process.
Exceptions¶
System.InvalidOperationException
Thrown when no generated provider is available, with guidance on how to enable it.
Remarks¶
In practice this rarely throws because the Needlr Agent Framework assembly itself emits a
(possibly empty) provider via [ModuleInitializer], which registers as soon as
NexusLabs.Needlr.AgentFramework.dll loads. The check is still useful as a sanity
guard in environments where modules may not have initialized yet (e.g. some custom
AssemblyLoadContext scenarios).
ToolInvocationRunner.Create(Action<IServiceCollection>) Method¶
Creates a runner over a fresh service provider with only the Needlr accessors registered. Use this overload when registering multiple tools or when you want full control over the service collection setup.
public static NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationRunner Create(System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection>? configureServices=null);
Parameters¶
configureServices System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection>
Hook to register the tools and any dependencies they need.
Returns¶
ToolInvocationRunner.CreateFor<TTool>(Action<IServiceCollection>) Method¶
Creates a runner over a fresh service provider with the Needlr accessors and TTool registered as a singleton. Additional services can be added via the optional configureServices callback.
public static NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationRunner CreateFor<TTool>(System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection>? configureServices=null)
where TTool : class;
Type parameters¶
TTool
The tool class to register.
Parameters¶
configureServices System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection>
Optional hook to register additional dependencies (e.g. IHttpClientFactory, options,
fakes for scoped services).
Returns¶
ToolInvocationRunner.GetFunction(Type, string) Method¶
Resolves an Microsoft.Extensions.AI.AIFunction for toolType by method name via the source-generated NexusLabs.Needlr.AgentFramework.IAIFunctionProvider.
Parameters¶
toolType System.Type
methodName System.String
Returns¶
Microsoft.Extensions.AI.AIFunction
ToolInvocationRunner.GetFunction<TTool>(string) Method¶
Resolves an Microsoft.Extensions.AI.AIFunction for TTool by method name via the source-generated NexusLabs.Needlr.AgentFramework.IAIFunctionProvider.
public Microsoft.Extensions.AI.AIFunction GetFunction<TTool>(string methodName)
where TTool : class;
Type parameters¶
TTool
The tool class declaring the [AgentFunction] method.
Parameters¶
methodName System.String
The function name as exposed to the LLM (defaults to the C# method name when the source generator emits the wrapper).
Returns¶
Microsoft.Extensions.AI.AIFunction
Exceptions¶
System.InvalidOperationException
Thrown when no generated provider is registered, or when no function with that name is
found for the type.
ToolInvocationRunner.GetFunctionAllowingReflection(Type, string) Method¶
Resolves an Microsoft.Extensions.AI.AIFunction for toolType by method name, preferring the source-generated provider but falling back to reflection-based discovery via Microsoft.Extensions.AI.AIFunctionFactory.Create(System.Reflection.MethodInfo,System.Object,Microsoft.Extensions.AI.AIFunctionFactoryOptions) when the generator output is not available.
public Microsoft.Extensions.AI.AIFunction GetFunctionAllowingReflection(System.Type toolType, string methodName);
Parameters¶
toolType System.Type
methodName System.String
Returns¶
Microsoft.Extensions.AI.AIFunction
ToolInvocationRunner.GetFunctionAllowingReflection<TTool>(string) Method¶
Resolves an Microsoft.Extensions.AI.AIFunction for TTool by method name, preferring the source-generated provider but falling back to reflection-based discovery via Microsoft.Extensions.AI.AIFunctionFactory.Create(System.Reflection.MethodInfo,System.Object,Microsoft.Extensions.AI.AIFunctionFactoryOptions) when the generator output is not available.
public Microsoft.Extensions.AI.AIFunction GetFunctionAllowingReflection<TTool>(string methodName)
where TTool : class;
Type parameters¶
TTool
Parameters¶
methodName System.String
Returns¶
Microsoft.Extensions.AI.AIFunction
Remarks¶
This method is incompatible with NativeAOT because the reflection branch dynamically generates marshalling code. Tests targeting AOT must use GetFunction<TTool>(string) instead.
ToolInvocationRunner.InvokeAsync(Type, string, Action<AIFunctionArguments>, CancellationToken) Method¶
Resolves and invokes a function for toolType. Identical to the generic overload but accepts the type as a parameter for callers who only have a runtime System.Type.
public System.Threading.Tasks.Task<NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationResult> InvokeAsync(System.Type toolType, string methodName, System.Action<Microsoft.Extensions.AI.AIFunctionArguments>? configureArgs=null, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));
Parameters¶
toolType System.Type
methodName System.String
configureArgs System.Action<Microsoft.Extensions.AI.AIFunctionArguments>
cancellationToken System.Threading.CancellationToken
Returns¶
System.Threading.Tasks.Task<ToolInvocationResult>
ToolInvocationRunner.InvokeAsync<TTool>(string, Action<AIFunctionArguments>, CancellationToken) Method¶
Resolves the source-generated Microsoft.Extensions.AI.AIFunction for the given tool method, builds an Microsoft.Extensions.AI.AIFunctionArguments via configureArgs, establishes the configured execution context, and invokes the function. Captures any thrown exception into the returned ToolInvocationResult rather than propagating.
public System.Threading.Tasks.Task<NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationResult> InvokeAsync<TTool>(string methodName, System.Action<Microsoft.Extensions.AI.AIFunctionArguments>? configureArgs=null, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken))
where TTool : class;
Type parameters¶
TTool
The tool class declaring the [AgentFunction] method.
Parameters¶
methodName System.String
The function name as exposed to the LLM.
configureArgs System.Action<Microsoft.Extensions.AI.AIFunctionArguments>
Optional hook to populate Microsoft.Extensions.AI.AIFunctionArguments. Pass null to invoke with no arguments.
cancellationToken System.Threading.CancellationToken
Cancellation token forwarded to Microsoft.Extensions.AI.AIFunction.InvokeAsync(Microsoft.Extensions.AI.AIFunctionArguments,System.Threading.CancellationToken). Tools that accept a System.Threading.CancellationToken parameter receive this token via the source-generated wrapper.
Returns¶
System.Threading.Tasks.Task<ToolInvocationResult>
ToolInvocationRunner.InvokeAsync<TTool>(string, IReadOnlyDictionary<string,object>, CancellationToken) Method¶
Same as InvokeAsync<TTool>(string, Action<AIFunctionArguments>, CancellationToken) but accepts a pre-built dictionary of arguments. Convenient when the test already has a System.Collections.Generic.IReadOnlyDictionary<> of args ready (e.g. captured from a prior run or shared across multiple invocations).
public System.Threading.Tasks.Task<NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationResult> InvokeAsync<TTool>(string methodName, System.Collections.Generic.IReadOnlyDictionary<string,object?> arguments, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken))
where TTool : class;
Type parameters¶
TTool
Parameters¶
methodName System.String
arguments System.Collections.Generic.IReadOnlyDictionary<System.String,System.Object>
cancellationToken System.Threading.CancellationToken
Returns¶
System.Threading.Tasks.Task<ToolInvocationResult>
ToolInvocationRunner.LimitToTools(Type[]) Method¶
Returns a new runner that scopes
NexusLabs.Needlr.AgentFramework.AgentFrameworkGeneratedBootstrap to expose only
toolTypes during function resolution. Useful in consumer test
projects that contain many [AgentFunction] types and want to assert behavior
against a specific subset without disturbing other tests.
public NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationRunner LimitToTools(params System.Type[] toolTypes);
Parameters¶
toolTypes System.Type[]
The set of tool types visible to the source-generated provider during this runner's invocations. Must contain at least one type.
Returns¶
Exceptions¶
System.ArgumentException
Thrown when toolTypes is empty.
ToolInvocationRunner.WithExecutionContext(Action<AgentExecutionContextBuilder>) Method¶
Returns a new runner that establishes an NexusLabs.Needlr.AgentFramework.Context.IAgentExecutionContext built by
configure for the duration of each InvokeAsync call.
public NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationRunner WithExecutionContext(System.Action<NexusLabs.Needlr.AgentFramework.Testing.AgentExecutionContextBuilder> configure);
Parameters¶
configure System.Action<AgentExecutionContextBuilder>
Returns¶
Remarks¶
Successive calls replace the previously-configured context (immutable copy semantics).
ToolInvocationRunner.WithWorkspace(IWorkspace) Method¶
Convenience: returns a new runner that attaches the supplied workspace to the execution context.
public NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationRunner WithWorkspace(NexusLabs.Needlr.AgentFramework.Workspace.IWorkspace workspace);
Parameters¶
workspace NexusLabs.Needlr.AgentFramework.Workspace.IWorkspace
Returns¶
ToolInvocationRunner.WithWorkspace(Action<IWorkspace>) Method¶
Convenience: returns a new runner that creates a fresh NexusLabs.Needlr.AgentFramework.Workspace.InMemoryWorkspace, runs seed against it, and attaches it to the execution context.
public NexusLabs.Needlr.AgentFramework.Testing.ToolInvocationRunner WithWorkspace(System.Action<NexusLabs.Needlr.AgentFramework.Workspace.IWorkspace> seed);
Parameters¶
seed System.Action<NexusLabs.Needlr.AgentFramework.Workspace.IWorkspace>