Files
MoneyMap/MoneyMap/Program.cs
AJ Isaacs 705f4ea201 Feature: Receipt parse queue with background worker
Add ReceiptParseQueue (Channel-based singleton) and
ReceiptParseWorkerService (BackgroundService) for sequential receipt
parsing. Replaces fire-and-forget Task.Run with a proper queue.
ReceiptManager now enqueues uploaded receipts and supports bulk upload
via UploadManyUnmappedReceiptsAsync. Worker recovers pending items on
startup. Register IAIToolExecutor and IAIVisionClientResolver in DI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 19:14:05 -05:00

130 lines
4.5 KiB
C#

using System.Globalization;
using Microsoft.EntityFrameworkCore;
using MoneyMap.Data;
using MoneyMap.Services;
using MoneyMap.Services.AITools;
// Set default culture to en-US for currency formatting ($)
var culture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MoneyMapContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MoneyMapDb")));
// Add memory cache for services like TransactionCategorizer
builder.Services.AddMemoryCache();
// Add session support
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
options.IOTimeout = TimeSpan.FromMinutes(5); // Increase timeout for large data
});
// Use session-based TempData provider to avoid cookie size limits
builder.Services.AddRazorPages()
.AddSessionStateTempDataProvider();
// Core transaction and import services
builder.Services.AddScoped<ITransactionImporter, TransactionImporter>();
builder.Services.AddScoped<ICardResolver, CardResolver>();
builder.Services.AddScoped<ITransactionCategorizer, TransactionCategorizer>();
builder.Services.AddScoped<ITransactionService, TransactionService>();
builder.Services.AddScoped<ITransactionStatisticsService, TransactionStatisticsService>();
// Entity management services
builder.Services.AddScoped<IAccountService, AccountService>();
builder.Services.AddScoped<ICardService, CardService>();
builder.Services.AddScoped<IMerchantService, MerchantService>();
builder.Services.AddScoped<IBudgetService, BudgetService>();
// Receipt services
builder.Services.AddScoped<IReceiptMatchingService, ReceiptMatchingService>();
// Reference data services
builder.Services.AddScoped<IReferenceDataService, ReferenceDataService>();
// Dashboard services
builder.Services.AddScoped<IDashboardService, DashboardService>();
builder.Services.AddScoped<IDashboardStatsCalculator, DashboardStatsCalculator>();
builder.Services.AddScoped<ITopCategoriesProvider, TopCategoriesProvider>();
builder.Services.AddScoped<IRecentTransactionsProvider, RecentTransactionsProvider>();
builder.Services.AddScoped<ISpendTrendsProvider, SpendTrendsProvider>();
// Receipt services
builder.Services.AddScoped<IReceiptManager, ReceiptManager>();
builder.Services.AddScoped<IReceiptAutoMapper, ReceiptAutoMapper>();
builder.Services.AddScoped<IPdfToImageConverter, PdfToImageConverter>();
// Receipt parse queue and background worker
builder.Services.AddSingleton<IReceiptParseQueue, ReceiptParseQueue>();
builder.Services.AddHostedService<ReceiptParseWorkerService>();
// AI vision clients and tool-use support
builder.Services.AddHttpClient<OpenAIVisionClient>();
builder.Services.AddHttpClient<ClaudeVisionClient>();
builder.Services.AddHttpClient<OllamaVisionClient>();
builder.Services.AddHttpClient<LlamaCppVisionClient>();
builder.Services.AddScoped<IAIVisionClientResolver, AIVisionClientResolver>();
builder.Services.AddScoped<IAIToolExecutor, AIToolExecutor>();
builder.Services.AddScoped<IReceiptParser, AIReceiptParser>();
// AI categorization service
builder.Services.AddHttpClient<ITransactionAICategorizer, TransactionAICategorizer>();
// Model warmup service - preloads the configured AI model on startup
builder.Services.AddHostedService<ModelWarmupService>();
// Financial audit API service
builder.Services.AddScoped<IFinancialAuditService, FinancialAuditService>();
var app = builder.Build();
// Seed default category mappings on startup
using (var scope = app.Services.CreateScope())
{
var categorizer = scope.ServiceProvider.GetRequiredService<ITransactionCategorizer>();
await categorizer.SeedDefaultMappingsAsync();
}
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseAuthorization();
app.MapRazorPages();
// Financial Audit API endpoint
app.MapGet("/api/audit", async (
IFinancialAuditService auditService,
DateTime? startDate,
DateTime? endDate,
bool includeTransactions = false) =>
{
var end = endDate ?? DateTime.Today;
var start = startDate ?? end.AddDays(-90);
var result = await auditService.GenerateAuditAsync(start, end, includeTransactions);
return Results.Ok(result);
})
.WithName("GetFinancialAudit");
app.Run();