Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ namespace CrestApps.Core.AI.Indexing;

public sealed class AIDocumentSearchIndexProfileHandler : EmbeddingSearchIndexProfileHandlerBase
{
public AIDocumentSearchIndexProfileHandler(ICatalog<AIDeployment> deploymentCatalog, IAIClientFactory aiClientFactory, ILogger<AIDocumentSearchIndexProfileHandler> logger) : base(IndexProfileTypes.AIDocuments, deploymentCatalog, aiClientFactory, logger)
public AIDocumentSearchIndexProfileHandler(
ICatalog<AIDeployment> deploymentCatalog,
IAIClientFactory aiClientFactory,
ILogger<AIDocumentSearchIndexProfileHandler> logger)
: base(IndexProfileTypes.AIDocuments, deploymentCatalog, aiClientFactory, logger)
{
}

Expand Down Expand Up @@ -58,4 +62,4 @@ protected override IReadOnlyCollection<SearchIndexField> BuildFields(int vectorD
VectorDimensions = vectorDimensions,
}, ];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<AIDeployment> deploymentCatalog, IAIClientFactory aiClientFactory, ILogger<AIMemorySearchIndexProfileHandler> logger) : base(IndexProfileTypes.AIMemory, deploymentCatalog, aiClientFactory, logger)
public AIMemorySearchIndexProfileHandler(
ICatalog<AIDeployment> deploymentCatalog,
IAIClientFactory aiClientFactory,
ILogger<AIMemorySearchIndexProfileHandler> logger)
: base(IndexProfileTypes.AIMemory, deploymentCatalog, aiClientFactory, logger)
{
}

Expand Down Expand Up @@ -61,4 +65,4 @@ protected override IReadOnlyCollection<SearchIndexField> BuildFields(int vectorD
VectorDimensions = vectorDimensions,
}, ];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ namespace CrestApps.Core.AI.Indexing;

public sealed class DataSourceSearchIndexProfileHandler : EmbeddingSearchIndexProfileHandlerBase
{
public DataSourceSearchIndexProfileHandler(ICatalog<AIDeployment> deploymentCatalog, IAIClientFactory aiClientFactory, ILogger<DataSourceSearchIndexProfileHandler> logger) : base(IndexProfileTypes.DataSource, deploymentCatalog, aiClientFactory, logger)
public DataSourceSearchIndexProfileHandler(
ICatalog<AIDeployment> deploymentCatalog,
IAIClientFactory aiClientFactory,
ILogger<DataSourceSearchIndexProfileHandler> logger)
: base(IndexProfileTypes.DataSource, deploymentCatalog, aiClientFactory, logger)
{
}

Expand Down Expand Up @@ -63,4 +67,4 @@ protected override IReadOnlyCollection<SearchIndexField> BuildFields(int vectorD
VectorDimensions = vectorDimensions,
}, ];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ public abstract class EmbeddingSearchIndexProfileHandlerBase : IndexProfileHandl
private readonly ICatalog<AIDeployment> _deploymentCatalog;
private readonly IAIClientFactory _aiClientFactory;
private readonly ILogger _logger;
protected EmbeddingSearchIndexProfileHandlerBase(string type, ICatalog<AIDeployment> deploymentCatalog, IAIClientFactory aiClientFactory, ILogger logger)
protected EmbeddingSearchIndexProfileHandlerBase(
string type,
ICatalog<AIDeployment> deploymentCatalog,
IAIClientFactory aiClientFactory,
ILogger logger)
{
_type = type;
_deploymentCatalog = deploymentCatalog;
Expand Down Expand Up @@ -99,4 +103,4 @@ private async Task<int> GetEmbeddingDimensionsAsync(SearchIndexProfile indexProf

throw new InvalidOperationException("The selected embedding deployment did not return a valid embedding vector.");
}
}
}
139 changes: 139 additions & 0 deletions src/Primitives/CrestApps.Core.AI/Services/AIDeploymentManagerBase.cs
Original file line number Diff line number Diff line change
@@ -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<AIDeployment>, IAIDeploymentManager
{
public AIDeploymentManagerBase(
INamedSourceCatalog<AIDeployment> deploymentStore,
IEnumerable<ICatalogEntryHandler<AIDeployment>> handlers,
ILogger<AIDeploymentManagerBase> logger)
: base(deploymentStore, handlers, logger)
{
}

public async ValueTask<IEnumerable<AIDeployment>> 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<IEnumerable<AIDeployment>> 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<AIDeployment> 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<AIDeployment> ResolveOrDefaultAsync(AIDeploymentType type, string deploymentName = null, string clientName = null, string connectionName = null)
{
return ResolveByTypeAsync(type, deploymentName, clientName, connectionName);
}

public async ValueTask<IEnumerable<AIDeployment>> 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<AIDeployment> 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<AIDeployment> 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<AIDeployment> FindBySelectorAsync(string selector)
{
var deployment = await FindByIdAsync(selector);

if (deployment != null)
{
return deployment;
}

return await FindByNameAsync(selector);
}

protected abstract ValueTask<string> GetGlobalDefaultSelectorAsync(AIDeploymentType type);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using CrestApps.Core.AI.Deployments;
using CrestApps.Core.AI.Models;
using CrestApps.Core.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace CrestApps.Core.AI.Services;

public class DefaultAIDeploymentManager : NamedSourceCatalogManager<AIDeployment>, IAIDeploymentManager
public class DefaultAIDeploymentManager : AIDeploymentManagerBase
{
private readonly IOptionsMonitor<DefaultAIDeploymentSettings> _deploymentSettings;

Expand All @@ -15,132 +14,12 @@ public DefaultAIDeploymentManager(
IEnumerable<ICatalogEntryHandler<AIDeployment>> handlers,
IOptionsMonitor<DefaultAIDeploymentSettings> deploymentSettings,
ILogger<DefaultAIDeploymentManager> logger)
: base(deploymentStore, handlers, logger)
: base(deploymentStore, handlers, logger)
{
_deploymentSettings = deploymentSettings;
}

public async ValueTask<IEnumerable<AIDeployment>> 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<IEnumerable<AIDeployment>> 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<AIDeployment> 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<AIDeployment> ResolveOrDefaultAsync(AIDeploymentType type, string deploymentName = null, string clientName = null, string connectionName = null)
{
return ResolveByTypeAsync(type, deploymentName, clientName, connectionName);
}

public async ValueTask<IEnumerable<AIDeployment>> 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<AIDeployment> 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<AIDeployment> 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<AIDeployment> FindBySelectorAsync(string selector)
{
var deployment = await FindByIdAsync(selector);

if (deployment != null)
{
return deployment;
}

return await FindByNameAsync(selector);
}

protected virtual ValueTask<string> GetGlobalDefaultSelectorAsync(AIDeploymentType type)
protected override ValueTask<string> GetGlobalDefaultSelectorAsync(AIDeploymentType type)
{
var settings = _deploymentSettings.CurrentValue;

Expand Down
Loading