diff --git a/src/Primitives/CrestApps.Core.AI/Indexing/AIDocumentSearchIndexProfileHandler.cs b/src/Primitives/CrestApps.Core.AI/Indexing/AIDocumentSearchIndexProfileHandler.cs index fe154aa4..b1bb7639 100644 --- a/src/Primitives/CrestApps.Core.AI/Indexing/AIDocumentSearchIndexProfileHandler.cs +++ b/src/Primitives/CrestApps.Core.AI/Indexing/AIDocumentSearchIndexProfileHandler.cs @@ -10,7 +10,11 @@ namespace CrestApps.Core.AI.Indexing; public sealed class AIDocumentSearchIndexProfileHandler : EmbeddingSearchIndexProfileHandlerBase { - public AIDocumentSearchIndexProfileHandler(ICatalog deploymentCatalog, IAIClientFactory aiClientFactory, ILogger logger) : base(IndexProfileTypes.AIDocuments, deploymentCatalog, aiClientFactory, logger) + public AIDocumentSearchIndexProfileHandler( + ICatalog deploymentCatalog, + IAIClientFactory aiClientFactory, + ILogger logger) + : base(IndexProfileTypes.AIDocuments, deploymentCatalog, aiClientFactory, logger) { } @@ -58,4 +62,4 @@ protected override IReadOnlyCollection BuildFields(int vectorD VectorDimensions = vectorDimensions, }, ]; } -} \ No newline at end of file +} diff --git a/src/Primitives/CrestApps.Core.AI/Indexing/AIMemorySearchIndexProfileHandler.cs b/src/Primitives/CrestApps.Core.AI/Indexing/AIMemorySearchIndexProfileHandler.cs index e75582de..0ebea57c 100644 --- a/src/Primitives/CrestApps.Core.AI/Indexing/AIMemorySearchIndexProfileHandler.cs +++ b/src/Primitives/CrestApps.Core.AI/Indexing/AIMemorySearchIndexProfileHandler.cs @@ -16,7 +16,11 @@ public sealed class AIMemorySearchIndexProfileHandler : EmbeddingSearchIndexProf private const string _contentFieldName = "content"; private const string _updatedUtcFieldName = "updatedUtc"; private const string _embeddingFieldName = "embedding"; - public AIMemorySearchIndexProfileHandler(ICatalog deploymentCatalog, IAIClientFactory aiClientFactory, ILogger logger) : base(IndexProfileTypes.AIMemory, deploymentCatalog, aiClientFactory, logger) + public AIMemorySearchIndexProfileHandler( + ICatalog deploymentCatalog, + IAIClientFactory aiClientFactory, + ILogger logger) + : base(IndexProfileTypes.AIMemory, deploymentCatalog, aiClientFactory, logger) { } @@ -61,4 +65,4 @@ protected override IReadOnlyCollection BuildFields(int vectorD VectorDimensions = vectorDimensions, }, ]; } -} \ No newline at end of file +} diff --git a/src/Primitives/CrestApps.Core.AI/Indexing/DataSourceSearchIndexProfileHandler.cs b/src/Primitives/CrestApps.Core.AI/Indexing/DataSourceSearchIndexProfileHandler.cs index 17dba089..7eabe934 100644 --- a/src/Primitives/CrestApps.Core.AI/Indexing/DataSourceSearchIndexProfileHandler.cs +++ b/src/Primitives/CrestApps.Core.AI/Indexing/DataSourceSearchIndexProfileHandler.cs @@ -10,7 +10,11 @@ namespace CrestApps.Core.AI.Indexing; public sealed class DataSourceSearchIndexProfileHandler : EmbeddingSearchIndexProfileHandlerBase { - public DataSourceSearchIndexProfileHandler(ICatalog deploymentCatalog, IAIClientFactory aiClientFactory, ILogger logger) : base(IndexProfileTypes.DataSource, deploymentCatalog, aiClientFactory, logger) + public DataSourceSearchIndexProfileHandler( + ICatalog deploymentCatalog, + IAIClientFactory aiClientFactory, + ILogger logger) + : base(IndexProfileTypes.DataSource, deploymentCatalog, aiClientFactory, logger) { } @@ -63,4 +67,4 @@ protected override IReadOnlyCollection BuildFields(int vectorD VectorDimensions = vectorDimensions, }, ]; } -} \ No newline at end of file +} diff --git a/src/Primitives/CrestApps.Core.AI/Indexing/EmbeddingSearchIndexProfileHandlerBase.cs b/src/Primitives/CrestApps.Core.AI/Indexing/EmbeddingSearchIndexProfileHandlerBase.cs index 7b4e7dad..8c0094f9 100644 --- a/src/Primitives/CrestApps.Core.AI/Indexing/EmbeddingSearchIndexProfileHandlerBase.cs +++ b/src/Primitives/CrestApps.Core.AI/Indexing/EmbeddingSearchIndexProfileHandlerBase.cs @@ -15,7 +15,11 @@ public abstract class EmbeddingSearchIndexProfileHandlerBase : IndexProfileHandl private readonly ICatalog _deploymentCatalog; private readonly IAIClientFactory _aiClientFactory; private readonly ILogger _logger; - protected EmbeddingSearchIndexProfileHandlerBase(string type, ICatalog deploymentCatalog, IAIClientFactory aiClientFactory, ILogger logger) + protected EmbeddingSearchIndexProfileHandlerBase( + string type, + ICatalog deploymentCatalog, + IAIClientFactory aiClientFactory, + ILogger logger) { _type = type; _deploymentCatalog = deploymentCatalog; @@ -99,4 +103,4 @@ private async Task GetEmbeddingDimensionsAsync(SearchIndexProfile indexProf throw new InvalidOperationException("The selected embedding deployment did not return a valid embedding vector."); } -} \ No newline at end of file +} diff --git a/src/Primitives/CrestApps.Core.AI/Services/AIDeploymentManagerBase.cs b/src/Primitives/CrestApps.Core.AI/Services/AIDeploymentManagerBase.cs new file mode 100644 index 00000000..53f0b317 --- /dev/null +++ b/src/Primitives/CrestApps.Core.AI/Services/AIDeploymentManagerBase.cs @@ -0,0 +1,139 @@ +using CrestApps.Core.AI.Deployments; +using CrestApps.Core.AI.Models; +using CrestApps.Core.Services; +using Microsoft.Extensions.Logging; + +namespace CrestApps.Core.AI.Services; + +public abstract class AIDeploymentManagerBase : NamedSourceCatalogManager, IAIDeploymentManager +{ + public AIDeploymentManagerBase( + INamedSourceCatalog deploymentStore, + IEnumerable> handlers, + ILogger logger) + : base(deploymentStore, handlers, logger) + { + } + + public async ValueTask> GetAllAsync(string clientName, string connectionName) + { + var deployments = (await Catalog.GetAllAsync()) + .Where(x => string.Equals(x.ClientName, clientName, StringComparison.OrdinalIgnoreCase) && + string.Equals(x.ConnectionName ?? string.Empty, connectionName, StringComparison.OrdinalIgnoreCase)); + + foreach (var deployment in deployments) + { + await LoadAsync(deployment); + } + + return deployments; + } + + public async ValueTask> GetByTypeAsync(AIDeploymentType type) + { + var deployments = (await Catalog.GetAllAsync()) + .Where(x => x.SupportsType(type)); + + foreach (var deployment in deployments) + { + await LoadAsync(deployment); + } + + return deployments; + } + + public async ValueTask GetDefaultAsync(string clientName, string connectionName, AIDeploymentType type) + { + var deployments = await GetAllAsync(clientName, connectionName); + + var candidates = deployments.Where(d => d.SupportsType(type)); + + return candidates.FirstOrDefault(); + } + + public ValueTask ResolveOrDefaultAsync(AIDeploymentType type, string deploymentName = null, string clientName = null, string connectionName = null) + { + return ResolveByTypeAsync(type, deploymentName, clientName, connectionName); + } + + public async ValueTask> GetAllByTypeAsync(AIDeploymentType type, string clientName = null) + { + var allDeployments = await GetAllAsync(); + + var filtered = allDeployments.Where(d => d.SupportsType(type)); + + if (!string.IsNullOrEmpty(clientName)) + { + filtered = filtered.Where(d => string.Equals(d.ClientName, clientName, StringComparison.OrdinalIgnoreCase)); + } + + return filtered; + } + + private async ValueTask ResolveByTypeAsync(AIDeploymentType type, string deploymentName, string clientName, string connectionName) + { + if (!string.IsNullOrEmpty(deploymentName)) + { + var deployment = await FindBySelectorAsync(deploymentName); + + if (deployment != null) + { + return deployment; + } + } + + var globalDefaultId = await GetGlobalDefaultSelectorAsync(type); + + if (!string.IsNullOrEmpty(globalDefaultId)) + { + var deployment = await FindBySelectorAsync(globalDefaultId); + + if (deployment != null) + { + return deployment; + } + } + + return await GetFirstMatchingDeploymentAsync(type, clientName, connectionName); + } + + private async ValueTask GetFirstMatchingDeploymentAsync(AIDeploymentType type, string clientName, string connectionName) + { + var deployments = await GetAllAsync(); + + return deployments.FirstOrDefault(deployment => + { + if (!deployment.SupportsType(type)) + { + return false; + } + + if (!string.IsNullOrEmpty(clientName) && + !string.Equals(deployment.ClientName, clientName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (string.IsNullOrEmpty(connectionName)) + { + return true; + } + + return string.Equals(deployment.ConnectionName ?? string.Empty, connectionName, StringComparison.OrdinalIgnoreCase); + }); + } + + private async ValueTask FindBySelectorAsync(string selector) + { + var deployment = await FindByIdAsync(selector); + + if (deployment != null) + { + return deployment; + } + + return await FindByNameAsync(selector); + } + + protected abstract ValueTask GetGlobalDefaultSelectorAsync(AIDeploymentType type); +} diff --git a/src/Primitives/CrestApps.Core.AI/Services/DefaultAIDeploymentManager.cs b/src/Primitives/CrestApps.Core.AI/Services/DefaultAIDeploymentManager.cs index 8cb94690..7c90e8c8 100644 --- a/src/Primitives/CrestApps.Core.AI/Services/DefaultAIDeploymentManager.cs +++ b/src/Primitives/CrestApps.Core.AI/Services/DefaultAIDeploymentManager.cs @@ -1,4 +1,3 @@ -using CrestApps.Core.AI.Deployments; using CrestApps.Core.AI.Models; using CrestApps.Core.Services; using Microsoft.Extensions.Logging; @@ -6,7 +5,7 @@ namespace CrestApps.Core.AI.Services; -public class DefaultAIDeploymentManager : NamedSourceCatalogManager, IAIDeploymentManager +public class DefaultAIDeploymentManager : AIDeploymentManagerBase { private readonly IOptionsMonitor _deploymentSettings; @@ -15,132 +14,12 @@ public DefaultAIDeploymentManager( IEnumerable> handlers, IOptionsMonitor deploymentSettings, ILogger logger) - : base(deploymentStore, handlers, logger) + : base(deploymentStore, handlers, logger) { _deploymentSettings = deploymentSettings; } - public async ValueTask> GetAllAsync(string clientName, string connectionName) - { - var deployments = (await Catalog.GetAllAsync()) - .Where(x => string.Equals(x.ClientName, clientName, StringComparison.OrdinalIgnoreCase) && - string.Equals(x.ConnectionName ?? string.Empty, connectionName, StringComparison.OrdinalIgnoreCase)); - - foreach (var deployment in deployments) - { - await LoadAsync(deployment); - } - - return deployments; - } - - public async ValueTask> GetByTypeAsync(AIDeploymentType type) - { - var deployments = (await Catalog.GetAllAsync()) - .Where(x => x.SupportsType(type)); - - foreach (var deployment in deployments) - { - await LoadAsync(deployment); - } - - return deployments; - } - - public async ValueTask GetDefaultAsync(string clientName, string connectionName, AIDeploymentType type) - { - var deployments = await GetAllAsync(clientName, connectionName); - - var candidates = deployments.Where(d => d.SupportsType(type)); - - return candidates.FirstOrDefault(); - } - - public ValueTask ResolveOrDefaultAsync(AIDeploymentType type, string deploymentName = null, string clientName = null, string connectionName = null) - { - return ResolveByTypeAsync(type, deploymentName, clientName, connectionName); - } - - public async ValueTask> GetAllByTypeAsync(AIDeploymentType type, string clientName = null) - { - var allDeployments = await GetAllAsync(); - - var filtered = allDeployments.Where(d => d.SupportsType(type)); - - if (!string.IsNullOrEmpty(clientName)) - { - filtered = filtered.Where(d => string.Equals(d.ClientName, clientName, StringComparison.OrdinalIgnoreCase)); - } - - return filtered; - } - - private async ValueTask ResolveByTypeAsync(AIDeploymentType type, string deploymentName, string clientName, string connectionName) - { - if (!string.IsNullOrEmpty(deploymentName)) - { - var deployment = await FindBySelectorAsync(deploymentName); - - if (deployment != null) - { - return deployment; - } - } - - var globalDefaultId = await GetGlobalDefaultSelectorAsync(type); - - if (!string.IsNullOrEmpty(globalDefaultId)) - { - var deployment = await FindBySelectorAsync(globalDefaultId); - - if (deployment != null) - { - return deployment; - } - } - - return await GetFirstMatchingDeploymentAsync(type, clientName, connectionName); - } - - private async ValueTask GetFirstMatchingDeploymentAsync(AIDeploymentType type, string clientName, string connectionName) - { - var deployments = await GetAllAsync(); - - return deployments.FirstOrDefault(deployment => - { - if (!deployment.SupportsType(type)) - { - return false; - } - - if (!string.IsNullOrEmpty(clientName) && - !string.Equals(deployment.ClientName, clientName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - if (string.IsNullOrEmpty(connectionName)) - { - return true; - } - - return string.Equals(deployment.ConnectionName ?? string.Empty, connectionName, StringComparison.OrdinalIgnoreCase); - }); - } - - private async ValueTask FindBySelectorAsync(string selector) - { - var deployment = await FindByIdAsync(selector); - - if (deployment != null) - { - return deployment; - } - - return await FindByNameAsync(selector); - } - - protected virtual ValueTask GetGlobalDefaultSelectorAsync(AIDeploymentType type) + protected override ValueTask GetGlobalDefaultSelectorAsync(AIDeploymentType type) { var settings = _deploymentSettings.CurrentValue;