.NET API
FakeModelProvider
Tracon.Testing.dllConfigurable, non-networked fake model provider.
public sealed class FakeModelProvider : ITenantCredentialModelProvider, IModelProvider, IDisposableInheritance
Section titled “Inheritance”Implements
Section titled “Implements”ITenantCredentialModelProvider, IModelProvider, IDisposable
Inherited Members
Section titled “Inherited Members”object.GetType(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Examples
Section titled “Examples”var provider = new FakeModelProvider().CallsTool("get_order_status", new { orderId = "ORD-7" }).EchoesUserMessage();Remarks
Section titled “Remarks”Five fluent methods (FakeModelProvider.RespondsWith, FakeModelProvider.EchoesUserMessage, FakeModelProvider.CallsTool, FakeModelProvider.ForModel, FakeModelProvider.WithModel) cover all the fake provider behavior that is today duplicated across five separate files in Tracon’s test suites.
Each model has its own ordered response queue (selected with FakeModelProvider.ForModel; the default queue is used when none is selected). A call pops the next step in the queue; once the queue is drained, every subsequent call returns either the echo set with FakeModelProvider.EchoesUserMessage or a fixed response. This behavior is lasting for the provider’s lifetime — it does not infer “which tool was already called” by scanning message history.
Constructors
Section titled “Constructors”FakeModelProvider(string)
Section titled “ FakeModelProvider(string)”Creates a new fake provider.
public FakeModelProvider(string name = "fake")Parameters
Section titled “Parameters”name string
Provider name.
Properties
Section titled “Properties”Models
Section titled “ Models”The models this provider offers.
public IReadOnlyList<ModelDescriptor> Models { get; }Property Value
Section titled “Property Value”IReadOnlyList<ModelDescriptor>
Remarks
Section titled “Remarks”Metadata, not an allow list — see the remarks on IModelProvider. This is read on the compile path and may be read concurrently; return a stable, immutable collection rather than one that is mutated after construction.
The provider name. ModelBinding.Provider matches this value. Comparison is case-insensitive.
public string Name { get; }Property Value
Section titled “Property Value”Requests
Section titled “ Requests”Requests that reached this provider; the newest is last.
public IReadOnlyList<FakeModelRequest> Requests { get; }Property Value
Section titled “Property Value”IReadOnlyList<FakeModelRequest>
Methods
Section titled “Methods”CallsTool(string, object?)
Section titled “ CallsTool(string, object?)”Enqueues a tool call.
public FakeModelProvider CallsTool(string toolName, object? arguments = null)Parameters
Section titled “Parameters”toolName string
Name of the tool to call.
arguments object?
Call arguments. If not an IDictionary, its properties are read through reflection (works for anonymous types).
Returns
Section titled “Returns”The chain, for continued configuration.
CallsTools(params (string ToolName, object? Arguments)[])
Section titled “ CallsTools(params (string ToolName, object? Arguments)[])”Enqueues a SINGLE turn that calls every given tool at once.
public FakeModelProvider CallsTools(params (string ToolName, object? Arguments)[] calls)Parameters
Section titled “Parameters”calls (string ToolName, object? Arguments)[]
The tool name and arguments for each call.
Returns
Section titled “Returns”The chain, for continued configuration.
Remarks
Section titled “Remarks”FakeModelProvider.CallsTool enqueues one call per turn and can never
produce more than one FunctionCallContent in the same response;
exercising a REAL concurrent tool-call loop
(FunctionInvokingChatClient.AllowConcurrentInvocation) needs
several independent calls to arrive together, in one turn.
CreateChatClient(ModelBinding)
Section titled “ CreateChatClient(ModelBinding)”Produces a raw chat client for the given binding.
public IChatClient CreateChatClient(ModelBinding binding)Parameters
Section titled “Parameters”binding ModelBinding
The model binding.
Returns
Section titled “Returns”IChatClient
The provider-specific client. Decorators specific to the provider (example: Anthropic’s settings decorator) may be added here.
Remarks
Section titled “Remarks”Do not build the common pipeline here.
UseFunctionInvocation, UseOpenTelemetry, the content
guard, the circuit breaker, the per-provider concurrency limiter,
attachment resolution, response caching, the fallback chain and
content-filter detection are all added by
ModelProviderRegistry.CreateChatClient. Before that shared pipeline, every
provider package built the tool-call loop inside itself; the result
was that no ring the registry wraps around could see the loop’s turns
— a tool result entered the model uninspected.
If the loop is also built here, two nested FunctionInvokingChatClient
instances form: the inner one resolves tools, the outer one never sees
any call. This is not merely cosmetic. The registry
places the content guard directly above the client this method returns,
so that every turn of the tool-call loop is inspected. With an inner
loop, the turn that carries a tool result back into the model runs
beneath the guard: measured on a single-tool run, the guard
sees the tool result only on its way out
(ContentGuardDirection.Output) and never on its way in
(ContentGuardDirection.Input) — which is the exact path prompt
injection takes. The reply text and the tool-call count are unchanged,
so nothing else reveals the mistake.
Lifetime of the returned client. Tracon does
not dispose it. The client is built once per compiled
agent and stored in it; the compiled agent
(Microsoft.Agents.AI.ChatClientAgent) implements neither
IDisposable nor IAsyncDisposable, and
evicting an agent from the compile cache drops the reference without
disposing. An implementation therefore owns the lifetime of whatever it
returns, and what it returns must tolerate never being disposed. This
is why the shipped providers return clients backed by a long-lived,
shared SDK client rather than a per-call one: returning a client that
holds a resource needing release would leak it.
CreateChatClient(ModelBinding, ModelProviderCredential)
Section titled “ CreateChatClient(ModelBinding, ModelProviderCredential)”Produces a raw chat client using a resolved tenant credential.
public IChatClient CreateChatClient(ModelBinding binding, ModelProviderCredential credential)Parameters
Section titled “Parameters”binding ModelBinding
The model binding.
credential ModelProviderCredential
The non-null tenant credential.
Returns
Section titled “Returns”IChatClient
The provider-specific raw chat client.
Remarks
Section titled “Remarks”The implementation uses ModelProviderCredential.ApiKey and, when present, ModelProviderCredential.Endpoint. The API key must never fall back to the setup-time key. An endpoint may fall back to a setup-time endpoint when only the key is overridden.
Dispose()
Section titled “ Dispose()”public void Dispose()EchoesLastToolResult(string, int?, int?)
Section titled “ EchoesLastToolResult(string, int?, int?)”Starts echoing the LAST tool result in history once the queue is drained.
public FakeModelProvider EchoesLastToolResult(string prefix = "", int? inputTokens = null, int? outputTokens = null)Parameters
Section titled “Parameters”prefix string
Text prepended to the result.
inputTokens int?
If given, the input token count reported with every response.
outputTokens int?
If given, the output token count reported with every response.
Returns
Section titled “Returns”The chain, for continued configuration.
Remarks
Section titled “Remarks”For the last step of a tool chain (set up with FakeModelProvider.CallsTool), where the final response must genuinely depend on the result the tool returned — e.g. a hand-off result to a sub-agent.
EchoesUserMessage()
Section titled “ EchoesUserMessage()”Starts echoing the last incoming user message once the queue is drained.
public FakeModelProvider EchoesUserMessage()Returns
Section titled “Returns”The chain, for continued configuration.
ForModel(string, Action<FakeModelProvider>)
Section titled “ ForModel(string, Action<FakeModelProvider>)”Defines a separate response queue for a specific model name. Calls to
FakeModelProvider.RespondsWith/FakeModelProvider.EchoesUserMessage/FakeModelProvider.CallsTool
inside the configure body affect only that model’s queue.
public FakeModelProvider ForModel(string modelId, Action<FakeModelProvider> configure)Parameters
Section titled “Parameters”modelId string
Model name.
configure Action<FakeModelProvider>
Configuration that fills this model’s queue.
Returns
Section titled “Returns”The chain, for continued configuration.
RespondsWith(params string[])
Section titled “ RespondsWith(params string[])”Enqueues responses to return in order. Once the queue is drained, a fixed response is returned unless FakeModelProvider.EchoesUserMessage was set.
public FakeModelProvider RespondsWith(params string[] responses)Parameters
Section titled “Parameters”responses string[]
Texts to return in order.
Returns
Section titled “Returns”The chain, for continued configuration.
RespondsWith(string, int, int)
Section titled “ RespondsWith(string, int, int)”Enqueues a single text response that reports a specific token usage.
public FakeModelProvider RespondsWith(string response, int inputTokens, int outputTokens)Parameters
Section titled “Parameters”response string
Text to return.
inputTokens int
Input token count to report.
outputTokens int
Output token count to report.
Returns
Section titled “Returns”The chain, for continued configuration.
Remarks
Section titled “Remarks”For scenarios that test cost/usage metrics end to end.
WithModel(ModelDescriptor)
Section titled “ WithModel(ModelDescriptor)”Registers a model on this provider.
public FakeModelProvider WithModel(ModelDescriptor descriptor)Parameters
Section titled “Parameters”descriptor ModelDescriptor
Model descriptor.
Returns
Section titled “Returns”The chain, for continued configuration.