Class AgentOverviewDashboardService

java.lang.Object
com.flowable.platform.service.dashboard.agent.AgentOverviewDashboardService

public class AgentOverviewDashboardService extends Object
Backend for the agent overview dashboard in Hub.

The actual ES aggregation logic lives in the _flowable_AgentInvocation*Query.query resource files (see modules/flowable-platform-service/.../query/default/). This service does not encode any new query logic — it just calls the existing queries by key via QueryService.executeQuery(String, String, Map) and merges the results into a denormalized AgentOverviewDashboardService.Response.

Evaluator quality is pass-rate only (evaluations + passed); paired aggregations share group/size/order to keep buckets consistent.

Performance note: getDashboard(Instant, Instant, List, String, String) issues ~22 sequential ES round-trips per call. Two faster alternatives were evaluated (but intentionally not yet implemented):

  • Elasticsearch _msearch — one HTTP round-trip with an NDJSON body of all sub-queries. Big latency win, but needs new methods on ElasticsearchClient / SearchService / QueryService
  • CompletableFuture parallelism — keeps N round-trips but runs them concurrently. Requires a dedicated I/O thread pool sized to match the Rest5Client connection pool (the default ForkJoinPool.commonPool() is sized for CPU work, would starve unrelated JVM work, and would queue threads on connection acquisition).
  • Field Details

  • Constructor Details

    • AgentOverviewDashboardService

      public AgentOverviewDashboardService(QueryService queryService, tools.jackson.databind.ObjectMapper objectMapper)
  • Method Details

    • getDashboard

      public AgentOverviewDashboardService.Response getDashboard(Instant from, Instant to, List<String> agentDefinitionKeys, String interval, String tenantId)
    • getAgentDrillDown

      public AgentOverviewDashboardService.AgentDrillDown getAgentDrillDown(Instant from, Instant to, String agentDefinitionKey, String interval, String tenantId)
      Per-agent drill-down: per-evaluator quality, a pass-rate trend, and quality by parent version and by operation.
    • buildKpis

      protected AgentOverviewDashboardService.Kpis buildKpis(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildInvocationsOverTime

      protected List<AgentOverviewDashboardService.TimeBucket> buildInvocationsOverTime(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildTokensByTypeOverTime

      protected List<AgentOverviewDashboardService.TimeBucket> buildTokensByTypeOverTime(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildByDefinition

      protected List<AgentOverviewDashboardService.ByDefinitionRow> buildByDefinition(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildByLlm

      protected List<AgentOverviewDashboardService.ByLlmRow> buildByLlm(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildGuardrails

      protected AgentOverviewDashboardService.Guardrails buildGuardrails(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildEvaluators

      protected AgentOverviewDashboardService.Evaluators buildEvaluators(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
      Evaluator KPI counts. Only completed evaluations are indexed, so evaluations = passed + failed.
    • readEvaluatorCounts

      protected AgentOverviewDashboardService.EvaluatorCounts readEvaluatorCounts(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
      Evaluator pass/fail counts from one QUERY_EVALUATOR_PASS_RATE aggregation; failed is the remainder after passed.
    • buildTools

      protected AgentOverviewDashboardService.Tools buildTools(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildCacheHitRate

      protected AgentOverviewDashboardService.CacheHit buildCacheHitRate(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildOperationDistribution

      protected List<AgentOverviewDashboardService.Bucket> buildOperationDistribution(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
    • buildEvaluatorRows

      protected List<AgentOverviewDashboardService.EvaluatorRow> buildEvaluatorRows(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
      Per-evaluator quality table grouped by evaluatorKey: bucket doc_count = evaluations, passedNumeric sum = passed.
    • buildEvaluatorTrend

      protected List<AgentOverviewDashboardService.TimeBucket> buildEvaluatorTrend(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
      Evaluator pass-rate over time: doc_count per bucket = evaluations, passedNumeric sum = passed.
    • buildEvaluatorTrendByVersion

      protected List<AgentOverviewDashboardService.TimeBucket> buildEvaluatorTrendByVersion(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
      Evaluator pass-rate over time, per agent-definition version (value = pass-rate %), null when a version had no verdicts in the bucket so the chart renders a gap, not a 0%.
    • buildQualityByVersion

      protected List<AgentOverviewDashboardService.VersionStat> buildQualityByVersion(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
      Quality by the PARENT agent's agentDefinitionVersion — evaluators version independently and are identified by bare key.
    • buildQualityByOperation

      protected List<AgentOverviewDashboardService.OperationStat> buildQualityByOperation(Map<String, tools.jackson.databind.JsonNode> filters, String tenantId)
      Quality by operation key (already filtered to the drilled-down agent; operation keys are agent-scoped).
    • run

      protected tools.jackson.databind.JsonNode run(String queryKey, String tenantId, Map<String, tools.jackson.databind.JsonNode> filterValuesByType)
      Calls the deployed query by key. Returns the raw "aggregations" subtree (or null on failure).

      Translates the type-keyed filter values into the actual payload keys the query expects by reading the query model's input parameters. This works for both old-style (implicit __flowable_internal_input_param_* names) and new-style (explicit inputParameters block) query JSONs.

    • translateFilterValues

      protected Map<String, tools.jackson.databind.JsonNode> translateFilterValues(QueryDefinitionModel model, Map<String, tools.jackson.databind.JsonNode> filterValuesByType)
      Walks the query's declared input parameters and builds a payload using each parameter's actual name, looking up its value by type from the dashboard's filter values. Decouples this service from how the platform deployer happens to name parameters internally.
    • buildFilterValuesByType

      protected Map<String, tools.jackson.databind.JsonNode> buildFilterValuesByType(Instant from, Instant to, Collection<String> agentDefinitionKeys, String interval)
    • asLong

      protected static long asLong(tools.jackson.databind.JsonNode aggs, String name)
    • asDocCount

      protected static long asDocCount(tools.jackson.databind.JsonNode aggs, String name)
      Reads the matching-document count of a single-bucket filter aggregation (the shape that countCompleted / countActive produce). Filter aggregations respond with doc_count — they have no value field like the metric aggregations (sum/avg/cardinality) that asLong(JsonNode, String) reads.
    • asNestedFilteredAvg

      protected static double asNestedFilteredAvg(tools.jackson.databind.JsonNode aggs, String name)
      Reads the value of an avgNestedFieldFiltered aggregation. The metric lives several levels deep: /{aggName}/{aggName}_filtered/{aggName}_avg/value.
    • asDouble

      protected static double asDouble(tools.jackson.databind.JsonNode aggs, String name)
    • readBuckets

      protected static void readBuckets(tools.jackson.databind.JsonNode aggs, String aggName, AgentOverviewDashboardService.BucketHandler handler)
    • readBucketsWithSumOrCount

      protected static void readBucketsWithSumOrCount(tools.jackson.databind.JsonNode aggs, String aggName, AgentOverviewDashboardService.BucketWithValueHandler handler)
    • readBucketsWithSum

      protected static List<AgentOverviewDashboardService.Bucket> readBucketsWithSum(tools.jackson.databind.JsonNode aggs, String aggName)
      Reads buckets from a groupByFieldWithSumField aggregation, returning only the sub-aggregation sum value. We deliberately do NOT fall back to doc_count: for metrics like "violations per definition", a zero sum is a meaningful answer (no violations for that agent), and bucketed agents with zero are filtered out so the chart shows only agents that actually contributed to the metric.
    • nestedBuckets

      protected static tools.jackson.databind.JsonNode nestedBuckets(tools.jackson.databind.JsonNode aggs, String aggName)
      Bucket list of a groupByNestedFieldFiltered-style aggregation, or a missing node when the path is absent.
    • readNestedFilteredBuckets

      protected static void readNestedFilteredBuckets(tools.jackson.databind.JsonNode aggs, String aggName, AgentOverviewDashboardService.BucketHandler handler)
      Reads buckets from a groupByNestedFieldFiltered-style aggregation. The bucket list lives several levels deep: /aggregations/{aggName}/{aggName}_filtered/{aggName}_inner/buckets.
    • readNestedFilteredBucketsWithSub

      protected static void readNestedFilteredBucketsWithSub(tools.jackson.databind.JsonNode aggs, String aggName, String subAggSuffix, AgentOverviewDashboardService.BucketWithValueHandler handler)
      Mirrors groupByNestedFieldFilteredWith[Sum|Avg]Field. Unlike the flat group-by builders (which name their metric sub-aggregation aggName + "_sub"), the nested filtered builders name it aggName + "_sum" or aggName + "_avg" depending on the metric — the caller passes the matching suffix.
    • readDateBuckets

      protected static List<AgentOverviewDashboardService.TimeBucket> readDateBuckets(tools.jackson.databind.JsonNode aggs, String aggName, String seriesName)
    • mergeDateSeries

      protected static void mergeDateSeries(Map<String, AgentOverviewDashboardService.TimeBucket> byTime, tools.jackson.databind.JsonNode aggs, String aggName, String seriesName)
    • mergeDateDocCount

      protected static void mergeDateDocCount(Map<String, AgentOverviewDashboardService.TimeBucket> byTime, tools.jackson.databind.JsonNode aggs, String aggName, String seriesName)
      Merges a date agg's per-bucket doc_count as a series, ignoring any sub-aggregation.
    • accumulateDateVersionPassedAndTotal

      protected static void accumulateDateVersionPassedAndTotal(tools.jackson.databind.JsonNode aggs, String aggName, Map<String, Map<String,long[]>> acc)
      Reads a date_histogram > terms(version) > sum aggregation into long[2] per (time, version): slot 0 = evaluations (doc_count), slot 1 = passed (sum).