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]DataProviderorI[Name]Providerif 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
OutlookEmailinIOutlookEmailProvider.cs).
- •Signature, parameters, return type (prefer
- •
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>orTask<T?> - •Shared DTOs (e.g.
OutlookEmail) in the same file as the interface when they are specific to that provider
namespace AnimalAI.Plugins.Providers;
/// <summary>
/// Interface for [purpose].
/// </summary>
public interface I[Feature]DataProvider
{
/// <summary>[Description]</summary>
/// <param name="...">...</param>
/// <returns>...</returns>
Task<ReturnType> MethodNameAsync(...);
}
Fake Provider Template
- •Implement the interface
- •No external calls; use in-memory data (e.g.
Dictionary,staticfields, or hard-coded lists) - •Return immediately with
Task.FromResult(...)for sync data, orTask.FromResultfor 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;
/// <summary>
/// Fake [feature] data provider that returns mock data for testing.
/// </summary>
public class Fake[Feature]DataProvider : I[Feature]DataProvider
{
public Task<ReturnType> MethodNameAsync(...)
{
// Return mock data; use Task.FromResult(...) if no real async work
return Task.FromResult(...);
}
}
Real Provider Template
- •Implement the interface
- •Use
HttpClient/HttpClient.GetFromJsonAsyncor 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
nullor a safe default per existing convention (e.g.RealWeatherDataProviderreturnsnullon failure)
using System.Net.Http.Json;
using System.Text.Json.Serialization;
namespace AnimalAI.Plugins.Providers;
/// <summary>
/// Real [feature] data provider that uses [API/service].
/// </summary>
public class Real[Feature]DataProvider : I[Feature]DataProvider
{
private static readonly HttpClient HttpClient = new();
public async Task<ReturnType> MethodNameAsync(...)
{
try
{
// Call external API; map response to interface return type
return ...;
}
catch
{
return null; // or default; match existing Real* style
}
}
}
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
KernelConstantsor appsettings), e.g.[Feature]ApiKeyor[Feature]ServiceUrl. - •Wire-up: When constructing the plugin (or nested plugin), resolve the provider once:
// Uses Real[Feature]DataProvider if config is set, otherwise Fake for testing
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:
/// <summary> /// [Purpose]. Leave empty to use fake data for testing. /// </summary> 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 forIOutlookEmailProvider/FakeOutlookEmailProvider)
Checklist Before Delivering
- • Interface and implementations use namespace
AnimalAI.Plugins.Providers. - • Only requested artifacts were created (Interface, Fake, Real, or both).
- • Plugin constructor uses
ILoggerFactory?thenI[Feature]DataProvider?and defaults provider tonew Fake[Feature]DataProvider()when null. - • If “conditional use” was requested, answer includes the conditional wiring snippet and any
KernelConstants(or config) change. - • Method names and return types match what the user asked for.