From e42cc5d8ebec5c219f2adfd56fc749d056690a07 Mon Sep 17 00:00:00 2001 From: Hamid Reza Mohammadi Date: Wed, 9 Sep 2026 13:17:21 +0330 Subject: [PATCH] semantic search --- RMuseum/Controllers/GanjoorController.cs | 35 +++++++++++++++++++++++- RMuseum/RMuseum.xml | 8 +++++- RMuseum/Startup.cs | 22 +++++++++++++++ RMuseum/appsettings.json | 7 +++++ 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/RMuseum/Controllers/GanjoorController.cs b/RMuseum/Controllers/GanjoorController.cs index fb25aa9a..fdbdbb7e 100644 --- a/RMuseum/Controllers/GanjoorController.cs +++ b/RMuseum/Controllers/GanjoorController.cs @@ -9,10 +9,12 @@ using RMuseum.Models.Auth.Memory; using RMuseum.Models.Auth.ViewModel; using RMuseum.Models.Ganjoor; using RMuseum.Models.Ganjoor.PublicExport; +using RMuseum.Models.Ganjoor.SemanticSearch; using RMuseum.Models.Ganjoor.ViewModels; using RMuseum.Models.GanjoorAudio.ViewModels; using RMuseum.Models.GanjoorIntegration; using RMuseum.Services; +using RMuseum.Services.Implementation; using RSecurityBackend.Models.Auth.Memory; using RSecurityBackend.Models.Auth.ViewModels; using RSecurityBackend.Models.Generic; @@ -5021,6 +5023,31 @@ namespace RMuseum.Controllers } + /// + /// semantic ("find a poem about...") search + /// + [HttpPost("search/semantic")] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + public async Task SemanticSearch([FromBody] SemanticSearchRequestDto request) + { + try + { + var result = await _semanticSearchService.SearchAsync(request.Query, request.TopK); + return Ok(result); + } + catch (ArgumentException exp) + { + return BadRequest(exp.Message); + } + catch (Exception exp) + { + return BadRequest(exp.ToString()); + } + } + + + /// /// readonly mode @@ -5089,6 +5116,9 @@ namespace RMuseum.Controllers /// protected IConfiguration Configuration { get; } + + protected ISemanticSearchService _semanticSearchService; + /// /// constructor /// @@ -5098,13 +5128,15 @@ namespace RMuseum.Controllers /// /// /// + /// public GanjoorController( IGanjoorService ganjoorService, IAppUserService appUserService, IHttpContextAccessor httpContextAccessor, IImageFileService imageFileService, IMemoryCache memoryCache, - IConfiguration configuration + IConfiguration configuration, + ISemanticSearchService semanticSearchService ) { _ganjoorService = ganjoorService; @@ -5113,6 +5145,7 @@ namespace RMuseum.Controllers _imageFileService = imageFileService; _memoryCache = memoryCache; Configuration = configuration; + _semanticSearchService = semanticSearchService; } } } diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml index ebe582f9..2db4d337 100644 --- a/RMuseum/RMuseum.xml +++ b/RMuseum/RMuseum.xml @@ -2644,6 +2644,11 @@ + + + semantic ("find a poem about...") search + + readonly mode @@ -2684,7 +2689,7 @@ Configuration - + constructor @@ -2694,6 +2699,7 @@ + diff --git a/RMuseum/Startup.cs b/RMuseum/Startup.cs index 48c00dfd..066a1060 100644 --- a/RMuseum/Startup.cs +++ b/RMuseum/Startup.cs @@ -21,6 +21,7 @@ using RMuseum.Models.Auth.Memory; using RMuseum.Services; using RMuseum.Services.Implementation; using RMuseum.Services.Implementationa; +using RMuseum.Utils.SemanticSearch; using RSecurityBackend.Authorization; using RSecurityBackend.DbContext; using RSecurityBackend.Models.Auth.Db; @@ -326,6 +327,27 @@ namespace RMuseum services.AddOpenAIService(); + services.AddSingleton(sp => + { + var config = sp.GetRequiredService(); + string dir = config["SemanticSearch:EmbeddingsDirectory"]; + return EmbeddingIndex.Load(dir); + }); + + services.AddSingleton(sp => + { + var config = sp.GetRequiredService(); + int dimension = int.Parse(config["SemanticSearch:Dimension"] ?? "1024"); + return new QueryEmbedder( + config["SemanticSearch:ModelPath"], + config["SemanticSearch:VocabPath"], + config["SemanticSearch:MergesPath"], + dimension); + }); + + services.AddSingleton(); + + } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/RMuseum/appsettings.json b/RMuseum/appsettings.json index 40fe92dc..d24b4bcb 100644 --- a/RMuseum/appsettings.json +++ b/RMuseum/appsettings.json @@ -111,6 +111,13 @@ "RootUrl": "https://i.ganjoor.net/images", "LoadFromExternalServer": "True" }, + "SemanticSearch": { + "EmbeddingsDirectory": "C:\\ganjoor-embeddings-data", + "ModelPath": "C:\\ganjoor-model\\onnx\\model_quantized.onnx", + "VocabPath": "C:\\ganjoor-model\\vocab.json", + "MergesPath": "C:\\ganjoor-model\\merges.txt", + "Dimension": "1024" + }, "WebServiceUrl": "http://localhost:3439", "ReadOnlyMode": "False", "TransilerateFormData": "ctl00%24ContentPlaceHolder2%24ScriptManager1=ctl00%24ContentPlaceHolder2%24UpdatePanel1%7Cctl00%24ContentPlaceHolder2%24btnConvert&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=50SiwtZgcWB3spddjuBb6BloHj933fgmboKNaKJBIwGTAAKq9eDHSPq%2FLjZZI2sJIMOfywb%2FVO3ZWSadvUsRVLOnxPleQwkf3EHhZT9mSeNvEFld9VKQZ0ben0y15vOMMCfoPQ%3D%3D&__VIEWSTATEGENERATOR=82B657B9&__EVENTVALIDATION=wcYH3JuIbMl7ulzDUUsJdcDMJwjBkZ%2BEyx5Gwv7F31G5yIN75QiuxDkD7acnJpR6cBC8VNNEowaHZB5xRMOF7%2BMhsiZ3AugxyWjaeeiiAUvv1BugkbhRft4aXkJpVd3%2FaeYBHFAyDrKDkYIA2jFrmtjkj1NrtRl5uSvsFY5fPVt9antJQ8kgNRfDEeNtsBzU6BH%2FCw%3D%3D&ctl00%24ContentPlaceHolder2%24txtPersian=%D8%AD%D8%B3%D9%86&ctl00%24ContentPlaceHolder2%24txtSuggest=&__ASYNCPOST=true&ctl00%24ContentPlaceHolder2%24btnConvert=%D8%A8%D8%B1%DA%AF%D8%B1%D8%AF%D8%A7%D9%86",