Skip to content

ALEC-296: Redesign correlation engine configuration UI#154

Open
joseanesONMS wants to merge 4 commits intoOpenNMS-Plugins:developfrom
joseanesONMS:ja/jira/ALEC-296
Open

ALEC-296: Redesign correlation engine configuration UI#154
joseanesONMS wants to merge 4 commits intoOpenNMS-Plugins:developfrom
joseanesONMS:ja/jira/ALEC-296

Conversation

@joseanesONMS
Copy link
Copy Markdown

@joseanesONMS joseanesONMS commented May 1, 2026

Summary

Reworks the Correlation Engine Configuration page (AccountSettings.vue) and the onboarding flow (ConfigurationPage.vue):

  • Page header renamed: Configuration PageCorrelation Engine Configuration Page.
  • Deep Learning option hidden from the engine selector on both screens. The deeplearning bundle is intentionally not unregistered — it remains available for tests and regression scenarios; this is a UI-only filter.
  • New disabled LLM Based entry with a Coming soon caption (placeholder for the upcoming engine option).
  • DBScan correlation variables (alpha / beta / epsilon) are now editable in the UI when Clustering is selected. The backend already accepted these values via EngineParameter — previously the UI only sent engineName + distanceMeasureName.
  • Variables section is hidden when a non-Clustering engine is selected (currently only LLM Based, which is disabled, but the logic is in place).
  • Click-toggle help popover next to the Correlation variables title with bulleted explanations and default values for α / β / ε. (Feather's tooltip is string-only, so a popover is needed for bullets + inline code.)
  • Circular-arrow Restore button to reset α / β / ε to their defaults in one click.
  • Two action buttons at the bottom of the page, to the left of Save Changes, each with a confirm() before firing:
    • Close All Open SituationsPOST /alec/situation/closeAll
    • Re-Evaluate All Open AlarmsPOST /alec/engine/reEvaluate

Backend additions

  • SituationRest: new POST /alec/situation/closeAll. Walks every situation that is not already ACCEPTED/REJECTED, marks it REJECTED, sends to the cloud client, and forwards an empty-alarm REJECTED situation to OpenNMS — same mechanism the existing per-situation reject path uses.
  • EngineRest: new POST /alec/engine/reEvaluate. Calls the existing driverInit() (destroy + initAsync) so the engine re-applies the current correlation variables to all currently active alarms.

Out of scope

  • Cross-device correlation bug is reproduced in the new CrossDeviceCorrelationIT (marked @Ignore'd) and tracked separately as ALEC-297. With no topology path between two devices, AlarmInSpaceTimeDistanceMeasure.compute() returns ~10⁹–10¹⁰, which no realistic ε can swallow. Empirical sweep in the test confirms epsilon ≥ 1e10 is required to cluster cross-device alarms — clearly unusable in practice. Fix (configurable no-path penalty, end-to-end plumbing) is intentionally deferred.

Ticket: ALEC-296

Test plan

  • mvn -pl features/ui test (extends EngineRestImplTest with testSetDbScanAppliesAlphaBetaEpsilonToFactory + testReEvaluateAllOpenAlarmsTriggersDriverReinit; extends SituationRestImplTest with closeAllOpenSituations_marksAllNonClosedAsRejected).
  • npm test in ui/ (12 tests in accountSettings.test.ts covering title, Deep-Learning removal, LLM-Based disabled + caption, variables form, save round-trip, help popover toggle + bullets + defaults, reset to defaults, engine-switch hide, both action buttons confirming and dispatching, and the no-op path when confirm is dismissed).
  • npm run build in ui/ and mvn clean install -DskipTests produce a fresh KAR.
  • Smoke-tested on a local OpenNMS Horizon 36 dev install:
    • Page header reads "Correlation Engine Configuration Page".
    • Engine selector shows Clustering + disabled LLM Based.
    • Help popover toggles, shows bullets + defaults.
    • Restore icon snaps α / β / ε back to defaults.
    • POST /opennms/rest/alec/situation/closeAll → 200, POST /opennms/rest/alec/engine/reEvaluate → 200.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 16 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread features/ui/src/main/java/org/opennms/alec/rest/EngineRestImpl.java
Comment thread ui/src/tests/accountSettings.test.ts
joseanesONMS added a commit to joseanesONMS/alec that referenced this pull request May 5, 2026
Addresses Copilot review feedback on PR OpenNMS-Plugins#154. Without this,
reEvaluateAllOpenAlarms() (and the existing engine-config save paths)
could report HTTP 200 even when the driver init was interrupted before
the engine came back up — masking failures from the caller.

(The companion test fix for the missing re-evaluate confirm-cancel
no-op test was already pushed by Copilot Autofix as 19bc094.)
Rebased onto current develop after PR OpenNMS-Plugins#152 (ALEC-298) removed the
cloud-feedback gRPC and data-sharing agreement flow. The agreement
section that lived in the original commit on this branch is gone —
it depended on infrastructure that no longer exists. The rest of the
ALEC-296 scope is preserved:

UI (ui/src/containers/AccountSettings.vue):
- Page header renamed: "Configuration Page" → "Correlation Engine
  Configuration Page".
- Deep Learning option hidden from the engine selector (the bundle is
  intentionally NOT unregistered: it remains available for tests and
  regression scenarios; this is a UI-only filter).
- New disabled "LLM Based" radio with a "Coming soon" caption as a
  placeholder for the upcoming engine option.
- DBScan correlation variables (alpha, beta, epsilon) editable as
  numeric inputs visible only when Clustering is selected; wired
  through useUserStore.setEngineInfo() (now accepts an overrides
  arg) into the existing /alec/engine/configuration endpoint, which
  already accepted these values — previously the UI sent only
  engineName and distanceMeasureName.
- Click-toggle help popover next to the variables title with bulleted
  guidance + default values for each variable. (Feather's tooltip is
  string-only, hence a popover for bullets / inline code / italics.)
- Circular-arrow Restore button to reset variables to defaults in
  one click.
- Two action buttons at the bottom (left of Save Changes), each
  prompting window.confirm before firing:
  * "Close All Open Situations" → POST /alec/situation/closeAll
  * "Re-Evaluate All Open Alarms" → POST /alec/engine/reEvaluate

Backend (features/ui):
- SituationRest + SituationRestImpl: new POST /alec/situation/closeAll
  endpoint. Walks every situation that is not already ACCEPTED/REJECTED
  and forwards an empty-alarm REJECTED situation to OpenNMS — same
  mechanism the existing per-situation reject path uses. Post-OpenNMS-Plugins#152
  the cloud-client (gRPC) sendSituation step the original commit had
  is intentionally absent.
- EngineRest + EngineRestImpl: new POST /alec/engine/reEvaluate
  endpoint. Calls the existing driverInit() (destroy + initAsync) so
  the engine re-applies the current correlation variables to all
  currently active alarms.
- driverInit() now returns an error Response on InterruptedException
  (was: silently returned null and let the caller report 200).
  Addresses Copilot review feedback from the original PR OpenNMS-Plugins#154.

Tests:
- UI: 13 vitest specs in src/tests/accountSettings.test.ts covering
  the new title, Deep-Learning removal, disabled LLM-Based + caption,
  variables form, save round-trip with alpha/beta/epsilon, help
  popover toggle (bullets + defaults), reset to defaults, the
  Clustering-only visibility rule, and both action buttons confirming
  and dispatching plus their no-op-on-cancel paths.
- Backend: extend EngineRestImplTest with a reEvaluate-triggers-init
  test and an alpha/beta/epsilon-applied-to-factory test; extend
  SituationRestImplTest with closeAllOpenSituations marking
  every non-closed situation as REJECTED.
…ization

Annotated the EngineParameterImpl Builder with
@JsonIgnoreProperties(ignoreUnknown = true) so persisted engine config
can be read back even when it contains fields added by a future
version (e.g. ALEC-297's noPathDistance). Without this, dropping back
to a build that doesn't know about the newer field caused the
features.ui bundle to fail to start with:

  Unrecognized field "noPathDistance" (class
  EngineParameterImpl$Builder), not marked as ignorable

Reproduced locally by deploying this branch on top of a KV store that
had a noPathDistance value persisted by an earlier ALEC-297 build.

Added EngineParameterImplTest.deserializeIgnoresUnknownFields to lock
in the new behaviour.
Previously the system default was alarminspaceandtimedistance: the
DBScan blueprint's cm:property and DBScanEngine.DEFAULT_DISTANCE_MEASURE
both pointed at it, and the UI checkbox was only ticked when the
persisted config explicitly said "hellinger". On a brand-new install
that meant Clustering ran with the spatial-time measure even though
Hellinger is the recommended default.

Switched the default to Hellinger in both layers:

- engine/dbscan blueprint: cm:property name="distanceMeasure" now
  defaults to "hellinger" (matches the lookup-map key, which the
  alarmInSpaceAndTimeDistanceMeasureFactory bean-id form did not —
  the old value was triggering the "Wrong distance measure
  configuration ..." fallback warning at startup).
- DBScanEngine.DEFAULT_DISTANCE_MEASURE = "hellinger". This also
  flows into EngineParameterImpl.getDistanceMeasureName()'s null
  fallback, so REST clients that don't supply a distanceMeasureName
  read back "hellinger".
- AccountSettings.vue: when there is no persisted engineInfo (fresh
  install), default the Hellinger checkbox to checked. When there is
  a saved config, honour what's persisted as before.

Tests: extended accountSettings.test.ts with a fresh-system check
asserting hellinger.value === true when engineInfo is null.
deserializeDefaultStringBuilder asserted that getEpsilon() returned
null when only engineName was supplied. That happened to be true
before, because DBScanEngine.DEFAULT_DISTANCE_MEASURE was
"alarminspaceandtimedistance", and getEpsilon()'s switch matches
"alarminspacetime" (not "alarminspaceandtimedistance"), so the
no-match branch returned null.

After the previous commit set the DBScan default to "hellinger",
getEpsilon()'s switch now picks up the "hellinger" case and returns
HellingerDistanceMeasure.DEFAULT_EPSILON (75.0). The test expectation
was wrong: it was relying on a switch-case fallthrough rather than an
intentional null. Updated to assert HellingerDistanceMeasure.DEFAULT_EPSILON.

Local test runs masked this because Java compile-time-constant
inlining kept the old DEFAULT_DISTANCE_MEASURE value baked into
EngineParameterImpl.class until a clean rebuild — CI does a clean
build, which is how this surfaced (CircleCI build 8631 on PR OpenNMS-Plugins#154).
Copy link
Copy Markdown
Contributor

@cgorantla cgorantla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Haven't looked closely at the UI changes. May be @synqotik can review these changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants