| name | create-provider |
| description | Generates the provider pattern (interface plus fake/real implementations) for plugin data sources. Use when the user says "create a provider for [data source]", "add provider interface for [feature]", or "implement provider pattern for [plugin]". |
Create Provider Pattern
Generates provider interface and implementations under src/Plugins/Providers/. Follow project conventions from existing providers (e.g. IWeatherDataProvider, IOutlookEmailProvider).
Discovery (Ask First)
Before generating files, gather:
-
Provider name and purpose
- e.g. "Weather", "OutlookEmail", "Stock"
- Use PascalCase; interface will be
I[Name]DataProvider or I[Name]Provider if the domain is not "data" (e.g. IOutlookEmailProvider).
-
Methods needed
- Signature, parameters, return type (prefer
Task<T> / Task<T?> for async).
- For DTOs, define them in the same file as the interface (see
OutlookEmail in IOutlookEmailProvider.cs).
-
Which artifacts
- Interface only
- Interface + Fake
- Interface + Real
- Interface + Fake + Real (Both)
Only generate the Interface and the implementations the user requested.
File Layout and Namespaces
| Artifact | Path | Namespace |
|---|
| Interface | src/Plugins/Providers/Interfaces/I[Feature]DataProvider.cs | AnimalAI.Plugins.Providers |
| Fake | src/Plugins/Providers/Fake/Fake[Feature]DataProvider.cs | AnimalAI.Plugins.Providers |
| Real | src/Plugins/Providers/Real/Real[Feature]DataProvider.cs | AnimalAI.Plugins.Providers |
Use I[Feature]DataProvider / Fake[Feature]DataProvider / Real[Feature]DataProvider unless the domain uses another suffix (e.g. IOutlookEmailProvider → FakeOutlookEmailProvider, RealOutlookEmailProvider).
Interface Template
- Namespace:
AnimalAI.Plugins.Providers
- XML summary on interface and on each method (params, returns)
- Methods async:
Task<T> or Task<T?>
- Shared DTOs (e.g.
OutlookEmail) in the same file as the interface when they are specific to that provider
namespace AnimalAI.Plugins.Providers;
public interface I[Feature]DataProvider
{
Task<ReturnType> MethodNameAsync(...);
}
Fake Provider Template
- Implement the interface
- No external calls; use in-memory data (e.g.
Dictionary, static fields, or hard-coded lists)
- Return immediately with
Task.FromResult(...) for sync data, or Task.FromResult for trivial async
- For “keyed” lookups (e.g. coordinates), round or normalize keys to match a small set of known mock keys; provide a sensible default for unknown keys
namespace AnimalAI.Plugins.Providers;
public class Fake[Feature]DataProvider : I[Feature]DataProvider
{
public Task<ReturnType> MethodNameAsync(...)
{
return Task.FromResult(...);
}
}
Real Provider Template
- Implement the interface
- Use
HttpClient / HttpClient.GetFromJsonAsync or other real I/O as needed
- Keep DTOs (e.g. for JSON) in the same file as private classes with
[JsonPropertyName("...")] if required
- Swallow or log errors and return
null or a safe default per existing convention (e.g. RealWeatherDataProvider returns null on failure)
using System.Net.Http.Json;
using System.Text.Json.Serialization;
namespace AnimalAI.Plugins.Providers;
public class Real[Feature]DataProvider : I[Feature]DataProvider
{
private static readonly HttpClient HttpClient = new();
public async Task<ReturnType> MethodNameAsync(...)
{
try
{
return ...;
}
catch
{
return null;
}
}
}
Plugin Injection
In the plugin class under src/Plugins/Base/SK/:
- Add a field and constructor parameter for the provider; keep
ILoggerFactory? as the first optional parameter.
- Default to the fake implementation when none is supplied.
#region Provider Fields
private readonly I[Feature]DataProvider _dataProvider;
#endregion Provider Fields
#region Constructors
public [Feature]Plugin(ILoggerFactory? loggerFactory = null, I[Feature]DataProvider? dataProvider = null)
{
_dataProvider = dataProvider ?? new Fake[Feature]DataProvider();
_logger = loggerFactory?.CreateLogger<[Feature]Plugin>();
}
#endregion Constructors
Use _dataProvider inside kernel functions instead of calling external APIs directly.
Conditional Fake vs Real (Config)
To choose fake vs real at runtime (e.g. in StandardKernel or a nested plugin), follow the Outlook pattern:
- Config: Add a constant or config key (e.g. in
KernelConstants or appsettings), e.g. [Feature]ApiKey or [Feature]ServiceUrl.
- Wire-up: When constructing the plugin (or nested plugin), resolve the provider once:
I[Feature]DataProvider provider = string.IsNullOrEmpty(KernelConstants.[Feature]ConfigKey)
? new Fake[Feature]DataProvider()
: new Real[Feature]DataProvider(KernelConstants.[Feature]ConfigKey);
var plugin = new [Feature]Plugin(loggerFactory, provider);
- KernelConstants: If the project uses
KernelConstants, add something like:
public const string [Feature]ConfigKey = "";
Show this conditional pattern and, if applicable, the KernelConstants addition in the “how to inject” / “how to switch fake vs real” part of the answer.
Reference Implementations
- Interface + small DTO:
src/Plugins/Providers/Interfaces/IOutlookEmailProvider.cs
- Interface only (no DTO):
src/Plugins/Providers/Interfaces/IWeatherDataProvider.cs
- Fake:
src/Plugins/Providers/Fake/FakeWeatherDataProvider.cs, FakeOutlookEmailProvider.cs
- Real:
src/Plugins/Providers/Real/RealWeatherDataProvider.cs
- Plugin using provider:
src/Plugins/Base/SK/WeatherPlugin.cs, OutlookEmailPlugin.cs
- Conditional wiring:
src/AnimalKernel/Core/StandardKernel.cs (search for IOutlookEmailProvider / FakeOutlookEmailProvider)
Checklist Before Delivering