Skip to content

ALEC-306: Fix LLM clustering engine correctness (post-#157 review) - #167

Closed
joseanesONMS wants to merge 1 commit into
release-3.xfrom
ja/alec-306-llm-engine-fixes
Closed

ALEC-306: Fix LLM clustering engine correctness (post-#157 review)#167
joseanesONMS wants to merge 1 commit into
release-3.xfrom
ja/alec-306-llm-engine-fixes

Conversation

@joseanesONMS

Copy link
Copy Markdown
Contributor

Tracked by ALEC-306. Addresses the six review findings against the merged ALEC-301 LLM clustering engine (#157) — the engine could silently stay inactive under a valid config, bypass the token budget, and lacked the topology it needs.

P1 — correctness

  1. Clustering no longer gated on the RCA enabled flag. readLlmConfig required enabled — the Root Cause Analysis toggle — so an operator with endpoint/model/key set but RCA off got null every tick. Clustering now requires only the shared connection fields; selecting the engine is the signal to use it.
  2. Shared token budget enforced + recorded. Each tick checks UTC daily/monthly usage against the limits (same semantics as the RCA TokenBudget) and writes each call's usage into the shared ALEC_LLM_USAGE store — so clustering both respects the cap and shows up in the usage dashboard.
  3. Topology is sent to the model. The request now serializes device adjacency (from the cluster graph) and tags each alarm with its device, so the model can group by connectivity as the prompt/UI/demo assume.

P2 — robustness

  1. Request bounded. Beyond MAX_ALARMS (200) the engine clusters the most-recent alarms instead of emitting a prompt guaranteed to overflow the context window.
  2. Membership dedup. parseResponse assigns each alarm to at most one cluster (global + within-group) and drops sub-2-alarm groups, so a repeated ID can't span situations or inflate a singleton.
  3. Deleted edges reconciled. The periodic topology refresh now removes edges ALEC still holds that EdgeDao no longer returns (missed delete callback) — the exact class of bug the refresh exists to repair. Removal logic factored into a shared removeEdgeInventory().

Verification

  • 34 engine/llm + 20 datasource tests green, with new coverage for budget enforcement, usage recording, topology rendering, membership dedup, the ≥2 rule, and stale-edge reconciliation.
  • The budget/usage records are written directly to the shared KV store in the same shape as the RCA UsageStore (the engine bundle can't depend on features/llm-suggestions).

🤖 Generated with Claude Code

https://claude.ai/code/session_01UB6PGc2rpPbojnqbTHU5ND

Addresses six review findings against the merged ALEC-301 LLM clustering
engine and its topology refresh.

engine/llm/LlmClusterEngine:
- [P1] Decouple clustering from the RCA 'enabled' flag. readLlmConfig no
  longer requires 'enabled' (that toggle is Root Cause Analysis); clustering
  requires only the shared endpoint/model/key. Selecting the engine is the
  signal to use it — it no longer sits silently inactive under a valid config.
- [P1] Enforce and record the shared token budget. Before each tick the engine
  checks UTC daily/monthly usage against the configured limits (same semantics
  as the RCA TokenBudget), and records each call's token usage into the shared
  ALEC_LLM_USAGE store, so clustering both respects and reports into the budget.
- [P1] Send topology connectivity. The request now serializes the adjacency
  between alarm-bearing devices (from the cluster graph) and tags each alarm
  with its device, so the model can actually group by topology as advertised.
- [P2] Bound the request. Beyond MAX_ALARMS (200) the engine clusters the most
  recent alarms rather than emitting a prompt guaranteed to overflow the model.
- [P2] Deduplicate alarm memberships. parseResponse now assigns each alarm to
  at most one cluster (global + within-group dedup) and drops sub-2-alarm
  groups, so a repeated ID can't put an alarm in multiple situations or inflate
  a singleton.

datasource/opennms-direct/DirectInventoryDatasource:
- [P2] Reconcile deleted edges. The periodic refresh now removes edges ALEC
  still holds that the EdgeDao poll no longer returns (missed delete callback),
  instead of leaving their inventory forever. Edge-removal logic extracted to a
  shared removeEdgeInventory() used by both the delete callback and the refresh.

Tests: 34 engine/llm + 20 datasource tests green (new coverage for the budget
enforcement, usage recording, topology rendering, membership dedup, >=2
enforcement, and stale-edge reconciliation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UB6PGc2rpPbojnqbTHU5ND
@joseanesONMS

Copy link
Copy Markdown
Contributor Author

Superseded by #168. This was opened under an erroneous ticket reference (ALEC-306, which does not exist); the work belongs to ALEC-301 (follow-up to #157). Same commit, corrected naming.

@joseanesONMS
joseanesONMS deleted the ja/alec-306-llm-engine-fixes branch July 16, 2026 13:06
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.

1 participant