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",