Skip to content
Tracon

ModelProviderSettingsContract

Namespace Tracon.Testing.Contracts.Providers · Assembly Tracon.Testing.Contracts.Xunit.dll

Behavior tests for how a provider handles ModelBinding.ProviderSettings.

public abstract class ModelProviderSettingsContract : ModelProviderContract, IAsyncLifetime, IAsyncDisposable
Inheritance
object ← ModelProviderContract ← ModelProviderSettingsContract
Implements
IAsyncLifetime, IAsyncDisposable
Inherited Members
ModelProviderContract.CreateProviderAsync(), ModelProviderContract.InitializeAsync(), ModelProviderContract.DisposeAsync(), ModelProviderContract.OnDisposeAsync(), ModelProviderContract.Binding(string?, string?), ModelProviderContract.Provider_name_is_not_empty(), ModelProviderContract.Catalog_model_names_are_unique_and_not_empty(), ModelProviderContract.Create_chat_client_returns_a_client_for_the_setup_time_credential(), ModelProviderContract.Create_chat_client_returns_a_raw_client_that_builds_no_tool_call_loop(), ModelProviderContract.Create_chat_client_accepts_a_binding_whose_provider_name_differs_in_case(), ModelProviderContract.A_model_the_catalog_does_not_list_is_not_rejected(), ModelProviderContract.Concurrent_create_chat_client_calls_all_return_a_client(), ModelProviderContract.A_client_that_is_never_disposed_does_not_stop_the_provider(), ModelProviderContract.Provider, ModelProviderContract.UnknownModelName, object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()

Every provider owes the one rule Tracon fixes for all of them: an unknown key is not ignored silently. This holds for a provider that offers no tunable of its own as well — it rejects every key. A setting that quietly does nothing makes the host author miss the behavior they configured without ever seeing why, which is the same reasoning that makes an unparsable ModelBinding.ReasoningEffort stop compilation instead of falling back to a default.

Derive this class for every provider. A provider that supports at least one key overrides ModelProviderSettingsContract.SupportedSetting with it; a provider that supports none leaves it null, and the scenarios that need a supported key are reported as skipped.

ModelProviderSettings.Validate in Tracon.Abstractions implements this rule, including the prefix convention (anthropic.*, google.*) that keeps one provider from silently accepting another’s keys. Pass an empty list of supported keys when the provider supports none. Use it rather than a hand-rolled check: the model provider registry passes its exception through with the offending key named, but masks the message of any other exception a provider throws while it builds a client. These scenarios call the provider directly, below the registry, so they cannot see that difference.

protected ModelProviderSettingsContract()

A setting key this provider supports, with a value it must reject, or null when no supported key has a closed set of values.

protected virtual KeyValuePair<string, JsonElement>? InvalidSettingValue { get; }

KeyValuePair<string, JsonElement>?

A known key with a wrong value is the same mistake as an unknown key — a typo the host author cannot see — so it fails the same way, naming the key and never repeating the value. Choose a value of the right JSON type that is outside the accepted set (for example an effort level the provider does not define), so the case proves value validation and not only type checking; a text value also proves the message does not echo it. The default is null, which reports the scenario as skipped.

A provider setting key this provider supports, with a value it accepts, or null when the provider supports no key.

protected virtual KeyValuePair<string, JsonElement>? SupportedSetting { get; }

KeyValuePair<string, JsonElement>?

When given, it proves the rejection below is about the key being unknown, not about settings being rejected wholesale. The default is null.

A setting key this provider does not support. Defaults to a key under this provider’s own prefix that no provider could plausibly define.

protected virtual string UnsupportedSettingKey { get; }

string

A_supported_provider_setting_is_accepted()

Section titled “ A_supported_provider_setting_is_accepted()”
[Fact]
public void A_supported_provider_setting_is_accepted()

A_supported_setting_is_matched_regardless_of_key_casing()

Section titled “ A_supported_setting_is_matched_regardless_of_key_casing()”
[Fact]
public void A_supported_setting_is_matched_regardless_of_key_casing()

Keys arrive with the default (ordinal) comparer when the binding is read back from a jsonb column, so a provider must not rely on the dictionary’s own comparer to match its keys case-insensitively.

A_supported_setting_with_an_invalid_value_is_rejected()

Section titled “ A_supported_setting_with_an_invalid_value_is_rejected()”
[Fact]
public void A_supported_setting_with_an_invalid_value_is_rejected()

An_unsupported_provider_setting_is_rejected()

Section titled “ An_unsupported_provider_setting_is_rejected()”
[Fact]
public void An_unsupported_provider_setting_is_rejected()