feat: add extraction schema, sidebar nav, few-shot prompting, and prompt settings

Overhaul extraction pipeline with new TradeItem model, conversation flow,
and dedicated extraction endpoint. Add sidebar navigation with NavMenu
component and landing page. Introduce few-shot prompting service and
tests. Add prompt settings and email upload specs. Update OpenSpec
tooling with improved export-spec and extract-feature commands. Archive
completed changes and export full specs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
local
2026-04-06 23:39:23 +01:00
parent 7a5c22593a
commit 5b027eb0db
83 changed files with 4242 additions and 296 deletions

View File

@@ -53,10 +53,50 @@ builder.Services.AddOpenAIChatCompletion(
// any AI services (like the chat completion above) that are already registered.
builder.Services.AddKernel();
// Register the ExtractionPlugin so the Kernel can expose its [KernelFunction] methods
// as tools. When the LLM sees these tools, it can decide to call them during a conversation
// to validate extracted data. The plugin is registered as a singleton via DI.
builder.Services.AddSingleton<ChatAgent.Api.Plugins.ExtractionPlugin>();
// --- External API Typed HttpClients ---
//
// Each extraction tool wraps an external API call. We register typed HttpClients
// so that each service gets its own HttpClient with a pre-configured base URL.
// AddHttpClient<T>() uses IHttpClientFactory under the hood, which manages
// HttpClient lifetimes and avoids socket exhaustion.
builder.Services.AddHttpClient<ChatAgent.Api.Services.CounterpartyApiClient>(client =>
{
client.BaseAddress = new Uri(
builder.Configuration["ExternalApis:CounterpartyBaseUrl"] ?? "http://localhost:5000/api/counterparty");
});
builder.Services.AddHttpClient<ChatAgent.Api.Services.TradeApiClient>(client =>
{
client.BaseAddress = new Uri(
builder.Configuration["ExternalApis:TradeBaseUrl"] ?? "http://localhost:5000/api/trade");
});
builder.Services.AddHttpClient<ChatAgent.Api.Services.CurrencyApiClient>(client =>
{
client.BaseAddress = new Uri(
builder.Configuration["ExternalApis:CurrencyBaseUrl"] ?? "http://localhost:5000/api/currency");
});
// --- Few-Shot Prompting Service ---
//
// FewShotService loads the extraction instruction template and few-shot examples
// from disk at startup. It pre-assembles a ChatHistory prefix that is cloned
// for each extraction request. Registered as a singleton since examples don't
// change at runtime.
// Resolve the examples path relative to the content root so it works correctly
// both when running the app normally and in WebApplicationFactory test contexts.
var examplesRelativePath = builder.Configuration["Examples:FewShotPath"] ?? "examples/extraction";
var examplesAbsolutePath = Path.IsPathRooted(examplesRelativePath)
? examplesRelativePath
: Path.Combine(builder.Environment.ContentRootPath, examplesRelativePath);
builder.Services.AddSingleton(new ChatAgent.Api.Services.FewShotService(examplesAbsolutePath));
// Register the ExtractionPlugin with its typed HttpClient dependencies.
// The plugin exposes [KernelFunction] methods as tools the LLM can call
// to look up counterparties, validate trades, currencies, and the extraction schema.
// Scoped lifetime because it depends on typed HttpClients (which are transient).
builder.Services.AddScoped<ChatAgent.Api.Plugins.ExtractionPlugin>();
// AddCors() registers Cross-Origin Resource Sharing services.
// CORS is REQUIRED because the Blazor WASM client runs on a different origin