diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/FileManagerController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/FileManagerController.cs index 75322fd..4c3354d 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/FileManagerController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/FileManagerController.cs @@ -14,6 +14,7 @@ using Nop.Plugin.Misc.FruitBankPlugin.Helpers; using Nop.Plugin.Misc.FruitBankPlugin.Services; using Nop.Plugin.Misc.FruitBankPlugin.Services.FileStorage; using Nop.Services.Catalog; +using Nop.Services.Common; using Nop.Services.Security; using Nop.Web.Framework; using Nop.Web.Framework.Controllers; @@ -31,31 +32,37 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers { private readonly IPermissionService _permissionService; private readonly OpenAIApiService _aiApiService; + private readonly CerebrasAPIService _cerebrasApiService; private readonly AICalculationService _aiCalculationService; private readonly IProductService _productService; private readonly FruitBankDbContext _dbContext; private readonly PdfToImageService _pdfToImageService; private readonly IWorkContext _workContext; private readonly FileStorageService _fileStorageService; + private readonly FruitBankAttributeService _fruitBankAttributeService; public FileManagerController( IPermissionService permissionService, OpenAIApiService aiApiService, + CerebrasAPIService cerebrasApiService, AICalculationService aiCalculationService, IProductService productService, FruitBankDbContext fruitBankDbContext, PdfToImageService pdfToImageService, IWorkContext workContext, - FileStorageService fileStorageService) + FileStorageService fileStorageService, + FruitBankAttributeService fruitBankAttributeService) { _permissionService = permissionService; _aiApiService = aiApiService; + _cerebrasApiService = cerebrasApiService; _aiCalculationService = aiCalculationService; _productService = productService; _dbContext = fruitBankDbContext; _pdfToImageService = pdfToImageService; _workContext = workContext; _fileStorageService = fileStorageService; + _fruitBankAttributeService = fruitBankAttributeService; } /// @@ -483,7 +490,8 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers deserializedProduct.name + "\n\n" + "Return the best matching product name from the catalog above (matching ALL details including size/grade), or 'NONE' if no good match exists."; - var aiMatchedProductName = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + //var aiMatchedProductName = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + var aiMatchedProductName = await _cerebrasApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); //var aiMatchedProductName = await _aiApiService.GetSimpleResponseAsync(aiMatchPrompt, deserializedProduct.name); Console.WriteLine($"AI matched product name for {deserializedProduct.name}: {aiMatchedProductName}"); @@ -577,7 +585,8 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers deserializedProduct.name + "\n\n" + "Return the best matching product name from the catalog above that matches ALL details (size, grade, quality), or 'NONE' if no confident match exists."; - var aiMatchedProductName2 = await _aiApiService.GetSimpleResponseAsync(systemPrompt2, userPrompt2); + //var aiMatchedProductName2 = await _aiApiService.GetSimpleResponseAsync(systemPrompt2, userPrompt2); + var aiMatchedProductName2 = await _cerebrasApiService.GetSimpleResponseAsync(systemPrompt2, userPrompt2); Console.WriteLine($"AI matched product name from hybrid catalog for {deserializedProduct.name}: {aiMatchedProductName2}"); if (!string.IsNullOrEmpty(aiMatchedProductName2) && aiMatchedProductName2 != "NONE") @@ -737,7 +746,8 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers partnerAnalysis + "\n\n" + "Return ONLY the numeric ID of the matching partner, or '0' if no match found."; - var aiResponse = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + //var aiResponse = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + var aiResponse = await _cerebrasApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); Console.WriteLine($"AI Partner Match Response: {aiResponse}"); // Parse the ID @@ -828,7 +838,8 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers "4. Return ONLY the company name, nothing else\n\n" + "If uncertain, return the most prominent non-FruitBank company name from the document."; - var partnerAnalysis = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + //var partnerAnalysis = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + var partnerAnalysis = await _cerebrasApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); // Clean up the response var cleanedPartnerName = CleanPartnerName(partnerAnalysis); @@ -1034,7 +1045,7 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers UnitPriceOnDocument = itemDto.UnitPriceOnDocument, IsMeasurable = itemDto.IsMeasurable }; - + shippingDocument.ShippingItems.Add(shippingItem); } @@ -1087,6 +1098,17 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers Console.WriteLine("⚠ No original file provided - skipping file save"); } + //everything done, let's update the genericattribute "IncomingQuantity" of the product + foreach (var item in shippingDocument.ShippingItems.Where(x => x.ProductId != null)) + { + await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync( + item.ProductId.Value, + "IncomingQuantity", + item.QuantityOnDocument + ); + } + + return Json(new { success = true, diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/VoiceOrderController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/VoiceOrderController.cs index f10fea3..bde8a24 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/VoiceOrderController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/VoiceOrderController.cs @@ -24,6 +24,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers { private readonly IPermissionService _permissionService; private readonly OpenAIApiService _aiApiService; + private readonly CerebrasAPIService _cerebrasApiService; private readonly ICustomerService _customerService; private readonly IProductService _productService; private readonly FruitBankDbContext _dbContext; @@ -31,12 +32,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers public VoiceOrderController( IPermissionService permissionService, OpenAIApiService aiApiService, + CerebrasAPIService cerebrasApiService, ICustomerService customerService, IProductService productService, FruitBankDbContext dbContext) { _permissionService = permissionService; _aiApiService = aiApiService; + _cerebrasApiService = cerebrasApiService; _customerService = customerService; _productService = productService; _dbContext = dbContext; @@ -320,6 +323,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers transcribedText = await _aiApiService.TranscribeAudioAsync(audioStream, fileName, language, customPrompt); } + if(transcribedText.EndsWith(".") || transcribedText.EndsWith("!") || transcribedText.EndsWith("?")) + { + //remove trailing punctuation + transcribedText = transcribedText.Substring(0, transcribedText.Length - 1); + } + // Clean up temporary file try { @@ -412,9 +421,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers try { // Get all customers with company names (increased limit) - var allCustomersWithCompany = await _customerService.GetAllCustomersAsync( - pageIndex: 0, - pageSize: 1000); // Increased from 500 to catch more companies + var allCustomersWithCompany = await _customerService.GetAllCustomersAsync(); // Increased from 500 to catch more companies // Filter to only those with company names var customersWithCompany = allCustomersWithCompany @@ -441,7 +448,7 @@ CRITICAL MATCHING RULES (in priority order): 2. SUBSTRING MATCH: If the search term is contained within a company name (e.g., 'Junket' in 'Junket Silver Kft.') 3. WORD MATCH: If all words from search term appear in company name (any order) 4. PARTIAL MATCH: If significant words overlap (e.g., 'Silver' matches 'Junket Silver') -5. PHONETIC SIMILARITY: How it sounds when spoken +5. PHONETIC SIMILARITY: How it sounds when spoken in Hungarian (e.g. 'Ökotály' should be matched to 'Öcotáj') 6. ABBREVIATIONS: 'SFI' matches 'SFI Rotterdam B.V.' EXAMPLES: @@ -461,7 +468,8 @@ OUTPUT FORMAT (JSON only): Companies: {companyList}"; - var aiResponse = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + //var aiResponse = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + var aiResponse = await _cerebrasApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); Console.WriteLine($"[VoiceOrder] AI company matching response: {aiResponse}"); @@ -552,7 +560,8 @@ Output: [{""product"":""szőlő"",""quantity"":50}]"; var userPrompt = $"Parse this: {text}"; - var aiResponse = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + //var aiResponse = await _aiApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); + var aiResponse = await _cerebrasApiService.GetSimpleResponseAsync(systemPrompt, userPrompt); Console.WriteLine($"[VoiceOrder] AI Response: {aiResponse}"); diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Extras/ImageTextExtraction.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Extras/ImageTextExtraction.cshtml index 96490f2..93dd6fb 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Extras/ImageTextExtraction.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Extras/ImageTextExtraction.cshtml @@ -8,238 +8,433 @@ @Html.AntiForgeryToken() -
-
-

Image & PDF Text Extraction (OCR)

-

Upload an image or PDF to extract text using OpenAI Vision API.

+ + -
-
-
- - + + + +
+ +
+
+ +
+
+
Dokumentum feltöltés
- Supported formats: JPG, PNG, GIF, WebP, PDF -
-
- -
-
- - - Customize how the AI should extract and format the text -
-
- -
-
- -
-
- -
-
- -
-
- - - - -