Merge pull request #64 from LD-Reborn/62-add-an-embedding-cache-size-label-to-front-end

Added home page dashboard, added embedding cache size estimation and …
This commit is contained in:
LD50
2025-12-30 02:55:23 +01:00
committed by GitHub
9 changed files with 2015 additions and 1692 deletions

View File

@@ -219,6 +219,11 @@ public class Client
return await GetUrlAndProcessJson<ServerGetModelsResult>(GetUrl($"{baseUri}/Server", "Models", apiKey, [])); return await GetUrlAndProcessJson<ServerGetModelsResult>(GetUrl($"{baseUri}/Server", "Models", apiKey, []));
} }
public async Task<ServerGetEmbeddingCacheSizeResult> ServerGetEmbeddingCacheSizeAsync()
{
return await GetUrlAndProcessJson<ServerGetEmbeddingCacheSizeResult>(GetUrl($"{baseUri}/Server/EmbeddingCache", "Size", apiKey, []));
}
private static async Task<T> GetUrlAndProcessJson<T>(string url) private static async Task<T> GetUrlAndProcessJson<T>(string url)
{ {
using var client = new HttpClient(); using var client = new HttpClient();

View File

@@ -8,7 +8,7 @@ using Server.Models;
namespace Server.Controllers; namespace Server.Controllers;
[ApiExplorerSettings(IgnoreApi = true)] [ApiExplorerSettings(IgnoreApi = true)]
[Route("/")] [Route("[Controller]")]
public class HomeController : Controller public class HomeController : Controller
{ {
private readonly ILogger<EntityController> _logger; private readonly ILogger<EntityController> _logger;
@@ -23,6 +23,13 @@ public class HomeController : Controller
[Authorize] [Authorize]
[HttpGet("/")] [HttpGet("/")]
public IActionResult Index() public IActionResult Index()
{
return View();
}
[Authorize]
[HttpGet("Searchdomains")]
public IActionResult Searchdomains()
{ {
HomeIndexViewModel viewModel = new() HomeIndexViewModel viewModel = new()
{ {

View File

@@ -1,6 +1,8 @@
namespace Server.Controllers; namespace Server.Controllers;
using System.Reflection;
using System.Text.Json; using System.Text.Json;
using AdaptiveExpressions;
using ElmahCore; using ElmahCore;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Server.Exceptions; using Server.Exceptions;
@@ -14,12 +16,14 @@ public class ServerController : ControllerBase
private readonly ILogger<ServerController> _logger; private readonly ILogger<ServerController> _logger;
private readonly IConfiguration _config; private readonly IConfiguration _config;
private AIProvider _aIProvider; private AIProvider _aIProvider;
private readonly SearchdomainManager _searchdomainManager;
public ServerController(ILogger<ServerController> logger, IConfiguration config, AIProvider aIProvider) public ServerController(ILogger<ServerController> logger, IConfiguration config, AIProvider aIProvider, SearchdomainManager searchdomainManager)
{ {
_logger = logger; _logger = logger;
_config = config; _config = config;
_aIProvider = aIProvider; _aIProvider = aIProvider;
_searchdomainManager = searchdomainManager;
} }
/// <summary> /// <summary>
@@ -41,4 +45,51 @@ public class ServerController : ControllerBase
return new ServerGetModelsResult() { Success = false, Message = ex.Message}; return new ServerGetModelsResult() { Success = false, Message = ex.Message};
} }
} }
/// <summary>
/// Gets the total memory size of the embedding cache
/// </summary>
[HttpGet("EmbeddingCache/Size")]
public ActionResult<ServerGetEmbeddingCacheSizeResult> GetEmbeddingCacheSize()
{
long size = 0;
long elementCount = 0;
long embeddingsCount = 0;
LRUCache<string, Dictionary<string, float[]>> 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<string> cacheListOriginal = (LinkedList<string>)cacheListField.GetValue(embeddingCache)!;
LinkedList<string> 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<string, float[]> 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;
}
} }

View File

@@ -53,8 +53,8 @@ builder.Services.AddSingleton<SearchdomainHelper>();
builder.Services.AddSingleton<SearchdomainManager>(); builder.Services.AddSingleton<SearchdomainManager>();
builder.Services.AddSingleton<AIProvider>(); builder.Services.AddSingleton<AIProvider>();
builder.Services.AddHealthChecks() builder.Services.AddHealthChecks()
.AddCheck<DatabaseHealthCheck>("DatabaseHealthCheck") .AddCheck<DatabaseHealthCheck>("DatabaseHealthCheck", tags: ["Database"])
.AddCheck<AIProviderHealthCheck>("AIProviderHealthCheck"); .AddCheck<AIProviderHealthCheck>("AIProviderHealthCheck", tags: ["AIProvider"]);
builder.Services.AddElmah<XmlFileErrorLog>(Options => builder.Services.AddElmah<XmlFileErrorLog>(Options =>
{ {
@@ -109,6 +109,15 @@ app.Use(async (context, next) =>
app.UseElmah(); app.UseElmah();
app.MapHealthChecks("/healthz"); 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 IsDevelopment = app.Environment.IsDevelopment();
bool useSwagger = app.Configuration.GetValue<bool>("UseSwagger"); bool useSwagger = app.Configuration.GetValue<bool>("UseSwagger");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - embeddingsearch</title> <title>@ViewData["Title"] - embeddingsearch</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<script> <script>
window.appTranslations = { window.appTranslations = {
@@ -29,16 +30,19 @@
@if (User.Identity?.IsAuthenticated == true) @if (User.Identity?.IsAuthenticated == true)
{ {
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">@T["Home"]</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Logout">Logout</a> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Searchdomains">@T["Searchdomains"]</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Logout">@T["Logout"]</a>
</li> </li>
} }
else else
{ {
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Login">Login</a> <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Login">@T["Login"]</a>
</li> </li>
} }
</ul> </ul>

View File

@@ -101,7 +101,7 @@ public struct SearchdomainSettings(bool cacheReconciliation = false)
public bool CacheReconciliation { get; set; } = cacheReconciliation; public bool CacheReconciliation { get; set; } = cacheReconciliation;
} }
internal static class MemorySizes public static class MemorySizes
{ {
public static readonly int PointerSize = IntPtr.Size; public static readonly int PointerSize = IntPtr.Size;
public static readonly int ObjectHeader = PointerSize * 2; public static readonly int ObjectHeader = PointerSize * 2;

View File

@@ -7,3 +7,15 @@ public class ServerGetModelsResult : SuccesMessageBaseModel
[JsonPropertyName("Models")] [JsonPropertyName("Models")]
public string[]? Models { get; set; } public string[]? Models { get; set; }
} }
public class ServerGetEmbeddingCacheSizeResult : SuccesMessageBaseModel
{
[JsonPropertyName("SizeInBytes")]
public required long? SizeInBytes { get; set; }
[JsonPropertyName("MaxElementCount")]
public required long? MaxElementCount { get; set; }
[JsonPropertyName("ElementCount")]
public required long? ElementCount { get; set; }
[JsonPropertyName("EmbeddingsCount")]
public required long? EmbeddingsCount { get; set; }
}