< Summary

Information
Class: Elsa.AI.Host.Endpoints.AI.Capabilities.Response
Assembly: Elsa.AI.Host
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.AI.Host/Endpoints/AI/Capabilities/Endpoint.cs
Line coverage
85%
Covered lines: 6
Uncovered lines: 1
Coverable lines: 7
Total lines: 149
Line coverage: 85.7%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Streaming()100%11100%
get_ConversationPersistence()100%11100%
get_ProposalReview()100%11100%
get_SupportedAttachmentKinds()100%11100%
get_Agents()100%210%
get_Grounding()100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.AI.Host/Endpoints/AI/Capabilities/Endpoint.cs

#LineLine coverage
 1using Elsa.Authorization;
 2using Elsa.Abstractions;
 3using Elsa.AI.Abstractions.Contracts;
 4using Elsa.AI.Abstractions.Models;
 5using Elsa.AI.Host.Options;
 6using Elsa.AI.Host.Permissions;
 7using Elsa.Workflows;
 8using Elsa.Workflows.Management;
 9using JetBrains.Annotations;
 10using Microsoft.Extensions.DependencyInjection;
 11using Microsoft.Extensions.Options;
 12
 13namespace Elsa.AI.Host.Endpoints.AI.Capabilities;
 14
 15[PublicAPI]
 16public class Endpoint(
 17    IOptions<AIHostOptions> options,
 18    IEnumerable<IAIProvider> providers,
 19    IEnumerable<IAIConversationStore> conversationStores,
 20    IEnumerable<IAIProposalStore> proposalStores,
 21    IServiceScopeFactory serviceScopeFactory) : ElsaEndpointWithoutRequest<Response>
 22{
 23    public override void Configure()
 24    {
 25        Get("/ai/capabilities");
 26        RequirePermission(Elsa.AI.Host.Permissions.AIResourcePermissions.Capabilities, CoreVerbs.View);
 27    }
 28
 29    public override Task<Response> ExecuteAsync(CancellationToken cancellationToken)
 30    {
 31        var optionsValue = options.Value;
 32        var providerOptions = optionsValue.Providers.ToList();
 33        var availableProviders = providers.Where(x => providerOptions.IsProviderEnabled(x.Name)).ToList();
 34        var hasSelectableProvider = HasSelectableProvider(optionsValue.DefaultProviderName, providerOptions, availablePr
 35        var hasDurableConversationStore = conversationStores.Any(x => x is not IAITransientConversationStore);
 36
 37        return Task.FromResult(new Response(
 38            optionsValue.StreamingEnabled && hasSelectableProvider,
 39            optionsValue.ConversationPersistenceEnabled && hasDurableConversationStore,
 40            optionsValue.ProposalReviewEnabled && proposalStores.Any(),
 41            optionsValue.SupportedAttachmentKinds.ToList(),
 42            optionsValue.Agents.Select(x => new AIAgentCapability(x.Name, x.DisplayName, x.Description)).ToList(),
 43            CreateGroundingCapabilities(optionsValue, proposalStores.Any())));
 44    }
 45
 46    private IReadOnlyCollection<AIGroundingCapabilityDescriptor> CreateGroundingCapabilities(AIHostOptions optionsValue,
 47    {
 48        var grounding = optionsValue.Grounding;
 49        using var scope = serviceScopeFactory.CreateScope();
 50        var services = scope.ServiceProvider;
 51        return
 52        [
 53            CreateCapability(
 54                "activities",
 55                "Activity discovery",
 56                grounding.ActivityGroundingEnabled && services.GetService<IActivityRegistry>() != null,
 57                [CapabilityToolNames.ActivitiesSearchToolName, CapabilityToolNames.ActivityDescriptorToolName],
 58                [AIContextAttachmentKinds.Activity],
 59                grounding.ActivityGroundingEnabled,
 60                "Activity Registry is not registered."),
 61            CreateCapability(
 62                "workflows",
 63                "Workflow definitions",
 64                grounding.WorkflowGroundingEnabled && services.GetService<IWorkflowDefinitionStore>() != null,
 65                [CapabilityToolNames.WorkflowsSearchToolName, CapabilityToolNames.WorkflowDefinitionToolName, Capability
 66                [AIContextAttachmentKinds.WorkflowDefinition],
 67                grounding.WorkflowGroundingEnabled,
 68                "Workflow definition store is not registered."),
 69            CreateCapability(
 70                "proposals",
 71                "Workflow proposals",
 72                grounding.ProposalGroundingEnabled && hasProposalStore,
 73                [CapabilityToolNames.WorkflowValidateDraftToolName, CapabilityToolNames.WorkflowProposeCreateToolName, C
 74                [AIContextAttachmentKinds.WorkflowDefinition, AIContextAttachmentKinds.Activity],
 75                grounding.ProposalGroundingEnabled,
 76                "AI proposal store is not registered."),
 77            CreateCapability(
 78                "runtime",
 79                "Runtime inspection",
 80                grounding.RuntimeGroundingEnabled && services.GetService<IWorkflowInstanceStore>() != null,
 81                [CapabilityToolNames.InstancesSearchToolName, CapabilityToolNames.WorkflowInstanceToolName, CapabilityTo
 82                [AIContextAttachmentKinds.WorkflowInstance, AIContextAttachmentKinds.DiagnosticsScope, AIContextAttachme
 83                grounding.RuntimeGroundingEnabled,
 84                "Workflow instance store is not registered.")
 85        ];
 86    }
 87
 88    private static AIGroundingCapabilityDescriptor CreateCapability(
 89        string family,
 90        string displayName,
 91        bool available,
 92        IReadOnlyCollection<string> toolNames,
 93        IReadOnlyCollection<string> attachmentKinds,
 94        bool enabled,
 95        string unavailableReason) =>
 96        new()
 97        {
 98            Family = family,
 99            DisplayName = displayName,
 100            Available = available,
 101            ToolNames = toolNames,
 102            AttachmentKinds = attachmentKinds,
 103            DisabledReasons = available ? [] : [enabled ? unavailableReason : "Grounding family is disabled by configura
 104        };
 105
 106    private static bool HasSelectableProvider(string? providerName, IReadOnlyCollection<AIProviderOptions> providerOptio
 107    {
 108        if (!string.IsNullOrWhiteSpace(providerName))
 109        {
 110            var configuredProviders = providerOptions.Where(x => x.Enabled).ToList();
 111            var configuredProvider = configuredProviders.FirstOrDefault(x => string.Equals(x.Name, providerName, StringC
 112            return configuredProvider != null
 113                ? availableProviders.Any(x => string.Equals(x.Name, configuredProvider.Name, StringComparison.OrdinalIgn
 114                                              string.Equals(x.Name, configuredProvider.Provider, StringComparison.Ordina
 115                : availableProviders.Any(x => string.Equals(x.Name, providerName, StringComparison.OrdinalIgnoreCase));
 116        }
 117
 118        return availableProviders.Count == 1;
 119    }
 120}
 121
 8122public record Response(
 4123    bool Streaming,
 5124    bool ConversationPersistence,
 2125    bool ProposalReview,
 5126    IReadOnlyCollection<string> SupportedAttachmentKinds,
 0127    IReadOnlyCollection<AIAgentCapability> Agents,
 13128    IReadOnlyCollection<AIGroundingCapabilityDescriptor> Grounding);
 129
 130public record AIAgentCapability(string Name, string DisplayName, string Description);
 131
 132file static class CapabilityToolNames
 133{
 134    public const string ActivitiesSearchToolName = "activities.search";
 135    public const string ActivityDescriptorToolName = "activities.getDescriptor";
 136    public const string WorkflowsSearchToolName = "workflows.search";
 137    public const string WorkflowDefinitionToolName = "workflows.getDefinition";
 138    public const string WorkflowDefinitionGraphToolName = "workflows.getDefinitionGraph";
 139    public const string WorkflowUsageSearchToolName = "workflows.findUsages";
 140    public const string WorkflowValidateDraftToolName = "workflows.validateDraft";
 141    public const string WorkflowProposeCreateToolName = "workflows.proposeCreate";
 142    public const string WorkflowProposeUpdateToolName = "workflows.proposeUpdate";
 143    public const string InstancesSearchToolName = "instances.search";
 144    public const string WorkflowInstanceToolName = "instances.get";
 145    public const string WorkflowInstanceExecutionHistoryToolName = "instances.getExecutionHistory";
 146    public const string WorkflowInstanceActivityStateToolName = "instances.getActivityState";
 147    public const string IncidentsSearchToolName = "incidents.search";
 148    public const string IncidentToolName = "incidents.get";
 149}