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)