using Elsa.AI.Abstractions.Contracts; using Elsa.AI.Abstractions.Models; using Elsa.AI.Host.Services; using Elsa.Workflows.Management; using Elsa.Workflows.Management.Filters; namespace Elsa.AI.Host.Context; public class WorkflowInstanceContextProvider(IServiceProvider serviceProvider, RuntimeGroundingMapper mapper) : IAIContextProvider, IPlaceholderAIContextProvider { public string Kind => AIContextAttachmentKinds.WorkflowInstance; public async ValueTask ResolveAsync(AIContextResolutionRequest request, CancellationToken cancellationToken = default) { var attachment = request.Attachment; var store = serviceProvider.GetService(typeof(IWorkflowInstanceStore)) as IWorkflowInstanceStore; if (store == null) return Unavailable(attachment); var instance = await store.FindAsync(new WorkflowInstanceFilter { Id = attachment.ReferenceId }, cancellationToken); if (instance == null || !string.Equals(NormalizeTenant(instance.TenantId), NormalizeTenant(request.TenantId), StringComparison.Ordinal)) return NotFound(attachment); var data = AIGroundingJson.ToJsonObject(mapper.Map(instance)); data["state"] = mapper.MapState(instance); data["incidents"] = AIGroundingJson.ToJsonArray(instance.WorkflowState.Incidents.Select(x => mapper.MapIncident(instance.Id, x))); return new AIResolvedContext { Kind = Kind, ReferenceId = attachment.ReferenceId, Summary = $"Workflow instance {instance.Id} is {instance.Status}/{instance.SubStatus} with {instance.IncidentCount} incidents.", Data = data, Metadata = new JsonObject { ["activityId"] = attachment.ActivityId } }; } private AIResolvedContext Unavailable(AIContextAttachment attachment) => new() { Kind = Kind, ReferenceId = attachment.ReferenceId, Summary = "Workflow instance store is not available.", Data = [] }; private AIResolvedContext NotFound(AIContextAttachment attachment) => new() { Kind = Kind, ReferenceId = attachment.ReferenceId, Summary = "Workflow instance was not found or is not authorized.", Data = [] }; private static string NormalizeTenant(string? tenantId) => string.IsNullOrWhiteSpace(tenantId) ? "" : tenantId; }