< Summary

Information
Class: Elsa.AI.Host.Endpoints.AI.Capabilities.Endpoint
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
93%
Covered lines: 72
Uncovered lines: 5
Coverable lines: 77
Total lines: 149
Line coverage: 93.5%
Branch coverage
55%
Covered branches: 10
Total branches: 18
Branch coverage: 55.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Configure()100%210%
ExecuteAsync(...)50%22100%
CreateGroundingCapabilities(...)50%66100%
CreateCapability(...)75%44100%
HasSelectableProvider(...)50%7675%

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]
 816public class Endpoint(
 817    IOptions<AIHostOptions> options,
 818    IEnumerable<IAIProvider> providers,
 819    IEnumerable<IAIConversationStore> conversationStores,
 820    IEnumerable<IAIProposalStore> proposalStores,
 821    IServiceScopeFactory serviceScopeFactory) : ElsaEndpointWithoutRequest<Response>
 22{
 23    public override void Configure()
 24    {
 025        Get("/ai/capabilities");
 026        RequirePermission(Elsa.AI.Host.Permissions.AIResourcePermissions.Capabilities, CoreVerbs.View);
 027    }
 28
 29    public override Task<Response> ExecuteAsync(CancellationToken cancellationToken)
 30    {
 831        var optionsValue = options.Value;
 832        var providerOptions = optionsValue.Providers.ToList();
 1733        var availableProviders = providers.Where(x => providerOptions.IsProviderEnabled(x.Name)).ToList();
 834        var hasSelectableProvider = HasSelectableProvider(optionsValue.DefaultProviderName, providerOptions, availablePr
 1635        var hasDurableConversationStore = conversationStores.Any(x => x is not IAITransientConversationStore);
 36
 837        return Task.FromResult(new Response(
 838            optionsValue.StreamingEnabled && hasSelectableProvider,
 839            optionsValue.ConversationPersistenceEnabled && hasDurableConversationStore,
 840            optionsValue.ProposalReviewEnabled && proposalStores.Any(),
 841            optionsValue.SupportedAttachmentKinds.ToList(),
 842            optionsValue.Agents.Select(x => new AIAgentCapability(x.Name, x.DisplayName, x.Description)).ToList(),
 843            CreateGroundingCapabilities(optionsValue, proposalStores.Any())));
 44    }
 45
 46    private IReadOnlyCollection<AIGroundingCapabilityDescriptor> CreateGroundingCapabilities(AIHostOptions optionsValue,
 47    {
 848        var grounding = optionsValue.Grounding;
 849        using var scope = serviceScopeFactory.CreateScope();
 850        var services = scope.ServiceProvider;
 851        return
 852        [
 853            CreateCapability(
 854                "activities",
 855                "Activity discovery",
 856                grounding.ActivityGroundingEnabled && services.GetService<IActivityRegistry>() != null,
 857                [CapabilityToolNames.ActivitiesSearchToolName, CapabilityToolNames.ActivityDescriptorToolName],
 858                [AIContextAttachmentKinds.Activity],
 859                grounding.ActivityGroundingEnabled,
 860                "Activity Registry is not registered."),
 861            CreateCapability(
 862                "workflows",
 863                "Workflow definitions",
 864                grounding.WorkflowGroundingEnabled && services.GetService<IWorkflowDefinitionStore>() != null,
 865                [CapabilityToolNames.WorkflowsSearchToolName, CapabilityToolNames.WorkflowDefinitionToolName, Capability
 866                [AIContextAttachmentKinds.WorkflowDefinition],
 867                grounding.WorkflowGroundingEnabled,
 868                "Workflow definition store is not registered."),
 869            CreateCapability(
 870                "proposals",
 871                "Workflow proposals",
 872                grounding.ProposalGroundingEnabled && hasProposalStore,
 873                [CapabilityToolNames.WorkflowValidateDraftToolName, CapabilityToolNames.WorkflowProposeCreateToolName, C
 874                [AIContextAttachmentKinds.WorkflowDefinition, AIContextAttachmentKinds.Activity],
 875                grounding.ProposalGroundingEnabled,
 876                "AI proposal store is not registered."),
 877            CreateCapability(
 878                "runtime",
 879                "Runtime inspection",
 880                grounding.RuntimeGroundingEnabled && services.GetService<IWorkflowInstanceStore>() != null,
 881                [CapabilityToolNames.InstancesSearchToolName, CapabilityToolNames.WorkflowInstanceToolName, CapabilityTo
 882                [AIContextAttachmentKinds.WorkflowInstance, AIContextAttachmentKinds.DiagnosticsScope, AIContextAttachme
 883                grounding.RuntimeGroundingEnabled,
 884                "Workflow instance store is not registered.")
 885        ];
 886    }
 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) =>
 3296        new()
 3297        {
 3298            Family = family,
 3299            DisplayName = displayName,
 32100            Available = available,
 32101            ToolNames = toolNames,
 32102            AttachmentKinds = attachmentKinds,
 32103            DisabledReasons = available ? [] : [enabled ? unavailableReason : "Grounding family is disabled by configura
 32104        };
 105
 106    private static bool HasSelectableProvider(string? providerName, IReadOnlyCollection<AIProviderOptions> providerOptio
 107    {
 8108        if (!string.IsNullOrWhiteSpace(providerName))
 109        {
 1110            var configuredProviders = providerOptions.Where(x => x.Enabled).ToList();
 1111            var configuredProvider = configuredProviders.FirstOrDefault(x => string.Equals(x.Name, providerName, StringC
 1112            return configuredProvider != null
 0113                ? availableProviders.Any(x => string.Equals(x.Name, configuredProvider.Name, StringComparison.OrdinalIgn
 0114                                              string.Equals(x.Name, configuredProvider.Provider, StringComparison.Ordina
 3115                : availableProviders.Any(x => string.Equals(x.Name, providerName, StringComparison.OrdinalIgnoreCase));
 116        }
 117
 7118        return availableProviders.Count == 1;
 119    }
 120}
 121
 122public record Response(
 123    bool Streaming,
 124    bool ConversationPersistence,
 125    bool ProposalReview,
 126    IReadOnlyCollection<string> SupportedAttachmentKinds,
 127    IReadOnlyCollection<AIAgentCapability> Agents,
 128    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}