diff --git a/src/Client/Client.cs b/src/Client/Client.cs index 5191e49..85718a0 100644 --- a/src/Client/Client.cs +++ b/src/Client/Client.cs @@ -219,6 +219,11 @@ public class Client return await GetUrlAndProcessJson(GetUrl($"{baseUri}/Server", "Models", apiKey, [])); } + public async Task ServerGetEmbeddingCacheSizeAsync() + { + return await GetUrlAndProcessJson(GetUrl($"{baseUri}/Server/EmbeddingCache", "Size", apiKey, [])); + } + private static async Task GetUrlAndProcessJson(string url) { using var client = new HttpClient(); diff --git a/src/Server/Controllers/Frontend/HomeController.cs b/src/Server/Controllers/Frontend/HomeController.cs index 6feb134..9a6c732 100644 --- a/src/Server/Controllers/Frontend/HomeController.cs +++ b/src/Server/Controllers/Frontend/HomeController.cs @@ -8,7 +8,7 @@ using Server.Models; namespace Server.Controllers; [ApiExplorerSettings(IgnoreApi = true)] -[Route("/")] +[Route("[Controller]")] public class HomeController : Controller { private readonly ILogger _logger; @@ -23,6 +23,13 @@ public class HomeController : Controller [Authorize] [HttpGet("/")] public IActionResult Index() + { + return View(); + } + + [Authorize] + [HttpGet("Searchdomains")] + public IActionResult Searchdomains() { HomeIndexViewModel viewModel = new() { diff --git a/src/Server/Controllers/ServerController.cs b/src/Server/Controllers/ServerController.cs index d880720..c442838 100644 --- a/src/Server/Controllers/ServerController.cs +++ b/src/Server/Controllers/ServerController.cs @@ -1,6 +1,8 @@ namespace Server.Controllers; +using System.Reflection; using System.Text.Json; +using AdaptiveExpressions; using ElmahCore; using Microsoft.AspNetCore.Mvc; using Server.Exceptions; @@ -14,12 +16,14 @@ public class ServerController : ControllerBase private readonly ILogger _logger; private readonly IConfiguration _config; private AIProvider _aIProvider; + private readonly SearchdomainManager _searchdomainManager; - public ServerController(ILogger logger, IConfiguration config, AIProvider aIProvider) + public ServerController(ILogger logger, IConfiguration config, AIProvider aIProvider, SearchdomainManager searchdomainManager) { _logger = logger; _config = config; _aIProvider = aIProvider; + _searchdomainManager = searchdomainManager; } /// @@ -41,4 +45,51 @@ public class ServerController : ControllerBase return new ServerGetModelsResult() { Success = false, Message = ex.Message}; } } + + /// + /// Gets the total memory size of the embedding cache + /// + [HttpGet("EmbeddingCache/Size")] + public ActionResult GetEmbeddingCacheSize() + { + long size = 0; + long elementCount = 0; + long embeddingsCount = 0; + LRUCache> embeddingCache = _searchdomainManager.embeddingCache; + var cacheListField = embeddingCache.GetType() + .GetField("_cacheList", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("_cacheList field not found"); // TODO Remove this unsafe reflection atrocity + LinkedList cacheListOriginal = (LinkedList)cacheListField.GetValue(embeddingCache)!; + LinkedList cacheList = new(cacheListOriginal); + + foreach (string key in cacheList) + { + if (!embeddingCache.TryGet(key, out var entry)) + continue; + + // estimate size + size += EstimateEntrySize(key, entry); + elementCount++; + embeddingsCount += entry.Keys.Count; + } + return new ServerGetEmbeddingCacheSizeResult() { Success = true, SizeInBytes = size, MaxElementCount = _searchdomainManager.EmbeddingCacheMaxCount, ElementCount = elementCount, EmbeddingsCount = embeddingsCount}; + } + + private static long EstimateEntrySize(string key, Dictionary value) + { + int stringOverhead = MemorySizes.Align(MemorySizes.ObjectHeader + sizeof(int)); + int arrayOverhead = MemorySizes.ArrayHeader; + int dictionaryOverhead = MemorySizes.ObjectHeader; + long size = 0; + + size += stringOverhead + key.Length * sizeof(char); + size += dictionaryOverhead; + + foreach (var kv in value) + { + size += stringOverhead + kv.Key.Length * sizeof(char); + size += arrayOverhead + kv.Value.Length * sizeof(float); + } + + return size; + } } diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 1d5c7c4..2fa7ccc 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -53,8 +53,8 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHealthChecks() - .AddCheck("DatabaseHealthCheck") - .AddCheck("AIProviderHealthCheck"); + .AddCheck("DatabaseHealthCheck", tags: ["Database"]) + .AddCheck("AIProviderHealthCheck", tags: ["AIProvider"]); builder.Services.AddElmah(Options => { @@ -109,6 +109,15 @@ app.Use(async (context, next) => app.UseElmah(); app.MapHealthChecks("/healthz"); +app.MapHealthChecks("/healthz/Database", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions +{ + Predicate = c => c.Name.Contains("Database") +}); + +app.MapHealthChecks("/healthz/AIProvider", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions +{ + Predicate = c => c.Name.Contains("AIProvider") +}); bool IsDevelopment = app.Environment.IsDevelopment(); bool useSwagger = app.Configuration.GetValue("UseSwagger"); diff --git a/src/Server/Views/Home/Index.cshtml b/src/Server/Views/Home/Index.cshtml index 22f6319..79b3339 100644 --- a/src/Server/Views/Home/Index.cshtml +++ b/src/Server/Views/Home/Index.cshtml @@ -1,1685 +1,235 @@ -@using Server.Models -@using System.Web -@using Shared.Models -@using Server.Services -@using Server - -@inject LocalizationService T -@inject AIProvider AIProvider -@model HomeIndexViewModel -@{ - ViewData["Title"] = "Home Page"; - int i = 0; - string[] probMethods = [.. Enum.GetValues(typeof(ProbMethodEnum)).Cast().Select(e => e.ToString())]; - string[] similarityMethods = [.. Enum.GetValues(typeof(SimilarityMethodEnum)).Cast().Select(e => e.ToString())]; - string[] models = AIProvider.GetModels(); - Dictionary domains = []; - Model.Searchdomains.ForEach(domain => - { - domains[i++] = domain; - }); -} - -
-

embeddingsearch

-
- - - - - -
- -
-
-

@T["Searchdomain information and settings"]

- -

@T["Actions"]

- -
-

Searchdomain

-
- - -
-
- - -
-

@T["Settings"]

-
- - -
-
- -
-
- -

@T["Search cache"]

- -
-
- @T["Search cache utilization"]: 0.00MiB -
- -
- -

@T["Database size"]

- -
-
- @T["Database size"]: 0.00MiB -
-
- - -
-
-
-

Recent queries

- -
-
- - - - - - - - - -
NameAction
-
-
- - -
-
-
-

Entities

- -
-
- - - - - - - - - -
NameAction
- @T["Add new entity"] -
-
- -
-
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Server/Views/Home/Searchdomains.cshtml b/src/Server/Views/Home/Searchdomains.cshtml new file mode 100644 index 0000000..d6536c8 --- /dev/null +++ b/src/Server/Views/Home/Searchdomains.cshtml @@ -0,0 +1,1685 @@ +@using Server.Models +@using System.Web +@using Shared.Models +@using Server.Services +@using Server + +@inject LocalizationService T +@inject AIProvider AIProvider +@model HomeIndexViewModel +@{ + ViewData["Title"] = "Searchdomains"; + int i = 0; + string[] probMethods = [.. Enum.GetValues(typeof(ProbMethodEnum)).Cast().Select(e => e.ToString())]; + string[] similarityMethods = [.. Enum.GetValues(typeof(SimilarityMethodEnum)).Cast().Select(e => e.ToString())]; + string[] models = AIProvider.GetModels(); + Dictionary domains = []; + Model.Searchdomains.ForEach(domain => + { + domains[i++] = domain; + }); +} + +
+

embeddingsearch

+
+ + + + + +
+ +
+
+

@T["Searchdomain information and settings"]

+ +

@T["Actions"]

+ +
+

Searchdomain

+
+ + +
+
+ + +
+

@T["Settings"]

+
+ + +
+
+ +
+
+ +

@T["Search cache"]

+ +
+
+ @T["Search cache utilization"]: 0.00MiB +
+ +
+ +

@T["Database size"]

+ +
+
+ @T["Database size"]: 0.00MiB +
+
+ + +
+
+
+

Recent queries

+ +
+
+ + + + + + + + + +
NameAction
+
+
+ + +
+
+
+

Entities

+ +
+
+ + + + + + + + + +
NameAction
+ @T["Add new entity"] +
+
+ +
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Server/Views/Shared/_Layout.cshtml b/src/Server/Views/Shared/_Layout.cshtml index 9fc45ab..d00a284 100644 --- a/src/Server/Views/Shared/_Layout.cshtml +++ b/src/Server/Views/Shared/_Layout.cshtml @@ -8,6 +8,7 @@ @ViewData["Title"] - embeddingsearch +