From 541bbf01afcc07b8c440b94fe42734feb806771d Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 11 Jan 2026 16:54:27 -0500 Subject: [PATCH] Refactor: Simplify receipt storage path handling Store only filename in database instead of full relative path. GetReceiptPhysicalPath now combines config base path with filename. AIReceiptParser uses ReceiptManager for consistent path resolution. Co-Authored-By: Claude Opus 4.5 --- MoneyMap/Services/AIReceiptParser.cs | 23 +++++++++++++++++------ MoneyMap/Services/ReceiptManager.cs | 9 ++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/MoneyMap/Services/AIReceiptParser.cs b/MoneyMap/Services/AIReceiptParser.cs index 3bfaa50..c2ac467 100644 --- a/MoneyMap/Services/AIReceiptParser.cs +++ b/MoneyMap/Services/AIReceiptParser.cs @@ -13,10 +13,12 @@ namespace MoneyMap.Services public class AIReceiptParser : IReceiptParser { private readonly MoneyMapContext _db; - private readonly IWebHostEnvironment _environment; + private readonly IReceiptManager _receiptManager; private readonly IPdfToImageConverter _pdfConverter; private readonly OpenAIVisionClient _openAIClient; private readonly ClaudeVisionClient _claudeClient; + private readonly OllamaVisionClient _ollamaClient; + private readonly LlamaCppVisionClient _llamaCppClient; private readonly IMerchantService _merchantService; private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; @@ -24,19 +26,23 @@ namespace MoneyMap.Services public AIReceiptParser( MoneyMapContext db, - IWebHostEnvironment environment, + IReceiptManager receiptManager, IPdfToImageConverter pdfConverter, OpenAIVisionClient openAIClient, ClaudeVisionClient claudeClient, + OllamaVisionClient ollamaClient, + LlamaCppVisionClient llamaCppClient, IMerchantService merchantService, IServiceProvider serviceProvider, ILogger logger) { _db = db; - _environment = environment; + _receiptManager = receiptManager; _pdfConverter = pdfConverter; _openAIClient = openAIClient; _claudeClient = claudeClient; + _ollamaClient = ollamaClient; + _llamaCppClient = llamaCppClient; _merchantService = merchantService; _serviceProvider = serviceProvider; _logger = logger; @@ -52,10 +58,12 @@ namespace MoneyMap.Services return ReceiptParseResult.Failure("Receipt not found."); var selectedModel = model ?? "gpt-4o-mini"; + var isLlamaCpp = selectedModel.StartsWith("llamacpp:"); + var isOllama = selectedModel.StartsWith("ollama:"); var isClaude = selectedModel.StartsWith("claude-"); - var provider = isClaude ? "Anthropic" : "OpenAI"; + var provider = isLlamaCpp ? "LlamaCpp" : (isOllama ? "Ollama" : (isClaude ? "Anthropic" : "OpenAI")); - var filePath = Path.Combine(_environment.WebRootPath, receipt.StoragePath.Replace("/", Path.DirectorySeparatorChar.ToString())); + var filePath = _receiptManager.GetReceiptPhysicalPath(receipt); if (!File.Exists(filePath)) return ReceiptParseResult.Failure("Receipt file not found on disk."); @@ -96,7 +104,10 @@ namespace MoneyMap.Services promptText += "\n\nRespond ONLY with valid JSON, no other text."; // Call appropriate vision API - var client = isClaude ? (IAIVisionClient)_claudeClient : _openAIClient; + IAIVisionClient client = isLlamaCpp ? _llamaCppClient + : isOllama ? _ollamaClient + : isClaude ? _claudeClient + : _openAIClient; var visionResult = await client.AnalyzeImageAsync(base64Data, mediaType, promptText, selectedModel); if (!visionResult.IsSuccess) diff --git a/MoneyMap/Services/ReceiptManager.cs b/MoneyMap/Services/ReceiptManager.cs index 410aef1..553bc2a 100644 --- a/MoneyMap/Services/ReceiptManager.cs +++ b/MoneyMap/Services/ReceiptManager.cs @@ -132,9 +132,8 @@ namespace MoneyMap.Services await file.CopyToAsync(fileStream); } - // Store relative path in database - var relativePath = _configuration["Receipts:StoragePath"] ?? "receipts"; - var relativeStoragePath = $"{relativePath}/{storedFileName}"; + // Store just the filename in database (base path comes from config) + var relativeStoragePath = storedFileName; // Create receipt record var receipt = new Receipt @@ -327,8 +326,8 @@ namespace MoneyMap.Services public string GetReceiptPhysicalPath(Receipt receipt) { - // StoragePath is like "receipts/filename.jpg" - return Path.Combine(_environment.WebRootPath, receipt.StoragePath.Replace("/", Path.DirectorySeparatorChar.ToString())); + // StoragePath is just the filename, combine with configured base path + return Path.Combine(GetReceiptsBasePath(), receipt.StoragePath); } public async Task GetReceiptAsync(long receiptId)