diff --git a/src/Client/Client.cs b/src/Client/Client.cs index ec0ea18..0b9e26c 100644 --- a/src/Client/Client.cs +++ b/src/Client/Client.cs @@ -108,7 +108,7 @@ public class Client public async Task EntityIndexAsync(string jsonEntity) { var content = new StringContent(jsonEntity, Encoding.UTF8, "application/json"); - return await PutUrlAndProcessJson(GetUrl($"{baseUri}", "Entity", apiKey, []), content); + return await PutUrlAndProcessJson(GetUrl($"{baseUri}", "Entities", apiKey, []), content); } public async Task EntityListAsync(bool returnEmbeddings = false) diff --git a/src/Server/Controllers/EntityController.cs b/src/Server/Controllers/EntityController.cs index 0637f08..b1a9406 100644 --- a/src/Server/Controllers/EntityController.cs +++ b/src/Server/Controllers/EntityController.cs @@ -24,44 +24,17 @@ public class EntityController : ControllerBase _databaseHelper = databaseHelper; } - [HttpPut] - public ActionResult Index([FromBody] List? jsonEntities) - { - try - { - List? entities = _searchdomainHelper.EntitiesFromJSON( - _domainManager, - _logger, - JsonSerializer.Serialize(jsonEntities)); - if (entities is not null && jsonEntities is not null) - { - List 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) - { - 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 }); - } - - } - + /// + /// List the entities in a searchdomain + /// + /// + /// With returnModels = false expect: "Datapoints": [..., "Embeddings": null]
+ /// With returnModels = true expect: "Datapoints": [..., "Embeddings": [{"Model": "...", "Embeddings": []}, ...]]
+ /// With returnEmbeddings = true expect: "Datapoints": [..., "Embeddings": [{"Model": "...", "Embeddings": [0.007384672,0.01309805,0.0012528514,...]}, ...]] + ///
+ /// Name of the searchdomain + /// Include the models in the response + /// Include the embeddings in the response (requires returnModels) [HttpGet("/Entities")] public ActionResult List(string searchdomain, bool returnModels = false, bool returnEmbeddings = false) { @@ -109,6 +82,56 @@ public class EntityController : ControllerBase return Ok(entityListResults); } + /// + /// Index entities + /// + /// + /// Behavior: Creates new entities, but overwrites existing entities that have the same name + /// + /// Entities to index + [HttpPut("/Entities")] + public ActionResult Index([FromBody] List? jsonEntities) + { + try + { + List? entities = _searchdomainHelper.EntitiesFromJSON( + _domainManager, + _logger, + JsonSerializer.Serialize(jsonEntities)); + if (entities is not null && jsonEntities is not null) + { + List 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) + { + 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 }); + } + + } + + /// + /// Deletes an entity + /// + /// Name of the searchdomain + /// Name of the entity [HttpDelete] public ActionResult Delete(string searchdomain, string entityName) { diff --git a/src/Server/Controllers/SearchdomainController.cs b/src/Server/Controllers/SearchdomainController.cs index a300450..a5e3992 100644 --- a/src/Server/Controllers/SearchdomainController.cs +++ b/src/Server/Controllers/SearchdomainController.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using System.Text.Json; using ElmahCore; using Microsoft.AspNetCore.Http.HttpResults; @@ -23,6 +24,9 @@ public class SearchdomainController : ControllerBase _domainManager = domainManager; } + /// + /// Lists all searchdomains + /// [HttpGet("/Searchdomains")] public ActionResult List() { @@ -40,8 +44,13 @@ public class SearchdomainController : ControllerBase return Ok(searchdomainListResults); } + /// + /// Creates a new searchdomain + /// + /// Name of the searchdomain + /// Optional initial settings [HttpPost] - public ActionResult Create(string searchdomain, [FromBody]SearchdomainSettings settings = new()) + public ActionResult Create([Required]string searchdomain, [FromBody]SearchdomainSettings settings = new()) { try { @@ -54,8 +63,12 @@ public class SearchdomainController : ControllerBase } } + /// + /// Deletes a searchdomain + /// + /// Name of the searchdomain [HttpDelete] - public ActionResult Delete(string searchdomain) + public ActionResult Delete([Required]string searchdomain) { bool success; int deletedEntries; @@ -84,8 +97,14 @@ public class SearchdomainController : ControllerBase return Ok(new SearchdomainDeleteResults(){Success = success, DeletedEntities = deletedEntries, Message = message}); } + /// + /// Updates name and settings of a searchdomain + /// + /// Name of the searchdomain + /// Updated name of the searchdomain + /// Updated settings of searchdomain [HttpPut] - public ActionResult Update(string searchdomain, string newName, [FromBody]string? settings = "{}") + public ActionResult Update([Required]string searchdomain, string newName, [FromBody]SearchdomainSettings? settings) { (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}); @@ -110,8 +129,29 @@ public class SearchdomainController : ControllerBase return Ok(new SearchdomainUpdateResults(){Success = true}); } + /// + /// Gets the query cache of a searchdomain + /// + /// Name of the searchdomain + [HttpGet("Queries")] + public ActionResult GetQueries([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 searchCache = searchdomain_.searchCache; + + return Ok(new SearchdomainSearchesResults() { Searches = searchCache, Success = true }); + } + + /// + /// Executes a query in the searchdomain + /// + /// Name of the searchdomain + /// Query to execute + /// Return only the top N results + /// Return the attributes of the object [HttpPost("Query")] - public ActionResult Query(string searchdomain, string query, int? topN, bool returnAttributes = false) + public ActionResult Query([Required]string searchdomain, [Required]string query, int? topN, bool returnAttributes = false) { (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}); @@ -125,33 +165,13 @@ public class SearchdomainController : ControllerBase return Ok(new EntityQueryResults(){Results = queryResults, Success = true }); } - [HttpPut("Settings")] - public ActionResult UpdateSettings(string searchdomain, [FromBody] SearchdomainSettings request) - { - (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 parameters = new() - { - {"settings", JsonSerializer.Serialize(request)}, - {"id", searchdomain_.id} - }; - searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set settings = @settings WHERE id = @id", parameters); - searchdomain_.settings = request; - return Ok(new SearchdomainUpdateResults(){Success = true}); - } - - [HttpGet("Queries")] - public ActionResult GetQueries(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 searchCache = searchdomain_.searchCache; - - return Ok(new SearchdomainSearchesResults() { Searches = searchCache, Success = true }); - } - + /// + /// Deletes a query from the query cache + /// + /// Name of the searchdomain + /// Query to delete [HttpDelete("Query")] - public ActionResult DeleteQuery(string searchdomain, string query) + public ActionResult 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}); @@ -165,8 +185,14 @@ public class SearchdomainController : ControllerBase return Ok(new SearchdomainDeleteSearchResult() {Success = false, Message = "Query not found in search cache"}); } + /// + /// Updates a query from the query cache + /// + /// Name of the searchdomain + /// Query to update + /// List of results to apply to the query [HttpPatch("Query")] - public ActionResult UpdateQuery(string searchdomain, string query, [FromBody]List results) + public ActionResult UpdateQuery([Required]string searchdomain, [Required]string query, [Required][FromBody]List results) { (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}); @@ -182,8 +208,12 @@ public class SearchdomainController : ControllerBase return Ok(new SearchdomainUpdateSearchResult() {Success = false, Message = "Query not found in search cache"}); } + /// + /// Get the settings of a searchdomain + /// + /// Name of the searchdomain [HttpGet("Settings")] - public ActionResult GetSettings(string searchdomain) + public ActionResult GetSettings([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}); @@ -191,8 +221,31 @@ public class SearchdomainController : ControllerBase return Ok(new SearchdomainSettingsResults() { Settings = settings, Success = true }); } + /// + /// Update the settings of a searchdomain + /// + /// Name of the searchdomain + [HttpPut("Settings")] + public ActionResult UpdateSettings([Required]string searchdomain, [Required][FromBody] SearchdomainSettings request) + { + (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 parameters = new() + { + {"settings", JsonSerializer.Serialize(request)}, + {"id", searchdomain_.id} + }; + searchdomain_.helper.ExecuteSQLNonQuery("UPDATE searchdomain set settings = @settings WHERE id = @id", parameters); + searchdomain_.settings = request; + return Ok(new SearchdomainUpdateResults(){Success = true}); + } + + /// + /// Get the query cache size of a searchdomain + /// + /// Name of the searchdomain [HttpGet("SearchCache/Size")] - public ActionResult GetSearchCacheSize(string searchdomain) + public ActionResult 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}); @@ -207,8 +260,12 @@ public class SearchdomainController : ControllerBase return Ok(new SearchdomainSearchCacheSizeResults() { SearchCacheSizeBytes = sizeInBytes, Success = true }); } + /// + /// Clear the query cache of a searchdomain + /// + /// Name of the searchdomain [HttpPost("SearchCache/Clear")] - public ActionResult InvalidateSearchCache(string searchdomain) + public ActionResult InvalidateSearchCache([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}); @@ -216,8 +273,12 @@ public class SearchdomainController : ControllerBase return Ok(new SearchdomainInvalidateCacheResults(){Success = true}); } + /// + /// Get the disk size of a searchdomain in bytes + /// + /// Name of the searchdomain [HttpGet("Database/Size")] - public ActionResult GetDatabaseSize(string searchdomain) + public ActionResult GetDatabaseSize([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}); diff --git a/src/Server/Controllers/ServerController.cs b/src/Server/Controllers/ServerController.cs index 238a3c6..d880720 100644 --- a/src/Server/Controllers/ServerController.cs +++ b/src/Server/Controllers/ServerController.cs @@ -22,6 +22,12 @@ public class ServerController : ControllerBase _aIProvider = aIProvider; } + /// + /// Lists the models available to the server + /// + /// + /// Returns ALL models available to the server - not only the embedding models. + /// [HttpGet("Models")] public ActionResult GetModels() { diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 77f701d..1d5c7c4 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -9,6 +9,7 @@ using Server.Helper; using Server.Models; using Server.Services; using System.Text.Json.Serialization; +using System.Reflection; var builder = WebApplication.CreateBuilder(args); @@ -37,7 +38,12 @@ builder.Services.AddScoped(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 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() .ReadFrom.Configuration(builder.Configuration) .CreateLogger(); diff --git a/src/Server/Server.csproj b/src/Server/Server.csproj index 6a38352..ebaac58 100644 --- a/src/Server/Server.csproj +++ b/src/Server/Server.csproj @@ -6,6 +6,11 @@ enable + + true + $(NoWarn);1591 + + diff --git a/src/Server/Views/Home/Index.cshtml b/src/Server/Views/Home/Index.cshtml index e2b5f2e..00e41c2 100644 --- a/src/Server/Views/Home/Index.cshtml +++ b/src/Server/Views/Home/Index.cshtml @@ -745,7 +745,7 @@ "datapoints": datapoints }]; showToast("@T["Creating entity"]", "primary"); - fetch(`/Entity`, { + fetch(`/Entities`, { method: 'PUT', headers: { 'Content-Type': 'application/json' @@ -874,7 +874,7 @@ "datapoints": datapoints }]; showToast("@T["Updating entity"]", "primary"); - fetch(`/Entity`, { + fetch(`/Entities`, { method: 'PUT', headers: { 'Content-Type': 'application/json'