Compare commits
36 Commits
41-create-
...
b5db4bc1e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5db4bc1e4 | ||
| 0f599a49d0 | |||
|
|
4fe6b4a112 | ||
| 16efe447a2 | |||
| 6a7bdf585c | |||
| 31c784f0ab | |||
| 625019f9f4 | |||
| c3dfe1a964 | |||
|
|
d647bedb33 | ||
| fe6bbfe9e5 | |||
|
|
6f7afca195 | ||
| 3fa71a8d8b | |||
|
|
8921121078 | ||
| baf76685b7 | |||
|
|
4030e4a824 | ||
| 7b4a3bd2c8 | |||
|
|
5eabb0d924 | ||
| 40424053da | |||
| f3a4665153 | |||
| a358eaea86 | |||
| 665a392b5a | |||
| 26d0561c3b | |||
| cc93a76546 | |||
|
|
7298593341 | ||
| 25723cb7a4 | |||
|
|
84d83206cb | ||
| b6e01a3f66 | |||
|
|
e4cfcb1030 | ||
| 6d1cffe2db | |||
|
|
dd0019b1c1 | ||
| 5877ebaff2 | |||
| 040d4f916a | |||
|
|
57beddd70f | ||
| 8416d7f404 | |||
| 16f08aa8a7 | |||
|
|
cce42d8ec3 |
@@ -34,9 +34,55 @@ public class Client
|
|||||||
this.searchdomain = searchdomain ?? "";
|
this.searchdomain = searchdomain ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<EntityListResults> EntityListAsync(bool returnEmbeddings = false)
|
||||||
|
{
|
||||||
|
return await EntityListAsync(searchdomain, returnEmbeddings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EntityListResults> EntityListAsync(string searchdomain, bool returnEmbeddings = false)
|
||||||
|
{
|
||||||
|
var url = $"{baseUri}/Entities?apiKey={HttpUtility.UrlEncode(apiKey)}&searchdomain={HttpUtility.UrlEncode(searchdomain)}&returnEmbeddings={HttpUtility.UrlEncode(returnEmbeddings.ToString())}";
|
||||||
|
return await GetUrlAndProcessJson<EntityListResults>(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EntityIndexResult> EntityIndexAsync(List<JSONEntity> jsonEntity)
|
||||||
|
{
|
||||||
|
return await EntityIndexAsync(JsonSerializer.Serialize(jsonEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EntityIndexResult> EntityIndexAsync(string jsonEntity)
|
||||||
|
{
|
||||||
|
var content = new StringContent(jsonEntity, Encoding.UTF8, "application/json");
|
||||||
|
return await PutUrlAndProcessJson<EntityIndexResult>(GetUrl($"{baseUri}", "Entities", apiKey, []), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EntityDeleteResults> EntityDeleteAsync(string entityName)
|
||||||
|
{
|
||||||
|
return await EntityDeleteAsync(searchdomain, entityName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EntityDeleteResults> EntityDeleteAsync(string searchdomain, string entityName)
|
||||||
|
{
|
||||||
|
var url = $"{baseUri}/Entity?apiKey={HttpUtility.UrlEncode(apiKey)}&searchdomain={HttpUtility.UrlEncode(searchdomain)}&entity={HttpUtility.UrlEncode(entityName)}";
|
||||||
|
return await DeleteUrlAndProcessJson<EntityDeleteResults>(url);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<SearchdomainListResults> SearchdomainListAsync()
|
public async Task<SearchdomainListResults> SearchdomainListAsync()
|
||||||
{
|
{
|
||||||
return await GetUrlAndProcessJson<SearchdomainListResults>(GetUrl($"{baseUri}/Searchdomain", "List", apiKey, []));
|
return await GetUrlAndProcessJson<SearchdomainListResults>(GetUrl($"{baseUri}", "Searchdomains", apiKey, []));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SearchdomainCreateResults> SearchdomainCreateAsync()
|
||||||
|
{
|
||||||
|
return await SearchdomainCreateAsync(searchdomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SearchdomainCreateResults> SearchdomainCreateAsync(string searchdomain, SearchdomainSettings searchdomainSettings = new())
|
||||||
|
{
|
||||||
|
return await PostUrlAndProcessJson<SearchdomainCreateResults>(GetUrl($"{baseUri}", "Searchdomain", apiKey, new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{"searchdomain", searchdomain}
|
||||||
|
}), new StringContent(JsonSerializer.Serialize(searchdomainSettings), Encoding.UTF8, "application/json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SearchdomainDeleteResults> SearchdomainDeleteAsync()
|
public async Task<SearchdomainDeleteResults> SearchdomainDeleteAsync()
|
||||||
@@ -46,20 +92,7 @@ public class Client
|
|||||||
|
|
||||||
public async Task<SearchdomainDeleteResults> SearchdomainDeleteAsync(string searchdomain)
|
public async Task<SearchdomainDeleteResults> SearchdomainDeleteAsync(string searchdomain)
|
||||||
{
|
{
|
||||||
return await GetUrlAndProcessJson<SearchdomainDeleteResults>(GetUrl($"{baseUri}/Searchdomain", "Delete", apiKey, new Dictionary<string, string>()
|
return await DeleteUrlAndProcessJson<SearchdomainDeleteResults>(GetUrl($"{baseUri}", "Searchdomain", apiKey, new Dictionary<string, string>()
|
||||||
{
|
|
||||||
{"searchdomain", searchdomain}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SearchdomainCreateResults> SearchdomainCreateAsync()
|
|
||||||
{
|
|
||||||
return await SearchdomainCreateAsync(searchdomain);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SearchdomainCreateResults> SearchdomainCreateAsync(string searchdomain)
|
|
||||||
{
|
|
||||||
return await GetUrlAndProcessJson<SearchdomainCreateResults>(GetUrl($"{baseUri}/Searchdomain", "Create", apiKey, new Dictionary<string, string>()
|
|
||||||
{
|
{
|
||||||
{"searchdomain", searchdomain}
|
{"searchdomain", searchdomain}
|
||||||
}));
|
}));
|
||||||
@@ -72,61 +105,123 @@ public class Client
|
|||||||
return updateResults;
|
return updateResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<SearchdomainUpdateResults> SearchdomainUpdateAsync(string searchdomain, string newName, SearchdomainSettings settings = new())
|
||||||
|
{
|
||||||
|
return await SearchdomainUpdateAsync(searchdomain, newName, JsonSerializer.Serialize(settings));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<SearchdomainUpdateResults> SearchdomainUpdateAsync(string searchdomain, string newName, string settings = "{}")
|
public async Task<SearchdomainUpdateResults> SearchdomainUpdateAsync(string searchdomain, string newName, string settings = "{}")
|
||||||
{
|
{
|
||||||
return await GetUrlAndProcessJson<SearchdomainUpdateResults>(GetUrl($"{baseUri}/Searchdomain", "Update", apiKey, new Dictionary<string, string>()
|
return await PutUrlAndProcessJson<SearchdomainUpdateResults>(GetUrl($"{baseUri}", "Searchdomain", apiKey, new Dictionary<string, string>()
|
||||||
{
|
{
|
||||||
{"searchdomain", searchdomain},
|
{"searchdomain", searchdomain},
|
||||||
{"newName", newName},
|
{"newName", newName}
|
||||||
{"settings", settings}
|
}), new StringContent(settings, Encoding.UTF8, "application/json"));
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EntityQueryResults> EntityQueryAsync(string query)
|
public async Task<SearchdomainSearchesResults> SearchdomainGetQueriesAsync(string searchdomain)
|
||||||
{
|
{
|
||||||
return await EntityQueryAsync(searchdomain, query);
|
Dictionary<string, string> parameters = new()
|
||||||
|
{
|
||||||
|
{"searchdomain", searchdomain}
|
||||||
|
};
|
||||||
|
return await GetUrlAndProcessJson<SearchdomainSearchesResults>(GetUrl($"{baseUri}/Searchdomain", "Queries", apiKey, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EntityQueryResults> EntityQueryAsync(string searchdomain, string query)
|
public async Task<EntityQueryResults> SearchdomainQueryAsync(string query)
|
||||||
{
|
{
|
||||||
return await GetUrlAndProcessJson<EntityQueryResults>(GetUrl($"{baseUri}/Entity", "Query", apiKey, new Dictionary<string, string>()
|
return await SearchdomainQueryAsync(searchdomain, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EntityQueryResults> SearchdomainQueryAsync(string searchdomain, string query, int? topN = null, bool returnAttributes = false)
|
||||||
|
{
|
||||||
|
Dictionary<string, string> parameters = new()
|
||||||
{
|
{
|
||||||
{"searchdomain", searchdomain},
|
{"searchdomain", searchdomain},
|
||||||
{"query", query}
|
{"query", query}
|
||||||
}));
|
};
|
||||||
|
if (topN is not null) parameters.Add("topN", ((int)topN).ToString());
|
||||||
|
if (returnAttributes) parameters.Add("returnAttributes", returnAttributes.ToString());
|
||||||
|
|
||||||
|
return await PostUrlAndProcessJson<EntityQueryResults>(GetUrl($"{baseUri}/Searchdomain", "Query", apiKey, parameters), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EntityIndexResult> EntityIndexAsync(List<JSONEntity> jsonEntity)
|
public async Task<SearchdomainDeleteSearchResult> SearchdomainDeleteQueryAsync(string searchdomain, string query)
|
||||||
{
|
{
|
||||||
return await EntityIndexAsync(JsonSerializer.Serialize(jsonEntity));
|
Dictionary<string, string> parameters = new()
|
||||||
|
{
|
||||||
|
{"searchdomain", searchdomain},
|
||||||
|
{"query", query}
|
||||||
|
};
|
||||||
|
return await DeleteUrlAndProcessJson<SearchdomainDeleteSearchResult>(GetUrl($"{baseUri}/Searchdomain", "Query", apiKey, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EntityIndexResult> EntityIndexAsync(string jsonEntity)
|
public async Task<SearchdomainUpdateSearchResult> SearchdomainUpdateQueryAsync(string searchdomain, string query, List<ResultItem> results)
|
||||||
{
|
{
|
||||||
var content = new StringContent(jsonEntity, Encoding.UTF8, "application/json");
|
Dictionary<string, string> parameters = new()
|
||||||
return await PostUrlAndProcessJson<EntityIndexResult>(GetUrl($"{baseUri}/Entity", "Index", apiKey, []), content);//new FormUrlEncodedContent(values));
|
{
|
||||||
|
{"searchdomain", searchdomain},
|
||||||
|
{"query", query}
|
||||||
|
};
|
||||||
|
return await PatchUrlAndProcessJson<SearchdomainUpdateSearchResult>(
|
||||||
|
GetUrl($"{baseUri}/Searchdomain", "Query", apiKey, parameters),
|
||||||
|
new StringContent(JsonSerializer.Serialize(results), Encoding.UTF8, "application/json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EntityListResults> EntityListAsync(bool returnEmbeddings = false)
|
public async Task<SearchdomainSettingsResults> SearchdomainGetSettingsAsync(string searchdomain)
|
||||||
{
|
{
|
||||||
return await EntityListAsync(searchdomain, returnEmbeddings);
|
Dictionary<string, string> parameters = new()
|
||||||
|
{
|
||||||
|
{"searchdomain", searchdomain}
|
||||||
|
};
|
||||||
|
return await GetUrlAndProcessJson<SearchdomainSettingsResults>(GetUrl($"{baseUri}/Searchdomain", "Settings", apiKey, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EntityListResults> EntityListAsync(string searchdomain, bool returnEmbeddings = false)
|
public async Task<SearchdomainUpdateResults> SearchdomainUpdateSettingsAsync(string searchdomain, SearchdomainSettings searchdomainSettings)
|
||||||
{
|
{
|
||||||
var url = $"{baseUri}/Entity/List?apiKey={HttpUtility.UrlEncode(apiKey)}&searchdomain={HttpUtility.UrlEncode(searchdomain)}&returnEmbeddings={HttpUtility.UrlEncode(returnEmbeddings.ToString())}";
|
Dictionary<string, string> parameters = new()
|
||||||
return await GetUrlAndProcessJson<EntityListResults>(url);
|
{
|
||||||
|
{"searchdomain", searchdomain}
|
||||||
|
};
|
||||||
|
StringContent content = new(JsonSerializer.Serialize(searchdomainSettings), Encoding.UTF8, "application/json");
|
||||||
|
return await PutUrlAndProcessJson<SearchdomainUpdateResults>(GetUrl($"{baseUri}/Searchdomain", "Settings", apiKey, parameters), content);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EntityDeleteResults> EntityDeleteAsync(string entityName)
|
public async Task<SearchdomainSearchCacheSizeResults> SearchdomainGetQueryCacheSizeAsync(string searchdomain)
|
||||||
{
|
{
|
||||||
return await EntityDeleteAsync(searchdomain, entityName);
|
Dictionary<string, string> parameters = new()
|
||||||
|
{
|
||||||
|
{"searchdomain", searchdomain}
|
||||||
|
};
|
||||||
|
return await GetUrlAndProcessJson<SearchdomainSearchCacheSizeResults>(GetUrl($"{baseUri}/Searchdomain/QueryCache", "Size", apiKey, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EntityDeleteResults> EntityDeleteAsync(string searchdomain, string entityName)
|
public async Task<SearchdomainInvalidateCacheResults> SearchdomainClearQueryCache(string searchdomain)
|
||||||
{
|
{
|
||||||
var url = $"{baseUri}/Entity/Delete?apiKey={HttpUtility.UrlEncode(apiKey)}&searchdomain={HttpUtility.UrlEncode(searchdomain)}&entity={HttpUtility.UrlEncode(entityName)}";
|
Dictionary<string, string> parameters = new()
|
||||||
return await GetUrlAndProcessJson<EntityDeleteResults>(url);
|
{
|
||||||
|
{"searchdomain", searchdomain}
|
||||||
|
};
|
||||||
|
return await PostUrlAndProcessJson<SearchdomainInvalidateCacheResults>(GetUrl($"{baseUri}/Searchdomain/QueryCache", "Clear", apiKey, parameters), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SearchdomainGetDatabaseSizeResult> SearchdomainGetDatabaseSizeAsync(string searchdomain)
|
||||||
|
{
|
||||||
|
Dictionary<string, string> parameters = new()
|
||||||
|
{
|
||||||
|
{"searchdomain", searchdomain}
|
||||||
|
};
|
||||||
|
return await GetUrlAndProcessJson<SearchdomainGetDatabaseSizeResult>(GetUrl($"{baseUri}/Searchdomain/Database", "Size", apiKey, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ServerGetModelsResult> ServerGetModelsAsync()
|
||||||
|
{
|
||||||
|
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)
|
||||||
@@ -138,7 +233,8 @@ public class Client
|
|||||||
?? throw new Exception($"Failed to deserialize JSON to type {typeof(T).Name}");
|
?? throw new Exception($"Failed to deserialize JSON to type {typeof(T).Name}");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
private static async Task<T> PostUrlAndProcessJson<T>(string url, HttpContent content)
|
|
||||||
|
private static async Task<T> PostUrlAndProcessJson<T>(string url, HttpContent? content)
|
||||||
{
|
{
|
||||||
using var client = new HttpClient();
|
using var client = new HttpClient();
|
||||||
var response = await client.PostAsync(url, content);
|
var response = await client.PostAsync(url, content);
|
||||||
@@ -148,6 +244,36 @@ public class Client
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<T> PutUrlAndProcessJson<T>(string url, HttpContent content)
|
||||||
|
{
|
||||||
|
using var client = new HttpClient();
|
||||||
|
var response = await client.PutAsync(url, content);
|
||||||
|
string responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonSerializer.Deserialize<T>(responseContent)
|
||||||
|
?? throw new Exception($"Failed to deserialize JSON to type {typeof(T).Name}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<T> PatchUrlAndProcessJson<T>(string url, HttpContent content)
|
||||||
|
{
|
||||||
|
using var client = new HttpClient();
|
||||||
|
var response = await client.PatchAsync(url, content);
|
||||||
|
string responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonSerializer.Deserialize<T>(responseContent)
|
||||||
|
?? throw new Exception($"Failed to deserialize JSON to type {typeof(T).Name}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<T> DeleteUrlAndProcessJson<T>(string url)
|
||||||
|
{
|
||||||
|
using var client = new HttpClient();
|
||||||
|
var response = await client.DeleteAsync(url);
|
||||||
|
string responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonSerializer.Deserialize<T>(responseContent)
|
||||||
|
?? throw new Exception($"Failed to deserialize JSON to type {typeof(T).Name}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetUrl(string baseUri, string endpoint, string apiKey, Dictionary<string, string> parameters)
|
public static string GetUrl(string baseUri, string endpoint, string apiKey, Dictionary<string, string> parameters)
|
||||||
{
|
{
|
||||||
var uriBuilder = new UriBuilder($"{baseUri}/{endpoint}");
|
var uriBuilder = new UriBuilder($"{baseUri}/{endpoint}");
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Server.Models;
|
|||||||
|
|
||||||
namespace Server.Controllers;
|
namespace Server.Controllers;
|
||||||
|
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
[Route("[Controller]")]
|
[Route("[Controller]")]
|
||||||
public class AccountController : Controller
|
public class AccountController : Controller
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,91 +24,27 @@ public class EntityController : ControllerBase
|
|||||||
_databaseHelper = databaseHelper;
|
_databaseHelper = databaseHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Query")]
|
/// <summary>
|
||||||
public ActionResult<EntityQueryResults> Query(string searchdomain, string query, int? topN)
|
/// List the entities in a searchdomain
|
||||||
{
|
/// </summary>
|
||||||
Searchdomain searchdomain_;
|
/// <remarks>
|
||||||
try
|
/// With returnModels = false expect: "Datapoints": [..., "Embeddings": null]<br/>
|
||||||
{
|
/// With returnModels = true expect: "Datapoints": [..., "Embeddings": [{"Model": "...", "Embeddings": []}, ...]]<br/>
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
/// With returnEmbeddings = true expect: "Datapoints": [..., "Embeddings": [{"Model": "...", "Embeddings": [0.007384672,0.01309805,0.0012528514,...]}, ...]]
|
||||||
} catch (SearchdomainNotFoundException)
|
/// </remarks>
|
||||||
{
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
/// <param name="returnModels">Include the models in the response</param>
|
||||||
return Ok(new EntityQueryResults() {Results = [], Success = false, Message = "Searchdomain not found" });
|
/// <param name="returnEmbeddings">Include the embeddings in the response (requires returnModels)</param>
|
||||||
} catch (Exception ex)
|
[HttpGet("/Entities")]
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new EntityQueryResults() {Results = [], Success = false, Message = "Unable to retrieve the searchdomain - it likely exists, but some other error happened." });
|
|
||||||
}
|
|
||||||
List<(float, string)> results = searchdomain_.Search(query, topN);
|
|
||||||
List<EntityQueryResult> queryResults = [.. results.Select(r => new EntityQueryResult
|
|
||||||
{
|
|
||||||
Name = r.Item2,
|
|
||||||
Value = r.Item1
|
|
||||||
})];
|
|
||||||
return Ok(new EntityQueryResults(){Results = queryResults, Success = true });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("Index")]
|
|
||||||
public ActionResult<EntityIndexResult> Index([FromBody] List<JSONEntity>? jsonEntities)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
List<Entity>? entities = _searchdomainHelper.EntitiesFromJSON(
|
|
||||||
_domainManager,
|
|
||||||
_logger,
|
|
||||||
JsonSerializer.Serialize(jsonEntities));
|
|
||||||
if (entities is not null && jsonEntities is not null)
|
|
||||||
{
|
|
||||||
List<string> invalidatedSearchdomains = [];
|
|
||||||
foreach (var jsonEntity in jsonEntities)
|
|
||||||
{
|
|
||||||
string jsonEntityName = jsonEntity.Name;
|
|
||||||
string jsonEntitySearchdomainName = jsonEntity.Searchdomain;
|
|
||||||
if (entities.Select(x => x.name == jsonEntityName).Any()
|
|
||||||
&& !invalidatedSearchdomains.Contains(jsonEntitySearchdomainName))
|
|
||||||
{
|
|
||||||
invalidatedSearchdomains.Add(jsonEntitySearchdomainName);
|
|
||||||
_domainManager.InvalidateSearchdomainCache(jsonEntitySearchdomainName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(new EntityIndexResult() { Success = true });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to deserialize an entity");
|
|
||||||
return Ok(new EntityIndexResult() { Success = false, Message = "Unable to deserialize an entity"});
|
|
||||||
}
|
|
||||||
} catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (ex.InnerException is not null) ex = ex.InnerException;
|
|
||||||
_logger.LogError("Unable to index the provided entities. {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new EntityIndexResult() { Success = false, Message = ex.Message });
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("List")]
|
|
||||||
public ActionResult<EntityListResults> List(string searchdomain, bool returnModels = false, bool returnEmbeddings = false)
|
public ActionResult<EntityListResults> List(string searchdomain, bool returnModels = false, bool returnEmbeddings = false)
|
||||||
{
|
{
|
||||||
if (returnEmbeddings && !returnModels)
|
if (returnEmbeddings && !returnModels)
|
||||||
{
|
{
|
||||||
_logger.LogError("Invalid request for {searchdomain} - embeddings return requested but without models - not possible!", [searchdomain]);
|
_logger.LogError("Invalid request for {searchdomain} - embeddings return requested but without models - not possible!", [searchdomain]);
|
||||||
return Ok(new EntityQueryResults() {Results = [], Success = false, Message = "Invalid request" });
|
return BadRequest(new EntityListResults() {Results = [], Success = false, Message = "Invalid request" });
|
||||||
}
|
|
||||||
Searchdomain searchdomain_;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
|
||||||
} catch (SearchdomainNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
|
||||||
return Ok(new EntityQueryResults() {Results = [], Success = false, Message = "Searchdomain not found" });
|
|
||||||
} catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new EntityQueryResults() {Results = [], Success = false, Message = "Unable to retrieve the searchdomain - it likely exists, but some other error happened." });
|
|
||||||
}
|
}
|
||||||
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
EntityListResults entityListResults = new() {Results = [], Success = true};
|
EntityListResults entityListResults = new() {Results = [], Success = true};
|
||||||
foreach (Entity entity in searchdomain_.entityCache)
|
foreach (Entity entity in searchdomain_.entityCache)
|
||||||
{
|
{
|
||||||
@@ -146,29 +82,69 @@ public class EntityController : ControllerBase
|
|||||||
return Ok(entityListResults);
|
return Ok(entityListResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Delete")]
|
/// <summary>
|
||||||
public ActionResult<EntityDeleteResults> Delete(string searchdomain, string entityName)
|
/// Index entities
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Behavior: Creates new entities, but overwrites existing entities that have the same name
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="jsonEntities">Entities to index</param>
|
||||||
|
[HttpPut("/Entities")]
|
||||||
|
public ActionResult<EntityIndexResult> Index([FromBody] List<JSONEntity>? jsonEntities)
|
||||||
{
|
{
|
||||||
Searchdomain searchdomain_;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
List<Entity>? entities = _searchdomainHelper.EntitiesFromJSON(
|
||||||
} catch (SearchdomainNotFoundException)
|
_domainManager,
|
||||||
{
|
_logger,
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
JsonSerializer.Serialize(jsonEntities));
|
||||||
return Ok(new EntityQueryResults() {Results = [], Success = false, Message = "Searchdomain not found" });
|
if (entities is not null && jsonEntities is not null)
|
||||||
|
{
|
||||||
|
List<string> invalidatedSearchdomains = [];
|
||||||
|
foreach (var jsonEntity in jsonEntities)
|
||||||
|
{
|
||||||
|
string jsonEntityName = jsonEntity.Name;
|
||||||
|
string jsonEntitySearchdomainName = jsonEntity.Searchdomain;
|
||||||
|
if (entities.Select(x => x.name == jsonEntityName).Any()
|
||||||
|
&& !invalidatedSearchdomains.Contains(jsonEntitySearchdomainName))
|
||||||
|
{
|
||||||
|
invalidatedSearchdomains.Add(jsonEntitySearchdomainName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(new EntityIndexResult() { Success = true });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError("Unable to deserialize an entity");
|
||||||
|
return Ok(new EntityIndexResult() { Success = false, Message = "Unable to deserialize an entity"});
|
||||||
|
}
|
||||||
} catch (Exception ex)
|
} catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
if (ex.InnerException is not null) ex = ex.InnerException;
|
||||||
return Ok(new EntityQueryResults() {Results = [], Success = false, Message = "Unable to retrieve the searchdomain - it likely exists, but some other error happened." });
|
_logger.LogError("Unable to index the provided entities. {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]);
|
||||||
|
return Ok(new EntityIndexResult() { Success = false, Message = ex.Message });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes an entity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
/// <param name="entityName">Name of the entity</param>
|
||||||
|
[HttpDelete]
|
||||||
|
public ActionResult<EntityDeleteResults> Delete(string searchdomain, string entityName)
|
||||||
|
{
|
||||||
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
|
|
||||||
Entity? entity_ = SearchdomainHelper.CacheGetEntity(searchdomain_.entityCache, entityName);
|
Entity? entity_ = SearchdomainHelper.CacheGetEntity(searchdomain_.entityCache, entityName);
|
||||||
if (entity_ is null)
|
if (entity_ is null)
|
||||||
{
|
{
|
||||||
_logger.LogError("Unable to delete the entity {entityName} in {searchdomain} - it was not found under the specified name", [entityName, searchdomain]);
|
_logger.LogError("Unable to delete the entity {entityName} in {searchdomain} - it was not found under the specified name", [entityName, searchdomain]);
|
||||||
return Ok(new EntityDeleteResults() {Success = false, Message = "Entity not found"});
|
return Ok(new EntityDeleteResults() {Success = false, Message = "Entity not found"});
|
||||||
}
|
}
|
||||||
|
searchdomain_.ReconciliateOrInvalidateCacheForDeletedEntity(entity_);
|
||||||
_databaseHelper.RemoveEntity([], _domainManager.helper, entityName, searchdomain);
|
_databaseHelper.RemoveEntity([], _domainManager.helper, entityName, searchdomain);
|
||||||
searchdomain_.entityCache.RemoveAll(entity => entity.name == entityName);
|
searchdomain_.entityCache.RemoveAll(entity => entity.name == entityName);
|
||||||
return Ok(new EntityDeleteResults() {Success = true});
|
return Ok(new EntityDeleteResults() {Success = true});
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ using Server.Exceptions;
|
|||||||
using Server.Models;
|
using Server.Models;
|
||||||
namespace Server.Controllers;
|
namespace Server.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using ElmahCore;
|
using ElmahCore;
|
||||||
using Microsoft.AspNetCore.Http.HttpResults;
|
using Microsoft.AspNetCore.Http.HttpResults;
|
||||||
@@ -23,7 +24,10 @@ public class SearchdomainController : ControllerBase
|
|||||||
_domainManager = domainManager;
|
_domainManager = domainManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("List")]
|
/// <summary>
|
||||||
|
/// Lists all searchdomains
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("/Searchdomains")]
|
||||||
public ActionResult<SearchdomainListResults> List()
|
public ActionResult<SearchdomainListResults> List()
|
||||||
{
|
{
|
||||||
List<string> results;
|
List<string> results;
|
||||||
@@ -40,8 +44,13 @@ public class SearchdomainController : ControllerBase
|
|||||||
return Ok(searchdomainListResults);
|
return Ok(searchdomainListResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Create")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainCreateResults> Create(string searchdomain, string settings = "{}")
|
/// Creates a new searchdomain
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
/// <param name="settings">Optional initial settings</param>
|
||||||
|
[HttpPost]
|
||||||
|
public ActionResult<SearchdomainCreateResults> Create([Required]string searchdomain, [FromBody]SearchdomainSettings settings = new())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -54,8 +63,12 @@ public class SearchdomainController : ControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Delete")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainDeleteResults> Delete(string searchdomain)
|
/// Deletes a searchdomain
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
[HttpDelete]
|
||||||
|
public ActionResult<SearchdomainDeleteResults> Delete([Required]string searchdomain)
|
||||||
{
|
{
|
||||||
bool success;
|
bool success;
|
||||||
int deletedEntries;
|
int deletedEntries;
|
||||||
@@ -84,12 +97,27 @@ public class SearchdomainController : ControllerBase
|
|||||||
return Ok(new SearchdomainDeleteResults(){Success = success, DeletedEntities = deletedEntries, Message = message});
|
return Ok(new SearchdomainDeleteResults(){Success = success, DeletedEntities = deletedEntries, Message = message});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("Update")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainUpdateResults> Update(string searchdomain, string newName, string settings = "{}")
|
/// Updates name and settings of a searchdomain
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
/// <param name="newName">Updated name of the searchdomain</param>
|
||||||
|
/// <param name="settings">Updated settings of searchdomain</param>
|
||||||
|
[HttpPut]
|
||||||
|
public ActionResult<SearchdomainUpdateResults> Update([Required]string searchdomain, string newName, [FromBody]SearchdomainSettings? settings)
|
||||||
{
|
{
|
||||||
try
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
|
if (settings is null)
|
||||||
|
{
|
||||||
|
Dictionary<string, dynamic> parameters = new()
|
||||||
|
{
|
||||||
|
{"name", newName},
|
||||||
|
{"id", searchdomain_.id}
|
||||||
|
};
|
||||||
|
searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set name = @name WHERE id = @id", parameters);
|
||||||
|
} else
|
||||||
{
|
{
|
||||||
Searchdomain searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
|
||||||
Dictionary<string, dynamic> parameters = new()
|
Dictionary<string, dynamic> parameters = new()
|
||||||
{
|
{
|
||||||
{"name", newName},
|
{"name", newName},
|
||||||
@@ -97,84 +125,56 @@ public class SearchdomainController : ControllerBase
|
|||||||
{"id", searchdomain_.id}
|
{"id", searchdomain_.id}
|
||||||
};
|
};
|
||||||
searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set name = @name, settings = @settings WHERE id = @id", parameters);
|
searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set name = @name, settings = @settings WHERE id = @id", parameters);
|
||||||
} catch (SearchdomainNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to update searchdomain {searchdomain} - not found", [searchdomain]);
|
|
||||||
return Ok(new SearchdomainUpdateResults() { Success = false, Message = $"Unable to update searchdomain {searchdomain} - not found" });
|
|
||||||
} catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to update searchdomain {searchdomain} - Exception: {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new SearchdomainUpdateResults() { Success = false, Message = $"Unable to update searchdomain {searchdomain}" });
|
|
||||||
}
|
}
|
||||||
return Ok(new SearchdomainUpdateResults(){Success = true});
|
return Ok(new SearchdomainUpdateResults(){Success = true});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("UpdateSettings")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainUpdateResults> UpdateSettings(string searchdomain, [FromBody] SearchdomainSettings request)
|
/// Gets the query cache of a searchdomain
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
[HttpGet("Queries")]
|
||||||
|
public ActionResult<SearchdomainSearchesResults> GetQueries([Required]string searchdomain)
|
||||||
{
|
{
|
||||||
try
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
{
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
Searchdomain searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
|
||||||
Dictionary<string, dynamic> parameters = new()
|
|
||||||
{
|
|
||||||
{"settings", JsonSerializer.Serialize(request)},
|
|
||||||
{"id", searchdomain_.id}
|
|
||||||
};
|
|
||||||
searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set settings = @settings WHERE id = @id", parameters);
|
|
||||||
searchdomain_.settings = request;
|
|
||||||
} catch (SearchdomainNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to update settings for searchdomain {searchdomain} - not found", [searchdomain]);
|
|
||||||
return Ok(new SearchdomainUpdateResults() { Success = false, Message = $"Unable to update settings for searchdomain {searchdomain} - not found" });
|
|
||||||
} catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to update settings for searchdomain {searchdomain} - Exception: {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new SearchdomainUpdateResults() { Success = false, Message = $"Unable to update settings for searchdomain {searchdomain}" });
|
|
||||||
}
|
|
||||||
return Ok(new SearchdomainUpdateResults(){Success = true});
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("GetSearches")]
|
|
||||||
public ActionResult<SearchdomainSearchesResults> GetSearches(string searchdomain)
|
|
||||||
{
|
|
||||||
Searchdomain searchdomain_;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
|
||||||
}
|
|
||||||
catch (SearchdomainNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
|
||||||
return Ok(new SearchdomainSearchesResults() { Searches = [], Success = false, Message = "Searchdomain not found" });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new SearchdomainSearchesResults() { Searches = [], Success = false, Message = ex.Message });
|
|
||||||
}
|
|
||||||
Dictionary<string, DateTimedSearchResult> searchCache = searchdomain_.searchCache;
|
Dictionary<string, DateTimedSearchResult> searchCache = searchdomain_.searchCache;
|
||||||
|
|
||||||
return Ok(new SearchdomainSearchesResults() { Searches = searchCache, Success = true });
|
return Ok(new SearchdomainSearchesResults() { Searches = searchCache, Success = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("Searches")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainDeleteSearchResult> DeleteSearch(string searchdomain, string query)
|
/// Executes a query in the searchdomain
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
/// <param name="query">Query to execute</param>
|
||||||
|
/// <param name="topN">Return only the top N results</param>
|
||||||
|
/// <param name="returnAttributes">Return the attributes of the object</param>
|
||||||
|
[HttpPost("Query")]
|
||||||
|
public ActionResult<EntityQueryResults> Query([Required]string searchdomain, [Required]string query, int? topN, bool returnAttributes = false)
|
||||||
{
|
{
|
||||||
Searchdomain searchdomain_;
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
try
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
|
List<(float, string)> results = searchdomain_.Search(query, topN);
|
||||||
|
List<EntityQueryResult> queryResults = [.. results.Select(r => new EntityQueryResult
|
||||||
{
|
{
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
Name = r.Item2,
|
||||||
}
|
Value = r.Item1,
|
||||||
catch (SearchdomainNotFoundException)
|
Attributes = returnAttributes ? (searchdomain_.entityCache.FirstOrDefault(x => x.name == r.Item2)?.attributes ?? null) : null
|
||||||
{
|
})];
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
return Ok(new EntityQueryResults(){Results = queryResults, Success = true });
|
||||||
return Ok(new SearchdomainDeleteSearchResult() { Success = false, Message = "Searchdomain not found" });
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
/// <summary>
|
||||||
{
|
/// Deletes a query from the query cache
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
/// </summary>
|
||||||
return Ok(new SearchdomainDeleteSearchResult() { Success = false, Message = ex.Message });
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
}
|
/// <param name="query">Query to delete</param>
|
||||||
|
[HttpDelete("Query")]
|
||||||
|
public ActionResult<SearchdomainDeleteSearchResult> DeleteQuery([Required]string searchdomain, [Required]string query)
|
||||||
|
{
|
||||||
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
Dictionary<string, DateTimedSearchResult> searchCache = searchdomain_.searchCache;
|
Dictionary<string, DateTimedSearchResult> searchCache = searchdomain_.searchCache;
|
||||||
bool containsKey = searchCache.ContainsKey(query);
|
bool containsKey = searchCache.ContainsKey(query);
|
||||||
if (containsKey)
|
if (containsKey)
|
||||||
@@ -185,24 +185,17 @@ public class SearchdomainController : ControllerBase
|
|||||||
return Ok(new SearchdomainDeleteSearchResult() {Success = false, Message = "Query not found in search cache"});
|
return Ok(new SearchdomainDeleteSearchResult() {Success = false, Message = "Query not found in search cache"});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("Searches")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainUpdateSearchResult> UpdateSearch(string searchdomain, string query, [FromBody]List<ResultItem> results)
|
/// Updates a query from the query cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
/// <param name="query">Query to update</param>
|
||||||
|
/// <param name="results">List of results to apply to the query</param>
|
||||||
|
[HttpPatch("Query")]
|
||||||
|
public ActionResult<SearchdomainUpdateSearchResult> UpdateQuery([Required]string searchdomain, [Required]string query, [Required][FromBody]List<ResultItem> results)
|
||||||
{
|
{
|
||||||
Searchdomain searchdomain_;
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
try
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
{
|
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
|
||||||
}
|
|
||||||
catch (SearchdomainNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
|
||||||
return Ok(new SearchdomainUpdateSearchResult() { Success = false, Message = "Searchdomain not found" });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new SearchdomainUpdateSearchResult() { Success = false, Message = ex.Message });
|
|
||||||
}
|
|
||||||
Dictionary<string, DateTimedSearchResult> searchCache = searchdomain_.searchCache;
|
Dictionary<string, DateTimedSearchResult> searchCache = searchdomain_.searchCache;
|
||||||
bool containsKey = searchCache.ContainsKey(query);
|
bool containsKey = searchCache.ContainsKey(query);
|
||||||
if (containsKey)
|
if (containsKey)
|
||||||
@@ -215,46 +208,47 @@ public class SearchdomainController : ControllerBase
|
|||||||
return Ok(new SearchdomainUpdateSearchResult() {Success = false, Message = "Query not found in search cache"});
|
return Ok(new SearchdomainUpdateSearchResult() {Success = false, Message = "Query not found in search cache"});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("GetSettings")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainSettingsResults> GetSettings(string searchdomain)
|
/// Get the settings of a searchdomain
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
[HttpGet("Settings")]
|
||||||
|
public ActionResult<SearchdomainSettingsResults> GetSettings([Required]string searchdomain)
|
||||||
{
|
{
|
||||||
Searchdomain searchdomain_;
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
try
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
{
|
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
|
||||||
}
|
|
||||||
catch (SearchdomainNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
|
||||||
return Ok(new SearchdomainSettingsResults() { Settings = null, Success = false, Message = "Searchdomain not found" });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new SearchdomainSettingsResults() { Settings = null, Success = false, Message = ex.Message });
|
|
||||||
}
|
|
||||||
SearchdomainSettings settings = searchdomain_.settings;
|
SearchdomainSettings settings = searchdomain_.settings;
|
||||||
return Ok(new SearchdomainSettingsResults() { Settings = settings, Success = true });
|
return Ok(new SearchdomainSettingsResults() { Settings = settings, Success = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("GetSearchCacheSize")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainSearchCacheSizeResults> GetSearchCacheSize(string searchdomain)
|
/// Update the settings of a searchdomain
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
[HttpPut("Settings")]
|
||||||
|
public ActionResult<SearchdomainUpdateResults> UpdateSettings([Required]string searchdomain, [Required][FromBody] SearchdomainSettings request)
|
||||||
{
|
{
|
||||||
Searchdomain searchdomain_;
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
try
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
|
Dictionary<string, dynamic> parameters = new()
|
||||||
{
|
{
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
{"settings", JsonSerializer.Serialize(request)},
|
||||||
}
|
{"id", searchdomain_.id}
|
||||||
catch (SearchdomainNotFoundException)
|
};
|
||||||
{
|
searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set settings = @settings WHERE id = @id", parameters);
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
searchdomain_.settings = request;
|
||||||
return Ok(new SearchdomainSearchCacheSizeResults() { SearchCacheSizeBytes = null, Success = false, Message = "Searchdomain not found" });
|
return Ok(new SearchdomainUpdateResults(){Success = true});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
/// <summary>
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
/// Get the query cache size of a searchdomain
|
||||||
return Ok(new SearchdomainSearchCacheSizeResults() { SearchCacheSizeBytes = null, Success = false, Message = ex.Message });
|
/// </summary>
|
||||||
}
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
[HttpGet("QueryCache/Size")]
|
||||||
|
public ActionResult<SearchdomainSearchCacheSizeResults> GetSearchCacheSize([Required]string searchdomain)
|
||||||
|
{
|
||||||
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
Dictionary<string, DateTimedSearchResult> searchCache = searchdomain_.searchCache;
|
Dictionary<string, DateTimedSearchResult> searchCache = searchdomain_.searchCache;
|
||||||
long sizeInBytes = 0;
|
long sizeInBytes = 0;
|
||||||
foreach (var entry in searchCache)
|
foreach (var entry in searchCache)
|
||||||
@@ -263,46 +257,31 @@ public class SearchdomainController : ControllerBase
|
|||||||
sizeInBytes += entry.Key.Length * sizeof(char); // string characters
|
sizeInBytes += entry.Key.Length * sizeof(char); // string characters
|
||||||
sizeInBytes += entry.Value.EstimateSize();
|
sizeInBytes += entry.Value.EstimateSize();
|
||||||
}
|
}
|
||||||
return Ok(new SearchdomainSearchCacheSizeResults() { SearchCacheSizeBytes = sizeInBytes, Success = true });
|
return Ok(new SearchdomainSearchCacheSizeResults() { QueryCacheSizeBytes = sizeInBytes, Success = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("ClearSearchCache")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainInvalidateCacheResults> InvalidateSearchCache(string searchdomain)
|
/// Clear the query cache of a searchdomain
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
[HttpPost("QueryCache/Clear")]
|
||||||
|
public ActionResult<SearchdomainInvalidateCacheResults> InvalidateSearchCache([Required]string searchdomain)
|
||||||
{
|
{
|
||||||
try
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
{
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
Searchdomain searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
searchdomain_.InvalidateSearchCache();
|
||||||
searchdomain_.InvalidateSearchCache();
|
|
||||||
} catch (SearchdomainNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to invalidate search cache for searchdomain {searchdomain} - not found", [searchdomain]);
|
|
||||||
return Ok(new SearchdomainInvalidateCacheResults() { Success = false, Message = $"Unable to invalidate search cache for searchdomain {searchdomain} - not found" });
|
|
||||||
} catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to invalidate search cache for searchdomain {searchdomain} - Exception: {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new SearchdomainInvalidateCacheResults() { Success = false, Message = $"Unable to invalidate search cache for searchdomain {searchdomain}" });
|
|
||||||
}
|
|
||||||
return Ok(new SearchdomainInvalidateCacheResults(){Success = true});
|
return Ok(new SearchdomainInvalidateCacheResults(){Success = true});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("GetDatabaseSize")]
|
/// <summary>
|
||||||
public ActionResult<SearchdomainGetDatabaseSizeResult> GetDatabaseSize(string searchdomain)
|
/// Get the disk size of a searchdomain in bytes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchdomain">Name of the searchdomain</param>
|
||||||
|
[HttpGet("Database/Size")]
|
||||||
|
public ActionResult<SearchdomainGetDatabaseSizeResult> GetDatabaseSize([Required]string searchdomain)
|
||||||
{
|
{
|
||||||
Searchdomain searchdomain_;
|
(Searchdomain? searchdomain_, int? httpStatusCode, string? message) = SearchdomainHelper.TryGetSearchdomain(_domainManager, searchdomain, _logger);
|
||||||
try
|
if (searchdomain_ is null || httpStatusCode is not null) return StatusCode(httpStatusCode ?? 500, new SearchdomainUpdateResults(){Success = false, Message = message});
|
||||||
{
|
|
||||||
searchdomain_ = _domainManager.GetSearchdomain(searchdomain);
|
|
||||||
}
|
|
||||||
catch (SearchdomainNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - it likely does not exist yet", [searchdomain]);
|
|
||||||
return Ok(new SearchdomainGetDatabaseSizeResult() { SearchdomainDatabaseSizeBytes = null, Success = false, Message = "Searchdomain not found" });
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError("Unable to retrieve the searchdomain {searchdomain} - {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
|
||||||
return Ok(new SearchdomainGetDatabaseSizeResult() { SearchdomainDatabaseSizeBytes = null, Success = false, Message = ex.Message });
|
|
||||||
}
|
|
||||||
long sizeInBytes = DatabaseHelper.GetSearchdomainDatabaseSize(searchdomain_.helper, searchdomain);
|
long sizeInBytes = DatabaseHelper.GetSearchdomainDatabaseSize(searchdomain_.helper, searchdomain);
|
||||||
return Ok(new SearchdomainGetDatabaseSizeResult() { SearchdomainDatabaseSizeBytes = sizeInBytes, Success = true });
|
return Ok(new SearchdomainGetDatabaseSizeResult() { SearchdomainDatabaseSizeBytes = sizeInBytes, Success = true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,15 +16,23 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("GetModels")]
|
/// <summary>
|
||||||
|
/// Lists the models available to the server
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Returns ALL models available to the server - not only the embedding models.
|
||||||
|
/// </remarks>
|
||||||
|
[HttpGet("Models")]
|
||||||
public ActionResult<ServerGetModelsResult> GetModels()
|
public ActionResult<ServerGetModelsResult> GetModels()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -35,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using AdaptiveExpressions;
|
||||||
using OllamaSharp;
|
using OllamaSharp;
|
||||||
using OllamaSharp.Models;
|
using OllamaSharp.Models;
|
||||||
|
|
||||||
@@ -27,74 +28,31 @@ public class Datapoint
|
|||||||
|
|
||||||
public static Dictionary<string, float[]> GenerateEmbeddings(string content, List<string> models, AIProvider aIProvider)
|
public static Dictionary<string, float[]> GenerateEmbeddings(string content, List<string> models, AIProvider aIProvider)
|
||||||
{
|
{
|
||||||
return GenerateEmbeddings(content, models, aIProvider, []);
|
return GenerateEmbeddings(content, models, aIProvider, new());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dictionary<string, float[]> GenerateEmbeddings(List<string> contents, string model, OllamaApiClient ollama, Dictionary<string, Dictionary<string, float[]>> embeddingCache)
|
public static Dictionary<string, float[]> GenerateEmbeddings(string content, List<string> models, AIProvider aIProvider, LRUCache<string, Dictionary<string, float[]>> embeddingCache)
|
||||||
{
|
|
||||||
Dictionary<string, float[]> retVal = [];
|
|
||||||
|
|
||||||
List<string> remainingContents = new List<string>(contents);
|
|
||||||
for (int i = contents.Count - 1; i >= 0; i--) // Compare against cache and remove accordingly
|
|
||||||
{
|
|
||||||
string content = contents[i];
|
|
||||||
if (embeddingCache.ContainsKey(model) && embeddingCache[model].ContainsKey(content))
|
|
||||||
{
|
|
||||||
retVal[content] = embeddingCache[model][content];
|
|
||||||
remainingContents.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remainingContents.Count == 0)
|
|
||||||
{
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
EmbedRequest request = new()
|
|
||||||
{
|
|
||||||
Model = model,
|
|
||||||
Input = remainingContents
|
|
||||||
};
|
|
||||||
|
|
||||||
EmbedResponse response = ollama.EmbedAsync(request).Result;
|
|
||||||
for (int i = 0; i < response.Embeddings.Count; i++)
|
|
||||||
{
|
|
||||||
string content = remainingContents.ElementAt(i);
|
|
||||||
float[] embeddings = response.Embeddings.ElementAt(i);
|
|
||||||
retVal[content] = embeddings;
|
|
||||||
if (!embeddingCache.ContainsKey(model))
|
|
||||||
{
|
|
||||||
embeddingCache[model] = [];
|
|
||||||
}
|
|
||||||
if (!embeddingCache[model].ContainsKey(content))
|
|
||||||
{
|
|
||||||
embeddingCache[model][content] = embeddings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<string, float[]> GenerateEmbeddings(string content, List<string> models, AIProvider aIProvider, Dictionary<string, Dictionary<string, float[]>> embeddingCache)
|
|
||||||
{
|
{
|
||||||
Dictionary<string, float[]> retVal = [];
|
Dictionary<string, float[]> retVal = [];
|
||||||
foreach (string model in models)
|
foreach (string model in models)
|
||||||
{
|
{
|
||||||
if (embeddingCache.ContainsKey(model) && embeddingCache[model].ContainsKey(content))
|
bool embeddingCacheHasModel = embeddingCache.TryGet(model, out var embeddingCacheForModel);
|
||||||
|
if (embeddingCacheHasModel && embeddingCacheForModel.ContainsKey(content))
|
||||||
{
|
{
|
||||||
retVal[model] = embeddingCache[model][content];
|
retVal[model] = embeddingCacheForModel[content];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var response = aIProvider.GenerateEmbeddings(model, [content]);
|
var response = aIProvider.GenerateEmbeddings(model, [content]);
|
||||||
if (response is not null)
|
if (response is not null)
|
||||||
{
|
{
|
||||||
retVal[model] = response;
|
retVal[model] = response;
|
||||||
if (!embeddingCache.ContainsKey(model))
|
if (!embeddingCacheHasModel)
|
||||||
{
|
{
|
||||||
embeddingCache[model] = [];
|
embeddingCacheForModel = [];
|
||||||
}
|
}
|
||||||
if (!embeddingCache[model].ContainsKey(content))
|
if (!embeddingCacheForModel.ContainsKey(content))
|
||||||
{
|
{
|
||||||
embeddingCache[model][content] = response;
|
embeddingCacheForModel[content] = response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
using Shared.Models;
|
||||||
|
|
||||||
namespace Server.Exceptions;
|
namespace Server.Exceptions;
|
||||||
|
|
||||||
public class ProbMethodNotFoundException(string probMethod) : Exception($"Unknown probMethod name {probMethod}") { }
|
public class ProbMethodNotFoundException(ProbMethodEnum probMethod) : Exception($"Unknown probMethod name {probMethod}") { }
|
||||||
|
|
||||||
public class SimilarityMethodNotFoundException(string similarityMethod) : Exception($"Unknown similarityMethod name \"{similarityMethod}\"") { }
|
public class SimilarityMethodNotFoundException(SimilarityMethodEnum similarityMethod) : Exception($"Unknown similarityMethod name \"{similarityMethod}\"") { }
|
||||||
|
|
||||||
public class JSONPathSelectionException(string path, string testedContent) : Exception($"Unable to select tokens using JSONPath {path} for string: {testedContent}.") { }
|
public class JSONPathSelectionException(string path, string testedContent) : Exception($"Unable to select tokens using JSONPath {path} for string: {testedContent}.") { }
|
||||||
@@ -38,12 +38,12 @@ public class DatabaseHelper(ILogger<DatabaseHelper> logger)
|
|||||||
return helper.ExecuteSQLCommandGetInsertedID("INSERT INTO searchdomain (name, settings) VALUES (@name, @settings)", parameters);
|
return helper.ExecuteSQLCommandGetInsertedID("INSERT INTO searchdomain (name, settings) VALUES (@name, @settings)", parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int DatabaseInsertEntity(SQLHelper helper, string name, string probmethod, int id_searchdomain)
|
public static int DatabaseInsertEntity(SQLHelper helper, string name, ProbMethodEnum probmethod, int id_searchdomain)
|
||||||
{
|
{
|
||||||
Dictionary<string, dynamic> parameters = new()
|
Dictionary<string, dynamic> parameters = new()
|
||||||
{
|
{
|
||||||
{ "name", name },
|
{ "name", name },
|
||||||
{ "probmethod", probmethod },
|
{ "probmethod", probmethod.ToString() },
|
||||||
{ "id_searchdomain", id_searchdomain }
|
{ "id_searchdomain", id_searchdomain }
|
||||||
};
|
};
|
||||||
return helper.ExecuteSQLCommandGetInsertedID("INSERT INTO entity (name, probmethod, id_searchdomain) VALUES (@name, @probmethod, @id_searchdomain)", parameters);
|
return helper.ExecuteSQLCommandGetInsertedID("INSERT INTO entity (name, probmethod, id_searchdomain) VALUES (@name, @probmethod, @id_searchdomain)", parameters);
|
||||||
@@ -60,13 +60,13 @@ public class DatabaseHelper(ILogger<DatabaseHelper> logger)
|
|||||||
return helper.ExecuteSQLCommandGetInsertedID("INSERT INTO attribute (attribute, value, id_entity) VALUES (@attribute, @value, @id_entity)", parameters);
|
return helper.ExecuteSQLCommandGetInsertedID("INSERT INTO attribute (attribute, value, id_entity) VALUES (@attribute, @value, @id_entity)", parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int DatabaseInsertDatapoint(SQLHelper helper, string name, string probmethod_embedding, string similarityMethod, string hash, int id_entity)
|
public static int DatabaseInsertDatapoint(SQLHelper helper, string name, ProbMethodEnum probmethod_embedding, SimilarityMethodEnum similarityMethod, string hash, int id_entity)
|
||||||
{
|
{
|
||||||
Dictionary<string, dynamic> parameters = new()
|
Dictionary<string, dynamic> parameters = new()
|
||||||
{
|
{
|
||||||
{ "name", name },
|
{ "name", name },
|
||||||
{ "probmethod_embedding", probmethod_embedding },
|
{ "probmethod_embedding", probmethod_embedding.ToString() },
|
||||||
{ "similaritymethod", similarityMethod },
|
{ "similaritymethod", similarityMethod.ToString() },
|
||||||
{ "hash", hash },
|
{ "hash", hash },
|
||||||
{ "id_entity", id_entity }
|
{ "id_entity", id_entity }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using AdaptiveExpressions;
|
||||||
using Server.Exceptions;
|
using Server.Exceptions;
|
||||||
using Shared.Models;
|
using Shared.Models;
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
|
|
||||||
public List<Entity>? EntitiesFromJSON(SearchdomainManager searchdomainManager, ILogger logger, string json)
|
public List<Entity>? EntitiesFromJSON(SearchdomainManager searchdomainManager, ILogger logger, string json)
|
||||||
{
|
{
|
||||||
Dictionary<string, Dictionary<string, float[]>> embeddingCache = searchdomainManager.embeddingCache;
|
LRUCache<string, Dictionary<string, float[]>> embeddingCache = searchdomainManager.embeddingCache;
|
||||||
AIProvider aIProvider = searchdomainManager.aIProvider;
|
AIProvider aIProvider = searchdomainManager.aIProvider;
|
||||||
SQLHelper helper = searchdomainManager.helper;
|
SQLHelper helper = searchdomainManager.helper;
|
||||||
|
|
||||||
@@ -91,8 +92,9 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
Searchdomain searchdomain = searchdomainManager.GetSearchdomain(jsonEntity.Searchdomain);
|
Searchdomain searchdomain = searchdomainManager.GetSearchdomain(jsonEntity.Searchdomain);
|
||||||
List<Entity> entityCache = searchdomain.entityCache;
|
List<Entity> entityCache = searchdomain.entityCache;
|
||||||
AIProvider aIProvider = searchdomain.aIProvider;
|
AIProvider aIProvider = searchdomain.aIProvider;
|
||||||
Dictionary<string, Dictionary<string, float[]>> embeddingCache = searchdomain.embeddingCache;
|
LRUCache<string, Dictionary<string, float[]>> embeddingCache = searchdomain.embeddingCache;
|
||||||
Entity? preexistingEntity = entityCache.FirstOrDefault(entity => entity.name == jsonEntity.Name);
|
Entity? preexistingEntity = entityCache.FirstOrDefault(entity => entity.name == jsonEntity.Name);
|
||||||
|
bool invalidateSearchCache = false;
|
||||||
|
|
||||||
if (preexistingEntity is not null)
|
if (preexistingEntity is not null)
|
||||||
{
|
{
|
||||||
@@ -147,8 +149,9 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Datapoint
|
// Datapoint
|
||||||
foreach (Datapoint datapoint in preexistingEntity.datapoints.ToList())
|
foreach (Datapoint datapoint_ in preexistingEntity.datapoints.ToList())
|
||||||
{
|
{
|
||||||
|
Datapoint datapoint = datapoint_; // To enable replacing the datapoint reference as foreach iterators cannot be overwritten
|
||||||
bool newEntityHasDatapoint = jsonEntity.Datapoints.Any(x => x.Name == datapoint.name);
|
bool newEntityHasDatapoint = jsonEntity.Datapoints.Any(x => x.Name == datapoint.name);
|
||||||
if (!newEntityHasDatapoint)
|
if (!newEntityHasDatapoint)
|
||||||
{
|
{
|
||||||
@@ -161,6 +164,7 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
helper.ExecuteSQLNonQuery("DELETE e FROM embedding e JOIN datapoint d ON e.id_datapoint=d.id WHERE d.name=@datapointName AND d.id_entity=@entityId", parameters);
|
helper.ExecuteSQLNonQuery("DELETE e FROM embedding e JOIN datapoint d ON e.id_datapoint=d.id WHERE d.name=@datapointName AND d.id_entity=@entityId", parameters);
|
||||||
helper.ExecuteSQLNonQuery("DELETE FROM datapoint WHERE id_entity=@entityId AND name=@datapointName", parameters);
|
helper.ExecuteSQLNonQuery("DELETE FROM datapoint WHERE id_entity=@entityId AND name=@datapointName", parameters);
|
||||||
preexistingEntity.datapoints.Remove(datapoint);
|
preexistingEntity.datapoints.Remove(datapoint);
|
||||||
|
invalidateSearchCache = true;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
JSONDatapoint? newEntityDatapoint = jsonEntity.Datapoints.FirstOrDefault(x => x.Name == datapoint.name);
|
JSONDatapoint? newEntityDatapoint = jsonEntity.Datapoints.FirstOrDefault(x => x.Name == datapoint.name);
|
||||||
@@ -177,22 +181,24 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
preexistingEntity.datapoints.Remove(datapoint);
|
preexistingEntity.datapoints.Remove(datapoint);
|
||||||
Datapoint newDatapoint = DatabaseInsertDatapointWithEmbeddings(helper, searchdomain, newEntityDatapoint, (int)preexistingEntityID);
|
Datapoint newDatapoint = DatabaseInsertDatapointWithEmbeddings(helper, searchdomain, newEntityDatapoint, (int)preexistingEntityID);
|
||||||
preexistingEntity.datapoints.Add(newDatapoint);
|
preexistingEntity.datapoints.Add(newDatapoint);
|
||||||
|
datapoint = newDatapoint;
|
||||||
|
invalidateSearchCache = true;
|
||||||
}
|
}
|
||||||
if (newEntityDatapoint is not null && (newEntityDatapoint.Probmethod_embedding != datapoint.probMethod.name || newEntityDatapoint.SimilarityMethod != datapoint.similarityMethod.name))
|
if (newEntityDatapoint is not null && (newEntityDatapoint.Probmethod_embedding != datapoint.probMethod.probMethodEnum || newEntityDatapoint.SimilarityMethod != datapoint.similarityMethod.similarityMethodEnum))
|
||||||
{
|
{
|
||||||
// Datapoint - Updated (probmethod or similaritymethod)
|
// Datapoint - Updated (probmethod or similaritymethod)
|
||||||
Dictionary<string, dynamic> parameters = new()
|
Dictionary<string, dynamic> parameters = new()
|
||||||
{
|
{
|
||||||
{ "probmethod", newEntityDatapoint.Probmethod_embedding },
|
{ "probmethod", newEntityDatapoint.Probmethod_embedding.ToString() },
|
||||||
{ "similaritymethod", newEntityDatapoint.SimilarityMethod },
|
{ "similaritymethod", newEntityDatapoint.SimilarityMethod.ToString() },
|
||||||
{ "datapointName", datapoint.name },
|
{ "datapointName", datapoint.name },
|
||||||
{ "entityId", preexistingEntityID}
|
{ "entityId", preexistingEntityID}
|
||||||
};
|
};
|
||||||
helper.ExecuteSQLNonQuery("UPDATE datapoint SET probmethod_embedding=@probmethod, similaritymethod=@similaritymethod WHERE id_entity=@entityId AND name=@datapointName", parameters);
|
helper.ExecuteSQLNonQuery("UPDATE datapoint SET probmethod_embedding=@probmethod, similaritymethod=@similaritymethod WHERE id_entity=@entityId AND name=@datapointName", parameters);
|
||||||
Datapoint preexistingDatapoint = preexistingEntity.datapoints.First(x => x == datapoint); // The for loop is a copy. This retrieves the original such that it can be updated.
|
Datapoint preexistingDatapoint = preexistingEntity.datapoints.First(x => x == datapoint); // The for loop is a copy. This retrieves the original such that it can be updated.
|
||||||
preexistingDatapoint.probMethod = datapoint.probMethod;
|
preexistingDatapoint.probMethod = new(newEntityDatapoint.Probmethod_embedding, _logger);
|
||||||
preexistingDatapoint.similarityMethod = datapoint.similarityMethod;
|
preexistingDatapoint.similarityMethod = new(newEntityDatapoint.SimilarityMethod, _logger);
|
||||||
|
invalidateSearchCache = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,10 +210,14 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
// Datapoint - New
|
// Datapoint - New
|
||||||
Datapoint datapoint = DatabaseInsertDatapointWithEmbeddings(helper, searchdomain, jsonDatapoint, (int)preexistingEntityID);
|
Datapoint datapoint = DatabaseInsertDatapointWithEmbeddings(helper, searchdomain, jsonDatapoint, (int)preexistingEntityID);
|
||||||
preexistingEntity.datapoints.Add(datapoint);
|
preexistingEntity.datapoints.Add(datapoint);
|
||||||
|
invalidateSearchCache = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (invalidateSearchCache)
|
||||||
|
{
|
||||||
|
searchdomain.ReconciliateOrInvalidateCacheForNewOrUpdatedEntity(preexistingEntity);
|
||||||
|
}
|
||||||
return preexistingEntity;
|
return preexistingEntity;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -227,11 +237,12 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
}
|
}
|
||||||
|
|
||||||
var probMethod = Probmethods.GetMethod(jsonEntity.Probmethod) ?? throw new ProbMethodNotFoundException(jsonEntity.Probmethod);
|
var probMethod = Probmethods.GetMethod(jsonEntity.Probmethod) ?? throw new ProbMethodNotFoundException(jsonEntity.Probmethod);
|
||||||
Entity entity = new(jsonEntity.Attributes, probMethod, jsonEntity.Probmethod, datapoints, jsonEntity.Name)
|
Entity entity = new(jsonEntity.Attributes, probMethod, jsonEntity.Probmethod.ToString(), datapoints, jsonEntity.Name)
|
||||||
{
|
{
|
||||||
id = id_entity
|
id = id_entity
|
||||||
};
|
};
|
||||||
entityCache.Add(entity);
|
entityCache.Add(entity);
|
||||||
|
searchdomain.ReconciliateOrInvalidateCacheForNewOrUpdatedEntity(entity);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,7 +272,7 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
throw new Exception("jsonDatapoint.Text must not be null at this point");
|
throw new Exception("jsonDatapoint.Text must not be null at this point");
|
||||||
}
|
}
|
||||||
using SQLHelper helper = searchdomain.helper.DuplicateConnection();
|
using SQLHelper helper = searchdomain.helper.DuplicateConnection();
|
||||||
Dictionary<string, Dictionary<string, float[]>> embeddingCache = searchdomain.embeddingCache;
|
LRUCache<string, Dictionary<string, float[]>> embeddingCache = searchdomain.embeddingCache;
|
||||||
hash ??= Convert.ToBase64String(SHA256.HashData(Encoding.UTF8.GetBytes(jsonDatapoint.Text)));
|
hash ??= Convert.ToBase64String(SHA256.HashData(Encoding.UTF8.GetBytes(jsonDatapoint.Text)));
|
||||||
DatabaseHelper.DatabaseInsertDatapoint(helper, jsonDatapoint.Name, jsonDatapoint.Probmethod_embedding, jsonDatapoint.SimilarityMethod, hash, entityId);
|
DatabaseHelper.DatabaseInsertDatapoint(helper, jsonDatapoint.Name, jsonDatapoint.Probmethod_embedding, jsonDatapoint.SimilarityMethod, hash, entityId);
|
||||||
Dictionary<string, float[]> embeddings = Datapoint.GenerateEmbeddings(jsonDatapoint.Text, [.. jsonDatapoint.Model], searchdomain.aIProvider, embeddingCache);
|
Dictionary<string, float[]> embeddings = Datapoint.GenerateEmbeddings(jsonDatapoint.Text, [.. jsonDatapoint.Model], searchdomain.aIProvider, embeddingCache);
|
||||||
@@ -269,4 +280,21 @@ public class SearchdomainHelper(ILogger<SearchdomainHelper> logger, DatabaseHelp
|
|||||||
var similarityMethod = new SimilarityMethod(jsonDatapoint.SimilarityMethod, logger) ?? throw new SimilarityMethodNotFoundException(jsonDatapoint.SimilarityMethod);
|
var similarityMethod = new SimilarityMethod(jsonDatapoint.SimilarityMethod, logger) ?? throw new SimilarityMethodNotFoundException(jsonDatapoint.SimilarityMethod);
|
||||||
return new Datapoint(jsonDatapoint.Name, probMethod_embedding, similarityMethod, hash, [.. embeddings.Select(kv => (kv.Key, kv.Value))]);
|
return new Datapoint(jsonDatapoint.Name, probMethod_embedding, similarityMethod, hash, [.. embeddings.Select(kv => (kv.Key, kv.Value))]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static (Searchdomain?, int?, string?) TryGetSearchdomain(SearchdomainManager searchdomainManager, string searchdomain, ILogger logger)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Searchdomain searchdomain_ = searchdomainManager.GetSearchdomain(searchdomain);
|
||||||
|
return (searchdomain_, null, null);
|
||||||
|
} catch (SearchdomainNotFoundException)
|
||||||
|
{
|
||||||
|
logger.LogError("Unable to update searchdomain {searchdomain} - not found", [searchdomain]);
|
||||||
|
return (null, 500, $"Unable to update searchdomain {searchdomain} - not found");
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError("Unable to update searchdomain {searchdomain} - Exception: {ex.Message} - {ex.StackTrace}", [searchdomain, ex.Message, ex.StackTrace]);
|
||||||
|
return (null, 404, $"Unable to update searchdomain {searchdomain}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,29 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Server.Exceptions;
|
using Server.Exceptions;
|
||||||
|
using Shared.Models;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
public class ProbMethod
|
public class ProbMethod
|
||||||
{
|
{
|
||||||
public Probmethods.probMethodDelegate method;
|
public Probmethods.probMethodDelegate method;
|
||||||
|
public ProbMethodEnum probMethodEnum;
|
||||||
public string name;
|
public string name;
|
||||||
|
|
||||||
public ProbMethod(string name, ILogger logger)
|
public ProbMethod(ProbMethodEnum probMethodEnum, ILogger logger)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.probMethodEnum = probMethodEnum;
|
||||||
|
this.name = probMethodEnum.ToString();
|
||||||
Probmethods.probMethodDelegate? probMethod = Probmethods.GetMethod(name);
|
Probmethods.probMethodDelegate? probMethod = Probmethods.GetMethod(name);
|
||||||
if (probMethod is null)
|
if (probMethod is null)
|
||||||
{
|
{
|
||||||
logger.LogError("Unable to retrieve probMethod {name}", [name]);
|
logger.LogError("Unable to retrieve probMethod {name}", [name]);
|
||||||
throw new ProbMethodNotFoundException(name);
|
throw new ProbMethodNotFoundException(probMethodEnum);
|
||||||
}
|
}
|
||||||
method = probMethod;
|
method = probMethod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ProbMethodEnum
|
|
||||||
{
|
|
||||||
Mean,
|
|
||||||
HarmonicMean,
|
|
||||||
QuadraticMean,
|
|
||||||
GeometricMean,
|
|
||||||
EVEWAvg,
|
|
||||||
HVEWAvg,
|
|
||||||
LVEWAvg,
|
|
||||||
DictionaryWeightedAverage
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Probmethods
|
public static class Probmethods
|
||||||
{
|
{
|
||||||
@@ -54,6 +46,11 @@ public static class Probmethods
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static probMethodDelegate? GetMethod(ProbMethodEnum probMethodEnum)
|
||||||
|
{
|
||||||
|
return GetMethod(probMethodEnum.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
public static probMethodDelegate? GetMethod(string name)
|
public static probMethodDelegate? GetMethod(string name)
|
||||||
{
|
{
|
||||||
string methodName = name;
|
string methodName = name;
|
||||||
|
|||||||
@@ -8,12 +8,20 @@ using Server.HealthChecks;
|
|||||||
using Server.Helper;
|
using Server.Helper;
|
||||||
using Server.Models;
|
using Server.Models;
|
||||||
using Server.Services;
|
using Server.Services;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
|
||||||
builder.Services.AddControllersWithViews();
|
builder.Services.AddControllersWithViews()
|
||||||
|
.AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
options.JsonSerializerOptions.Converters.Add(
|
||||||
|
new JsonStringEnumConverter()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Add Localization
|
// Add Localization
|
||||||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||||
@@ -30,7 +38,12 @@ builder.Services.AddScoped<LocalizationService>();
|
|||||||
|
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||||
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
||||||
|
c.IncludeXmlComments(xmlPath);
|
||||||
|
});
|
||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
.ReadFrom.Configuration(builder.Configuration)
|
.ReadFrom.Configuration(builder.Configuration)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
@@ -40,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 =>
|
||||||
{
|
{
|
||||||
@@ -96,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");
|
||||||
|
|||||||
@@ -24,4 +24,223 @@
|
|||||||
<data name="IrreversibleActionWarning" xml:space="preserve">
|
<data name="IrreversibleActionWarning" xml:space="preserve">
|
||||||
<value>Diese Aktion kann nicht rückgängig gemacht werden.</value>
|
<value>Diese Aktion kann nicht rückgängig gemacht werden.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Searchdomain selection" xml:space="preserve">
|
||||||
|
<value>Searchdomain Auswahl</value>
|
||||||
|
</data>
|
||||||
|
<data name="Create" xml:space="preserve">
|
||||||
|
<value>Erstellen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain information and settings" xml:space="preserve">
|
||||||
|
<value>Searchdomain Informationen und Einstellungen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Actions" xml:space="preserve">
|
||||||
|
<value>Aktionen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Rename" xml:space="preserve">
|
||||||
|
<value>Umbenennen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete" xml:space="preserve">
|
||||||
|
<value>Löschen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings" xml:space="preserve">
|
||||||
|
<value>Einstellungen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Cache reconciliation" xml:space="preserve">
|
||||||
|
<value>Cache Abgleich</value>
|
||||||
|
</data>
|
||||||
|
<data name="Update" xml:space="preserve">
|
||||||
|
<value>Anpassen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search cache" xml:space="preserve">
|
||||||
|
<value>Such-Cache</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search cache utilization" xml:space="preserve">
|
||||||
|
<value>Such-Cache Speicherauslastung</value>
|
||||||
|
</data>
|
||||||
|
<data name="Clear" xml:space="preserve">
|
||||||
|
<value>Leeren</value>
|
||||||
|
</data>
|
||||||
|
<data name="Database size" xml:space="preserve">
|
||||||
|
<value>Größe in der Datenbank</value>
|
||||||
|
</data>
|
||||||
|
<data name="Add new entity" xml:space="preserve">
|
||||||
|
<value>Neue Entity erstellen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity Details" xml:space="preserve">
|
||||||
|
<value>Entity Details</value>
|
||||||
|
</data>
|
||||||
|
<data name="Attributes" xml:space="preserve">
|
||||||
|
<value>Attribute</value>
|
||||||
|
</data>
|
||||||
|
<data name="Key" xml:space="preserve">
|
||||||
|
<value>Schlüssel</value>
|
||||||
|
</data>
|
||||||
|
<data name="Value" xml:space="preserve">
|
||||||
|
<value>Wert</value>
|
||||||
|
</data>
|
||||||
|
<data name="Datapoints" xml:space="preserve">
|
||||||
|
<value>Datapoints</value>
|
||||||
|
</data>
|
||||||
|
<data name="Name" xml:space="preserve">
|
||||||
|
<value>Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProbMethod" xml:space="preserve">
|
||||||
|
<value>ProbMethod</value>
|
||||||
|
</data>
|
||||||
|
<data name="SimilarityMethod" xml:space="preserve">
|
||||||
|
<value>SimilarityMethod</value>
|
||||||
|
</data>
|
||||||
|
<data name="Close" xml:space="preserve">
|
||||||
|
<value>Schließen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Query Details" xml:space="preserve">
|
||||||
|
<value>Suchanfrage Details</value>
|
||||||
|
</data>
|
||||||
|
<data name="Access times" xml:space="preserve">
|
||||||
|
<value>Zugriffszeiten</value>
|
||||||
|
</data>
|
||||||
|
<data name="Results" xml:space="preserve">
|
||||||
|
<value>Ergebnisse</value>
|
||||||
|
</data>
|
||||||
|
<data name="Score" xml:space="preserve">
|
||||||
|
<value>Bewertung</value>
|
||||||
|
</data>
|
||||||
|
<data name="Query Update" xml:space="preserve">
|
||||||
|
<value>Suchanfrage anpassen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Rename searchdomain" xml:space="preserve">
|
||||||
|
<value>Searchdomain umbenennen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete searchdomain" xml:space="preserve">
|
||||||
|
<value>Searchdomain löschen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Create searchdomain" xml:space="preserve">
|
||||||
|
<value>Searchdomain anlegen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain name" xml:space="preserve">
|
||||||
|
<value>Searchdomain Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Enable cache reconciliation" xml:space="preserve">
|
||||||
|
<value>Cache Abgleich verwenden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Create entity" xml:space="preserve">
|
||||||
|
<value>Entity erstellen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity name" xml:space="preserve">
|
||||||
|
<value>Entity Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Probmethod" xml:space="preserve">
|
||||||
|
<value>Probmethod</value>
|
||||||
|
</data>
|
||||||
|
<data name="Add attribute" xml:space="preserve">
|
||||||
|
<value>Attribut hinzufügen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Probmethod_embedding" xml:space="preserve">
|
||||||
|
<value>Probmethod_embedding</value>
|
||||||
|
</data>
|
||||||
|
<data name="Similarity method" xml:space="preserve">
|
||||||
|
<value>Similarity method</value>
|
||||||
|
</data>
|
||||||
|
<data name="Model" xml:space="preserve">
|
||||||
|
<value>Modell</value>
|
||||||
|
</data>
|
||||||
|
<data name="Add datapoint" xml:space="preserve">
|
||||||
|
<value>Datapoint hinzufügen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete entity" xml:space="preserve">
|
||||||
|
<value>Entity löschen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Update entity" xml:space="preserve">
|
||||||
|
<value>Entity anpassen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Action" xml:space="preserve">
|
||||||
|
<value>Aktion</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete query" xml:space="preserve">
|
||||||
|
<value>Suchanfrage löschen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Creating entity" xml:space="preserve">
|
||||||
|
<value>Erstelle Entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity was created successfully" xml:space="preserve">
|
||||||
|
<value>Entity wurde erfolgreich erstellt</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to create entity" xml:space="preserve">
|
||||||
|
<value>Entity konnte nicht erstellt werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain was created successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain wurde erfolgreich erstellt</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to create searchdomain" xml:space="preserve">
|
||||||
|
<value>Searchdomain konnte nicht erstellt werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain cache was cleared successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain Cache wurde erfolgreich geleert</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to clear searchdomain cache" xml:space="preserve">
|
||||||
|
<value>Searchdomain Cache konnte nicht geleert werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity was deleted successfully" xml:space="preserve">
|
||||||
|
<value>Entity wurde erfolgreich gelöscht</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to delete entity" xml:space="preserve">
|
||||||
|
<value>Entity konnte nicht gelöscht werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Updating entity" xml:space="preserve">
|
||||||
|
<value>Entity wird angepasst</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity was updated successfully" xml:space="preserve">
|
||||||
|
<value>Entity wurde erfolgreich angepasst</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to update entity" xml:space="preserve">
|
||||||
|
<value>Entity konnte nicht angepasst werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search query was deleted successfully" xml:space="preserve">
|
||||||
|
<value>Suchanfrage wurde erfolgreich gelöscht</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to delete search query" xml:space="preserve">
|
||||||
|
<value>Suchanfrage konnte nicht gelöscht werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain was created successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain wurde erfolgreich erstellt</value>
|
||||||
|
</data>
|
||||||
|
<data name="Updating search query failed" xml:space="preserve">
|
||||||
|
<value>Suchanfrage konnte nicht angepasst werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain was deleted successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain wurde erfolgreich gelöscht</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to delete searchdomain" xml:space="preserve">
|
||||||
|
<value>Konnte Searchdomain nicht löschen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain was renamed successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain wurde erfolgreich umbenannt</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to rename searchdomain" xml:space="preserve">
|
||||||
|
<value>Searchdomain konnte nicht umbenannt werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain settings were updated successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain Einstellungen wurden erfolgreich angepasst</value>
|
||||||
|
</data>
|
||||||
|
<data name="Updating searchdomain settings failed" xml:space="preserve">
|
||||||
|
<value>Searchdomain Einstellungen konnten nicht angepasst werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Unable to fetch searchdomain config" xml:space="preserve">
|
||||||
|
<value>Searchdomain Einstellungen konnten nicht abgerufen werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Unable to fetch searchdomain cache utilization" xml:space="preserve">
|
||||||
|
<value>Searchdomain Cache-Auslastung konnte nicht abgerufen werden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Details" xml:space="preserve">
|
||||||
|
<value>Details</value>
|
||||||
|
</data>
|
||||||
|
<data name="Remove attribute" xml:space="preserve">
|
||||||
|
<value>Attribut entfernen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Remove" xml:space="preserve">
|
||||||
|
<value>Entfernen</value>
|
||||||
|
</data>
|
||||||
|
<data name="Close alert" xml:space="preserve">
|
||||||
|
<value>Benachrichtigung schließen</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -24,4 +24,223 @@
|
|||||||
<data name="IrreversibleActionWarning" xml:space="preserve">
|
<data name="IrreversibleActionWarning" xml:space="preserve">
|
||||||
<value>This action cannot be undone.</value>
|
<value>This action cannot be undone.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Searchdomain selection" xml:space="preserve">
|
||||||
|
<value>Searchdomain selection</value>
|
||||||
|
</data>
|
||||||
|
<data name="Create" xml:space="preserve">
|
||||||
|
<value>Create</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain information and settings" xml:space="preserve">
|
||||||
|
<value>Searchdomain information and settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="Actions" xml:space="preserve">
|
||||||
|
<value>Actions</value>
|
||||||
|
</data>
|
||||||
|
<data name="Rename" xml:space="preserve">
|
||||||
|
<value>Rename</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete" xml:space="preserve">
|
||||||
|
<value>Delete</value>
|
||||||
|
</data>
|
||||||
|
<data name="Settings" xml:space="preserve">
|
||||||
|
<value>Settings</value>
|
||||||
|
</data>
|
||||||
|
<data name="Cache reconciliation" xml:space="preserve">
|
||||||
|
<value>Cache reconciliation</value>
|
||||||
|
</data>
|
||||||
|
<data name="Update" xml:space="preserve">
|
||||||
|
<value>Update</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search cache" xml:space="preserve">
|
||||||
|
<value>Search cache</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search cache utilization" xml:space="preserve">
|
||||||
|
<value>Search cache utilization</value>
|
||||||
|
</data>
|
||||||
|
<data name="Clear" xml:space="preserve">
|
||||||
|
<value>Clear</value>
|
||||||
|
</data>
|
||||||
|
<data name="Database size" xml:space="preserve">
|
||||||
|
<value>Database size</value>
|
||||||
|
</data>
|
||||||
|
<data name="Add new entity" xml:space="preserve">
|
||||||
|
<value>Add new entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity Details" xml:space="preserve">
|
||||||
|
<value>Entity Details</value>
|
||||||
|
</data>
|
||||||
|
<data name="Attributes" xml:space="preserve">
|
||||||
|
<value>Attributes</value>
|
||||||
|
</data>
|
||||||
|
<data name="Key" xml:space="preserve">
|
||||||
|
<value>Key</value>
|
||||||
|
</data>
|
||||||
|
<data name="Value" xml:space="preserve">
|
||||||
|
<value>Value</value>
|
||||||
|
</data>
|
||||||
|
<data name="Datapoints" xml:space="preserve">
|
||||||
|
<value>Datapoints</value>
|
||||||
|
</data>
|
||||||
|
<data name="Name" xml:space="preserve">
|
||||||
|
<value>Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="ProbMethod" xml:space="preserve">
|
||||||
|
<value>ProbMethod</value>
|
||||||
|
</data>
|
||||||
|
<data name="SimilarityMethod" xml:space="preserve">
|
||||||
|
<value>SimilarityMethod</value>
|
||||||
|
</data>
|
||||||
|
<data name="Close" xml:space="preserve">
|
||||||
|
<value>Close</value>
|
||||||
|
</data>
|
||||||
|
<data name="Query Details" xml:space="preserve">
|
||||||
|
<value>Query Details</value>
|
||||||
|
</data>
|
||||||
|
<data name="Access times" xml:space="preserve">
|
||||||
|
<value>Access times</value>
|
||||||
|
</data>
|
||||||
|
<data name="Results" xml:space="preserve">
|
||||||
|
<value>Results</value>
|
||||||
|
</data>
|
||||||
|
<data name="Score" xml:space="preserve">
|
||||||
|
<value>Score</value>
|
||||||
|
</data>
|
||||||
|
<data name="Query Update" xml:space="preserve">
|
||||||
|
<value>Query Update</value>
|
||||||
|
</data>
|
||||||
|
<data name="Rename searchdomain" xml:space="preserve">
|
||||||
|
<value>Rename searchdomain</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete searchdomain" xml:space="preserve">
|
||||||
|
<value>Delete searchdomain</value>
|
||||||
|
</data>
|
||||||
|
<data name="Create searchdomain" xml:space="preserve">
|
||||||
|
<value>Create searchdomain</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain name" xml:space="preserve">
|
||||||
|
<value>Searchdomain name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Enable cache reconciliation" xml:space="preserve">
|
||||||
|
<value>Enable cache reconciliation</value>
|
||||||
|
</data>
|
||||||
|
<data name="Create entity" xml:space="preserve">
|
||||||
|
<value>Create entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity name" xml:space="preserve">
|
||||||
|
<value>Entity name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Probmethod" xml:space="preserve">
|
||||||
|
<value>Probmethod</value>
|
||||||
|
</data>
|
||||||
|
<data name="Add attribute" xml:space="preserve">
|
||||||
|
<value>Add attribute</value>
|
||||||
|
</data>
|
||||||
|
<data name="Probmethod_embedding" xml:space="preserve">
|
||||||
|
<value>Probmethod_embedding</value>
|
||||||
|
</data>
|
||||||
|
<data name="Similarity method" xml:space="preserve">
|
||||||
|
<value>Similarity method</value>
|
||||||
|
</data>
|
||||||
|
<data name="Model" xml:space="preserve">
|
||||||
|
<value>Model</value>
|
||||||
|
</data>
|
||||||
|
<data name="Add datapoint" xml:space="preserve">
|
||||||
|
<value>Add datapoint</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete entity" xml:space="preserve">
|
||||||
|
<value>Delete entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Update entity" xml:space="preserve">
|
||||||
|
<value>Update entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Action" xml:space="preserve">
|
||||||
|
<value>Action</value>
|
||||||
|
</data>
|
||||||
|
<data name="Delete query" xml:space="preserve">
|
||||||
|
<value>Delete query</value>
|
||||||
|
</data>
|
||||||
|
<data name="Creating entity" xml:space="preserve">
|
||||||
|
<value>Creating entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity was created successfully" xml:space="preserve">
|
||||||
|
<value>Entity was created successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to create entity" xml:space="preserve">
|
||||||
|
<value>Failed to create entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain was created successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain was created successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to create searchdomain" xml:space="preserve">
|
||||||
|
<value>Failed to create searchdomain</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain cache was cleared successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain cache was cleared successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to clear searchdomain cache" xml:space="preserve">
|
||||||
|
<value>Failed to clear searchdomain cache</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity was deleted successfully" xml:space="preserve">
|
||||||
|
<value>Entity was deleted successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to delete entity" xml:space="preserve">
|
||||||
|
<value>Failed to delete entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Updating entity" xml:space="preserve">
|
||||||
|
<value>Updating entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Entity was updated successfully" xml:space="preserve">
|
||||||
|
<value>Entity was updated successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to update entity" xml:space="preserve">
|
||||||
|
<value>Failed to update entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search query was deleted successfully" xml:space="preserve">
|
||||||
|
<value>Search query was deleted successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to delete search query" xml:space="preserve">
|
||||||
|
<value>Failed to delete search query</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain was created successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain was created successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Updating search query failed" xml:space="preserve">
|
||||||
|
<value>Updating search query failed</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain was deleted successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain was deleted successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to delete searchdomain" xml:space="preserve">
|
||||||
|
<value>Failed to delete searchdomain</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain was renamed successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain was renamed successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Failed to rename searchdomain" xml:space="preserve">
|
||||||
|
<value>Failed to rename searchdomain</value>
|
||||||
|
</data>
|
||||||
|
<data name="Searchdomain settings were updated successfully" xml:space="preserve">
|
||||||
|
<value>Searchdomain settings were updated successfully</value>
|
||||||
|
</data>
|
||||||
|
<data name="Updating searchdomain settings failed" xml:space="preserve">
|
||||||
|
<value>Updating searchdomain settings failed</value>
|
||||||
|
</data>
|
||||||
|
<data name="Unable to fetch searchdomain config" xml:space="preserve">
|
||||||
|
<value>Unable to fetch searchdomain config</value>
|
||||||
|
</data>
|
||||||
|
<data name="Unable to fetch searchdomain cache utilization" xml:space="preserve">
|
||||||
|
<value>"Unable to fetch searchdomain cache utilization</value>
|
||||||
|
</data>
|
||||||
|
<data name="Details" xml:space="preserve">
|
||||||
|
<value>Details</value>
|
||||||
|
</data>
|
||||||
|
<data name="Remove attribute" xml:space="preserve">
|
||||||
|
<value>Remove attribute</value>
|
||||||
|
</data>
|
||||||
|
<data name="Remove" xml:space="preserve">
|
||||||
|
<value>Remove</value>
|
||||||
|
</data>
|
||||||
|
<data name="Close alert" xml:space="preserve">
|
||||||
|
<value>Close alert</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -5,6 +5,7 @@ using ElmahCore.Mvc.Logger;
|
|||||||
using MySql.Data.MySqlClient;
|
using MySql.Data.MySqlClient;
|
||||||
using Server.Helper;
|
using Server.Helper;
|
||||||
using Shared.Models;
|
using Shared.Models;
|
||||||
|
using AdaptiveExpressions;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
@@ -19,13 +20,12 @@ public class Searchdomain
|
|||||||
public Dictionary<string, DateTimedSearchResult> searchCache; // Key: query, Value: Search results for that query (with timestamp)
|
public Dictionary<string, DateTimedSearchResult> searchCache; // Key: query, Value: Search results for that query (with timestamp)
|
||||||
public List<Entity> entityCache;
|
public List<Entity> entityCache;
|
||||||
public List<string> modelsInUse;
|
public List<string> modelsInUse;
|
||||||
public Dictionary<string, Dictionary<string, float[]>> embeddingCache;
|
public LRUCache<string, Dictionary<string, float[]>> embeddingCache;
|
||||||
public int embeddingCacheMaxSize = 10000000;
|
|
||||||
private readonly MySqlConnection connection;
|
private readonly MySqlConnection connection;
|
||||||
public SQLHelper helper;
|
public SQLHelper helper;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public Searchdomain(string searchdomain, string connectionString, AIProvider aIProvider, Dictionary<string, Dictionary<string, float[]>> embeddingCache, ILogger logger, string provider = "sqlserver", bool runEmpty = false)
|
public Searchdomain(string searchdomain, string connectionString, AIProvider aIProvider, LRUCache<string, Dictionary<string, float[]>> embeddingCache, ILogger logger, string provider = "sqlserver", bool runEmpty = false)
|
||||||
{
|
{
|
||||||
_connectionString = connectionString;
|
_connectionString = connectionString;
|
||||||
_provider = provider.ToLower();
|
_provider = provider.ToLower();
|
||||||
@@ -96,8 +96,16 @@ public class Searchdomain
|
|||||||
string probmethodString = datapointReader.GetString(3);
|
string probmethodString = datapointReader.GetString(3);
|
||||||
string similarityMethodString = datapointReader.GetString(4);
|
string similarityMethodString = datapointReader.GetString(4);
|
||||||
string hash = datapointReader.GetString(5);
|
string hash = datapointReader.GetString(5);
|
||||||
ProbMethod probmethod = new(probmethodString, _logger);
|
ProbMethodEnum probmethodEnum = (ProbMethodEnum)Enum.Parse(
|
||||||
SimilarityMethod similarityMethod = new(similarityMethodString, _logger);
|
typeof(ProbMethodEnum),
|
||||||
|
probmethodString
|
||||||
|
);
|
||||||
|
SimilarityMethodEnum similairtyMethodEnum = (SimilarityMethodEnum)Enum.Parse(
|
||||||
|
typeof(SimilarityMethodEnum),
|
||||||
|
similarityMethodString
|
||||||
|
);
|
||||||
|
ProbMethod probmethod = new(probmethodEnum, _logger);
|
||||||
|
SimilarityMethod similarityMethod = new(similairtyMethodEnum, _logger);
|
||||||
if (embedding_unassigned.TryGetValue(id, out Dictionary<string, float[]>? embeddings) && probmethod is not null)
|
if (embedding_unassigned.TryGetValue(id, out Dictionary<string, float[]>? embeddings) && probmethod is not null)
|
||||||
{
|
{
|
||||||
embedding_unassigned.Remove(id);
|
embedding_unassigned.Remove(id);
|
||||||
@@ -151,7 +159,6 @@ public class Searchdomain
|
|||||||
}
|
}
|
||||||
entityReader.Close();
|
entityReader.Close();
|
||||||
modelsInUse = GetModels(entityCache);
|
modelsInUse = GetModels(entityCache);
|
||||||
embeddingCache = []; // TODO remove this and implement proper remediation to improve performance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<(float, string)> Search(string query, int? topN = null)
|
public List<(float, string)> Search(string query, int? topN = null)
|
||||||
@@ -162,33 +169,13 @@ public class Searchdomain
|
|||||||
return [.. cachedResult.Results.Select(r => (r.Score, r.Name))];
|
return [.. cachedResult.Results.Select(r => (r.Score, r.Name))];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!embeddingCache.TryGetValue(query, out Dictionary<string, float[]>? queryEmbeddings))
|
Dictionary<string, float[]> queryEmbeddings = GetQueryEmbeddings(query);
|
||||||
{
|
|
||||||
queryEmbeddings = Datapoint.GenerateEmbeddings(query, modelsInUse, aIProvider);
|
|
||||||
if (embeddingCache.Count < embeddingCacheMaxSize) // TODO add better way of managing cache limit hits
|
|
||||||
{ // Idea: Add access count to each entry. On limit hit, sort the entries by access count and remove the bottom 10% of entries
|
|
||||||
embeddingCache.Add(query, queryEmbeddings);
|
|
||||||
}
|
|
||||||
} // TODO implement proper cache remediation for embeddingCache here
|
|
||||||
|
|
||||||
List<(float, string)> result = [];
|
List<(float, string)> result = [];
|
||||||
|
|
||||||
foreach (Entity entity in entityCache)
|
foreach (Entity entity in entityCache)
|
||||||
{
|
{
|
||||||
List<(string, float)> datapointProbs = [];
|
result.Add((EvaluateEntityAgainstQueryEmbeddings(entity, queryEmbeddings), entity.name));
|
||||||
foreach (Datapoint datapoint in entity.datapoints)
|
|
||||||
{
|
|
||||||
SimilarityMethod similarityMethod = datapoint.similarityMethod;
|
|
||||||
List<(string, float)> list = [];
|
|
||||||
foreach ((string, float[]) embedding in datapoint.embeddings)
|
|
||||||
{
|
|
||||||
string key = embedding.Item1;
|
|
||||||
float value = similarityMethod.method(queryEmbeddings[embedding.Item1], embedding.Item2);
|
|
||||||
list.Add((key, value));
|
|
||||||
}
|
|
||||||
datapointProbs.Add((datapoint.name, datapoint.probMethod.method(list)));
|
|
||||||
}
|
|
||||||
result.Add((entity.probMethod(datapointProbs), entity.name));
|
|
||||||
}
|
}
|
||||||
IEnumerable<(float, string)> sortedResults = result.OrderByDescending(s => s.Item1);
|
IEnumerable<(float, string)> sortedResults = result.OrderByDescending(s => s.Item1);
|
||||||
if (topN is not null)
|
if (topN is not null)
|
||||||
@@ -204,6 +191,49 @@ public class Searchdomain
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, float[]> GetQueryEmbeddings(string query)
|
||||||
|
{
|
||||||
|
bool hasQuery = embeddingCache.TryGet(query, out Dictionary<string, float[]> queryEmbeddings);
|
||||||
|
bool allModelsInQuery = queryEmbeddings is not null && modelsInUse.All(model => queryEmbeddings.ContainsKey(model));
|
||||||
|
if (!(hasQuery && allModelsInQuery) || queryEmbeddings is null)
|
||||||
|
{
|
||||||
|
queryEmbeddings = Datapoint.GenerateEmbeddings(query, modelsInUse, aIProvider, embeddingCache);
|
||||||
|
if (!embeddingCache.TryGet(query, out var embeddingCacheForCurrentQuery))
|
||||||
|
{
|
||||||
|
embeddingCache.Set(query, queryEmbeddings);
|
||||||
|
}
|
||||||
|
else // embeddingCache already has an entry for this query, so the missing model-embedding pairs have to be filled in
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, float[]> kvp in queryEmbeddings) // kvp.Key = model, kvp.Value = embedding
|
||||||
|
{
|
||||||
|
if (!embeddingCache.TryGet(kvp.Key, out var _))
|
||||||
|
{
|
||||||
|
embeddingCacheForCurrentQuery[kvp.Key] = kvp.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return queryEmbeddings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float EvaluateEntityAgainstQueryEmbeddings(Entity entity, Dictionary<string, float[]> queryEmbeddings)
|
||||||
|
{
|
||||||
|
List<(string, float)> datapointProbs = [];
|
||||||
|
foreach (Datapoint datapoint in entity.datapoints)
|
||||||
|
{
|
||||||
|
SimilarityMethod similarityMethod = datapoint.similarityMethod;
|
||||||
|
List<(string, float)> list = [];
|
||||||
|
foreach ((string, float[]) embedding in datapoint.embeddings)
|
||||||
|
{
|
||||||
|
string key = embedding.Item1;
|
||||||
|
float value = similarityMethod.method(queryEmbeddings[embedding.Item1], embedding.Item2);
|
||||||
|
list.Add((key, value));
|
||||||
|
}
|
||||||
|
datapointProbs.Add((datapoint.name, datapoint.probMethod.method(list)));
|
||||||
|
}
|
||||||
|
return entity.probMethod(datapointProbs);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<string> GetModels(List<Entity> entities)
|
public static List<string> GetModels(List<Entity> entities)
|
||||||
{
|
{
|
||||||
List<string> result = [];
|
List<string> result = [];
|
||||||
@@ -250,6 +280,53 @@ public class Searchdomain
|
|||||||
return JsonSerializer.Deserialize<SearchdomainSettings>(settingsString);
|
return JsonSerializer.Deserialize<SearchdomainSettings>(settingsString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReconciliateOrInvalidateCacheForNewOrUpdatedEntity(Entity entity)
|
||||||
|
{
|
||||||
|
if (settings.CacheReconciliation)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, DateTimedSearchResult> element in searchCache)
|
||||||
|
{
|
||||||
|
string query = element.Key;
|
||||||
|
DateTimedSearchResult searchResult = element.Value;
|
||||||
|
|
||||||
|
Dictionary<string, float[]> queryEmbeddings = GetQueryEmbeddings(query);
|
||||||
|
float evaluationResult = EvaluateEntityAgainstQueryEmbeddings(entity, queryEmbeddings);
|
||||||
|
|
||||||
|
searchResult.Results.RemoveAll(x => x.Name == entity.name); // If entity already exists in that results list: remove it.
|
||||||
|
|
||||||
|
ResultItem newItem = new(evaluationResult, entity.name);
|
||||||
|
int index = searchResult.Results.BinarySearch(
|
||||||
|
newItem,
|
||||||
|
Comparer<ResultItem>.Create((a, b) => b.Score.CompareTo(a.Score)) // Invert searching order
|
||||||
|
);
|
||||||
|
if (index < 0) // If not found, BinarySearch gives the bitwise complement
|
||||||
|
index = ~index;
|
||||||
|
searchResult.Results.Insert(index, newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InvalidateSearchCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReconciliateOrInvalidateCacheForDeletedEntity(Entity entity)
|
||||||
|
{
|
||||||
|
if (settings.CacheReconciliation)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, DateTimedSearchResult> element in searchCache)
|
||||||
|
{
|
||||||
|
string query = element.Key;
|
||||||
|
DateTimedSearchResult searchResult = element.Value;
|
||||||
|
searchResult.Results.RemoveAll(x => x.Name == entity.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InvalidateSearchCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void InvalidateSearchCache()
|
public void InvalidateSearchCache()
|
||||||
{
|
{
|
||||||
searchCache = [];
|
searchCache = [];
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using System.Data.Common;
|
|||||||
using Server.Migrations;
|
using Server.Migrations;
|
||||||
using Server.Helper;
|
using Server.Helper;
|
||||||
using Server.Exceptions;
|
using Server.Exceptions;
|
||||||
|
using AdaptiveExpressions;
|
||||||
|
using Shared.Models;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
@@ -16,7 +19,8 @@ public class SearchdomainManager
|
|||||||
private readonly string connectionString;
|
private readonly string connectionString;
|
||||||
private MySqlConnection connection;
|
private MySqlConnection connection;
|
||||||
public SQLHelper helper;
|
public SQLHelper helper;
|
||||||
public Dictionary<string, Dictionary<string, float[]>> embeddingCache;
|
public LRUCache<string, Dictionary<string, float[]>> embeddingCache;
|
||||||
|
public int EmbeddingCacheMaxCount;
|
||||||
|
|
||||||
public SearchdomainManager(ILogger<SearchdomainManager> logger, IConfiguration config, AIProvider aIProvider, DatabaseHelper databaseHelper)
|
public SearchdomainManager(ILogger<SearchdomainManager> logger, IConfiguration config, AIProvider aIProvider, DatabaseHelper databaseHelper)
|
||||||
{
|
{
|
||||||
@@ -24,7 +28,8 @@ public class SearchdomainManager
|
|||||||
_config = config;
|
_config = config;
|
||||||
this.aIProvider = aIProvider;
|
this.aIProvider = aIProvider;
|
||||||
_databaseHelper = databaseHelper;
|
_databaseHelper = databaseHelper;
|
||||||
embeddingCache = [];
|
EmbeddingCacheMaxCount = config.GetValue<int?>("Embeddingsearch:EmbeddingCacheMaxCount") ?? 1000000;
|
||||||
|
embeddingCache = new(EmbeddingCacheMaxCount);
|
||||||
connectionString = _config.GetSection("Embeddingsearch").GetConnectionString("SQL") ?? "";
|
connectionString = _config.GetSection("Embeddingsearch").GetConnectionString("SQL") ?? "";
|
||||||
connection = new MySqlConnection(connectionString);
|
connection = new MySqlConnection(connectionString);
|
||||||
connection.Open();
|
connection.Open();
|
||||||
@@ -66,7 +71,7 @@ public class SearchdomainManager
|
|||||||
{
|
{
|
||||||
var searchdomain = GetSearchdomain(searchdomainName);
|
var searchdomain = GetSearchdomain(searchdomainName);
|
||||||
searchdomain.UpdateEntityCache();
|
searchdomain.UpdateEntityCache();
|
||||||
searchdomain.InvalidateSearchCache(); // TODO implement cache remediation (Suggestion: searchdomain-wide setting for cache remediation / invalidation - )
|
searchdomain.InvalidateSearchCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> ListSearchdomains()
|
public List<string> ListSearchdomains()
|
||||||
@@ -84,6 +89,10 @@ public class SearchdomainManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int CreateSearchdomain(string searchdomain, SearchdomainSettings settings)
|
||||||
|
{
|
||||||
|
return CreateSearchdomain(searchdomain, JsonSerializer.Serialize(settings));
|
||||||
|
}
|
||||||
public int CreateSearchdomain(string searchdomain, string settings = "{}")
|
public int CreateSearchdomain(string searchdomain, string settings = "{}")
|
||||||
{
|
{
|
||||||
if (searchdomains.TryGetValue(searchdomain, out Searchdomain? value))
|
if (searchdomains.TryGetValue(searchdomain, out Searchdomain? value))
|
||||||
|
|||||||
@@ -6,7 +6,13 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AdaptiveExpressions" Version="4.23.0" />
|
||||||
<PackageReference Include="ElmahCore" Version="2.1.2" />
|
<PackageReference Include="ElmahCore" Version="2.1.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
using System.Numerics.Tensors;
|
using System.Numerics.Tensors;
|
||||||
using System.Text.Json;
|
using Shared.Models;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
public class SimilarityMethod
|
public class SimilarityMethod
|
||||||
{
|
{
|
||||||
public SimilarityMethods.similarityMethodDelegate method;
|
public SimilarityMethods.similarityMethodDelegate method;
|
||||||
|
public SimilarityMethodEnum similarityMethodEnum;
|
||||||
public string name;
|
public string name;
|
||||||
|
|
||||||
public SimilarityMethod(string name, ILogger logger)
|
public SimilarityMethod(SimilarityMethodEnum similarityMethodEnum, ILogger logger)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.similarityMethodEnum = similarityMethodEnum;
|
||||||
|
this.name = similarityMethodEnum.ToString();
|
||||||
SimilarityMethods.similarityMethodDelegate? probMethod = SimilarityMethods.GetMethod(name);
|
SimilarityMethods.similarityMethodDelegate? probMethod = SimilarityMethods.GetMethod(name);
|
||||||
if (probMethod is null)
|
if (probMethod is null)
|
||||||
{
|
{
|
||||||
@@ -21,14 +23,6 @@ public class SimilarityMethod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SimilarityMethodEnum
|
|
||||||
{
|
|
||||||
Cosine,
|
|
||||||
Euclidian,
|
|
||||||
Manhattan,
|
|
||||||
Pearson
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SimilarityMethods
|
public static class SimilarityMethods
|
||||||
{
|
{
|
||||||
public delegate float similarityMethodProtoDelegate(float[] vector1, float[] vector2);
|
public delegate float similarityMethodProtoDelegate(float[] vector1, float[] vector2);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
|||||||
@{
|
|
||||||
ViewData["Title"] = "Privacy Policy";
|
|
||||||
}
|
|
||||||
<h1>@ViewData["Title"]</h1>
|
|
||||||
|
|
||||||
<p>Use this page to detail your site's privacy policy.</p>
|
|
||||||
1685
src/Server/Views/Home/Searchdomains.cshtml
Normal file
1685
src/Server/Views/Home/Searchdomains.cshtml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||||
@@ -54,7 +58,7 @@
|
|||||||
|
|
||||||
<footer class="border-top footer text-muted">
|
<footer class="border-top footer text-muted">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
© 2025 - embeddingsearch - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
© 2025 - embeddingsearch
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"172.17.0.1"
|
"172.17.0.1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"EmbeddingCacheMaxCount": 10000000,
|
||||||
"AiProviders": {
|
"AiProviders": {
|
||||||
"ollama": {
|
"ollama": {
|
||||||
"handler": "ollama",
|
"handler": "ollama",
|
||||||
@@ -35,6 +36,15 @@
|
|||||||
"ApiKey": "Some API key here"
|
"ApiKey": "Some API key here"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SimpleAuth": {
|
||||||
|
"Users": [
|
||||||
|
{
|
||||||
|
"Username": "admin",
|
||||||
|
"Password": "UnsafePractice.67",
|
||||||
|
"Roles": ["Admin"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"ApiKeys": ["Some UUID here", "Another UUID here"],
|
"ApiKeys": ["Some UUID here", "Another UUID here"],
|
||||||
"UseHttpsRedirection": true
|
"UseHttpsRedirection": true
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/Shared/Models/BaseModels.cs
Normal file
12
src/Shared/Models/BaseModels.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Shared.Models;
|
||||||
|
|
||||||
|
public class SuccesMessageBaseModel
|
||||||
|
{
|
||||||
|
[JsonPropertyName("Success")]
|
||||||
|
public required bool Success { get; set; }
|
||||||
|
[JsonPropertyName("Message")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Message { get; set; }
|
||||||
|
}
|
||||||
@@ -3,14 +3,10 @@ using System.Text.Json.Serialization;
|
|||||||
namespace Shared.Models;
|
namespace Shared.Models;
|
||||||
|
|
||||||
|
|
||||||
public class EntityQueryResults
|
public class EntityQueryResults : SuccesMessageBaseModel
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Results")]
|
[JsonPropertyName("Results")]
|
||||||
public required List<EntityQueryResult> Results { get; set; }
|
public required List<EntityQueryResult> Results { get; set; }
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EntityQueryResult
|
public class EntityQueryResult
|
||||||
@@ -19,20 +15,19 @@ public class EntityQueryResult
|
|||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
[JsonPropertyName("Value")]
|
[JsonPropertyName("Value")]
|
||||||
public float Value { get; set; }
|
public float Value { get; set; }
|
||||||
|
[JsonPropertyName("Attributes")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public Dictionary<string, string>? Attributes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EntityIndexResult
|
public class EntityIndexResult : SuccesMessageBaseModel {}
|
||||||
{
|
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EntityListResults
|
public class EntityListResults
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Results")]
|
[JsonPropertyName("Results")]
|
||||||
public required List<EntityListResult> Results { get; set; }
|
public required List<EntityListResult> Results { get; set; }
|
||||||
|
[JsonPropertyName("Message")]
|
||||||
|
public string? Message { get; set; }
|
||||||
[JsonPropertyName("Success")]
|
[JsonPropertyName("Success")]
|
||||||
public required bool Success { get; set; }
|
public required bool Success { get; set; }
|
||||||
}
|
}
|
||||||
@@ -77,11 +72,5 @@ public class EmbeddingResult
|
|||||||
public required float[] Embeddings { get; set; }
|
public required float[] Embeddings { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EntityDeleteResults
|
public class EntityDeleteResults : SuccesMessageBaseModel {}
|
||||||
{
|
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Shared.Models;
|
|||||||
public class JSONEntity
|
public class JSONEntity
|
||||||
{
|
{
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public required string Probmethod { get; set; }
|
public required ProbMethodEnum Probmethod { get; set; }
|
||||||
public required string Searchdomain { get; set; }
|
public required string Searchdomain { get; set; }
|
||||||
public required Dictionary<string, string> Attributes { get; set; }
|
public required Dictionary<string, string> Attributes { get; set; }
|
||||||
public required JSONDatapoint[] Datapoints { get; set; }
|
public required JSONDatapoint[] Datapoints { get; set; }
|
||||||
@@ -13,7 +13,27 @@ public class JSONDatapoint
|
|||||||
{
|
{
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public required string? Text { get; set; }
|
public required string? Text { get; set; }
|
||||||
public required string Probmethod_embedding { get; set; }
|
public required ProbMethodEnum Probmethod_embedding { get; set; }
|
||||||
public required string SimilarityMethod { get; set; }
|
public required SimilarityMethodEnum SimilarityMethod { get; set; }
|
||||||
public required string[] Model { get; set; }
|
public required string[] Model { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ProbMethodEnum
|
||||||
|
{
|
||||||
|
Mean,
|
||||||
|
HarmonicMean,
|
||||||
|
QuadraticMean,
|
||||||
|
GeometricMean,
|
||||||
|
EVEWAvg,
|
||||||
|
HVEWAvg,
|
||||||
|
LVEWAvg,
|
||||||
|
DictionaryWeightedAverage
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SimilarityMethodEnum
|
||||||
|
{
|
||||||
|
Cosine,
|
||||||
|
Euclidian,
|
||||||
|
Manhattan,
|
||||||
|
Pearson
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -11,109 +11,46 @@ public class SearchdomainListResults
|
|||||||
public string? Message { get; set; }
|
public string? Message { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SearchdomainCreateResults
|
public class SearchdomainCreateResults : SuccesMessageBaseModel
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Id")]
|
[JsonPropertyName("Id")]
|
||||||
public int? Id { get; set; }
|
public int? Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SearchdomainUpdateResults
|
public class SearchdomainUpdateResults : SuccesMessageBaseModel {}
|
||||||
|
|
||||||
|
public class SearchdomainDeleteResults : SuccesMessageBaseModel
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SearchdomainDeleteResults
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("DeletedEntities")]
|
[JsonPropertyName("DeletedEntities")]
|
||||||
public required int DeletedEntities { get; set; }
|
public required int DeletedEntities { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SearchdomainSearchesResults
|
public class SearchdomainSearchesResults : SuccesMessageBaseModel
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
[JsonPropertyName("Searches")]
|
[JsonPropertyName("Searches")]
|
||||||
public required Dictionary<string, DateTimedSearchResult> Searches { get; set; }
|
public required Dictionary<string, DateTimedSearchResult> Searches { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SearchdomainDeleteSearchResult
|
public class SearchdomainDeleteSearchResult : SuccesMessageBaseModel {}
|
||||||
|
|
||||||
|
public class SearchdomainUpdateSearchResult : SuccesMessageBaseModel {}
|
||||||
|
|
||||||
|
public class SearchdomainSettingsResults : SuccesMessageBaseModel
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SearchdomainUpdateSearchResult
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SearchdomainSettingsResults
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Settings")]
|
[JsonPropertyName("Settings")]
|
||||||
public required SearchdomainSettings? Settings { get; set; }
|
public required SearchdomainSettings? Settings { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SearchdomainSearchCacheSizeResults
|
public class SearchdomainSearchCacheSizeResults : SuccesMessageBaseModel
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Success")]
|
[JsonPropertyName("QueryCacheSizeBytes")]
|
||||||
public required bool Success { get; set; }
|
public required long? QueryCacheSizeBytes { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("SearchCacheSizeBytes")]
|
|
||||||
public required long? SearchCacheSizeBytes { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SearchdomainInvalidateCacheResults
|
public class SearchdomainInvalidateCacheResults : SuccesMessageBaseModel {}
|
||||||
|
|
||||||
|
public class SearchdomainGetDatabaseSizeResult : SuccesMessageBaseModel
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SearchdomainGetDatabaseSizeResult
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("SearchdomainDatabaseSizeBytes")]
|
[JsonPropertyName("SearchdomainDatabaseSizeBytes")]
|
||||||
public required long? SearchdomainDatabaseSizeBytes { get; set; }
|
public required long? SearchdomainDatabaseSizeBytes { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,20 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace Shared.Models;
|
namespace Shared.Models;
|
||||||
|
|
||||||
public class ServerGetModelsResult
|
public class ServerGetModelsResult : SuccesMessageBaseModel
|
||||||
{
|
{
|
||||||
[JsonPropertyName("Success")]
|
|
||||||
public required bool Success { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Message")]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
[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; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user