Add a model provider
This guide explains the external-provider registration flow for developers. Provider modules are dynamic, compiled C# assemblies loaded at runtime, ensuring enterprise stability and strict response contracts.
When to add a provider module
Add a model provider only when Buffaly must select and call an entirely different LLM reasoning backend. Provider modules are not a generic tool mechanism.
| If you need to... | Do this instead |
|---|---|
| Add a repeatable text/checklist workflow | Create a Prompt skill |
| Execute a native typed operation | Create a ProtoScript action |
| Connect deterministic SDK/protocol logic | Build a C# DLL tool |
| Model a configured system with identity/lifecycle | Build a Service |
| Add a new LLM backend to the runtime | Build a Model Provider Module |
Provider module anatomy & Contracts
The architecture keeps provider implementations outside the core Buffaly.Development tree. They must implement shared Buffaly.Agent.Web.Common contracts.
IProviderCatalogSource
Builds the catalog row. Returns the provider token, available models, default model, reasoning options, and enabled state to the UI and validation engine.
IProviderNativeCompletionExecutor
The execution surface. Handles the actual API calls to the LLM backend and maps the response into Buffaly's canonical format.
IProviderAuthHandler (Optional)
Implemented by OAuth-capable providers. Owns provider-specific login protocol details (like OIDC discovery and PKCE exchange), handing tokens back to the host for storage.
ProviderModules.json & Layout
Modules are loaded from the runtime install root, specifically <InstallRootPath>\lib\provider-modules\. You must copy your compiled provider assembly and its dependencies into a dedicated subfolder here.
Critical Deployment Note:
The Web App needs provider catalog-source assemblies. Worker processes need provider completion-executor assemblies. Keep the web and worker provider-modules folders perfectly aligned.
Example ProviderModules.json
{
"Modules": [
{
"Enabled": true,
"Provider": "anthropic",
"DisplayName": "Anthropic",
"AssemblyPath": "Buffaly.Provider.Anthropic/Buffaly.Provider.Anthropic.dll",
"CatalogSourceTypeName": "Buffaly.Provider.Anthropic.AnthropicProviderCatalogSource",
"CompletionExecutorTypeName": "Buffaly.Provider.Anthropic.AnthropicCompletionExecutor",
"LoadOrder": 500
}
]
}Credentials & Feature Rows
Provider credentials and model settings live in the target instance's SQL sessions database (dbo.Features table), not in source code or the manifest.
Example structure of the Anthropic Feature row:
FeatureName = Anthropic Feature
Settings = {
"ApiKey": "...",
"ModelNames": ["claude-sonnet-4-5"],
"SupportedReasoningLevels": ["low", "medium", "high"],
"DefaultReasoningLevel": "medium",
"BaseUrl": "https://api.anthropic.com",
"Version": "2023-06-01",
"MaxTokens": 4096
}The Response Contract
External APIs return data in a hundred different ways. The Provider Executor must unwrap the external API response and return it as Buffaly canonical items JSON in ProviderNativeCompletionResult.Raw.
{
"items": [
{
"type": "message",
"role": "assistant",
"phase": "final_answer",
"content": [
{
"type": "output_text",
"text": "provider smoke ok"
}
],
"metadata": {
"format": "text",
"result_type": "display message"
}
}
]
}If a 3rd-party model returns a canonical payload as text inside a fenced JSON code block, the executor must unwrap it before returning it to Buffaly.
Registration Workflow
- Build the provider project implementing the Common contracts.
- Copy the module folder into
<InstallRootPath>\lib\provider-modules. - Add/update the manifest entry in
ProviderModules.json. - Inject the provider feature row (credentials) into the target instance SQL sessions database.
- Recycle only the target instance app pool (e.g.,
appcmd.exe recycle apppool /apppool.name:stagingPool). Do not kill shared dotnet processes. - Verify the catalog endpoints and run a smoke test.
API Verification
Use JsonWs endpoints directly for API-only verification. Post to /api/buffaly.agent.host/provider-catalog-service/get-provider-catalog to ensure your provider appears.
-
Providertoken matches. -
IsEnabled=true(from the manifest). -
IsConfigured=true(verifying your SQL Feature Row credentials successfully bound).