Skip to content

feat: LLM time range extraction + report time window display#35

Merged
WZ merged 6 commits into
mainfrom
feature/time-range-extraction
Mar 24, 2026
Merged

feat: LLM time range extraction + report time window display#35
WZ merged 6 commits into
mainfrom
feature/time-range-extraction

Conversation

@WZ
Copy link
Copy Markdown
Owner

@WZ WZ commented Mar 23, 2026

Summary

  • Fixes broken time range extraction — "what happened to admin UI last Fri around 4PM" now correctly resolves to last Friday 3PM–5PM instead of defaulting to the last 8 hours from now
  • Shows investigated time range in the RCA report header — across web UI, CLI, and markdown export so users can verify the investigation queried the right period
  • LLM-based extraction with robust fallbackgenerateText in the anomaly step with 10s timeout → regex fallback (resolved to absolute UTC) → 8h default

Implementation

Layer Change
Anomaly step generateText call extracts {from, to} from natural language with date validation (no future, no >30d ago, from ≤ to)
Fallback chain LLM → regex → 8h default. New resolveTimeRangeToAbsolute() converts Grafana-relative strings to absolute UTC (prevents stale now-8h in persisted reports)
Pipeline threading timeRange flows through evidence (pass-through in shared factory) → synthesis → post-synthesis → server adapter
Schemas New TimeRangeSchema added to EvidenceOutputSchema, SynthesisOutputSchema, PostSynthesisOutputSchema
Server adapter agents.ts report mapping updated to include timeRange (without this, the field was silently dropped)
Frontend RcaReport.tsx displays "Investigated: Fri Mar 20, 3:00 PM → 5:00 PM" in header with semantic <time> element
CLI + Markdown App.tsx and formatRcaMarkdown.ts both include the investigation window

Test plan

  • 581 tests passing (574 existing + 7 new, 0 failures)
  • resolveTimeRangeToAbsolute — 5 tests covering ISO passthrough, relative resolution, multi-unit support
  • formatRcaMarkdown — 2 tests covering timeRange present/absent
  • Type check clean (npx tsc --noEmit)
  • Manual: type "what happened to admin UI last Fri around 4PM" in console, verify report header shows correct Friday window
  • Manual: verify Prometheus/Loki queries use resolved timestamps (check DOPS_DEBUG=1 logs)

Wilson Li added 6 commits March 23, 2026 11:44
Replace regex-only time parsing with LLM-based extraction in the anomaly
step so natural language like "last Fri around 4PM" resolves to the correct
investigation window. Display the investigated time range in the RCA report
header across web, CLI, and markdown export.

- Add generateText call in anomaly step with 10s timeout and validation
  (no future dates, no >30d ago, from <= to)
- Fallback chain: LLM → regex (resolved to absolute UTC) → 8h default
- New resolveTimeRangeToAbsolute() converts Grafana-relative strings to
  absolute UTC for report persistence (prevents stale "now-8h" metadata)
- Thread timeRange through evidence → synthesis → post-synthesis pipeline
  via pass-through field in shared evidence factory
- Add timeRange to RcaReport type, all workflow schemas, and server adapter
- Display in RcaReport.tsx header with formatTimeRange helper
- Add to CLI App.tsx and formatRcaMarkdown.ts
- 7 new tests (resolveTimeRangeToAbsolute + markdown timeRange)
Prevents timeout callback leak when generateText throws (e.g., AbortController
fires). The callback was harmless (abort on aborted controller is a no-op) but
the finally block is the correct pattern.
- Guard against invalid ISO dates in resolveTimeRangeToAbsolute (e.g.,
  "2026-02-30" which creates Invalid Date and throws on toISOString)
- Include changesFindings in timeRange fallback chain in synthesis step
- Use toLocaleString instead of toLocaleDateString for cross-day ranges
  (toLocaleDateString ignores hour/minute options in some environments)
… fallback

- Add onTokenUsage callback for the generateText call so token usage
  is tracked in phase metrics (Codex adversarial finding #4)
- Wrap regex fallback in try/catch with ultimate 8h default so a bad
  date in the user message can never abort the workflow (Codex #2)
LanguageModelUsage uses inputTokens/outputTokens, not
promptTokens/completionTokens.
The LLM was treating user-stated times (e.g., "4PM") as UTC instead of
converting from the server's local timezone. Added explicit timezone
conversion instructions with worked examples showing the UTC offset
calculation. "around 4PM" in PDT (UTC-7) should produce 23:00Z, not 16:00Z.
@WZ WZ merged commit cc76118 into main Mar 24, 2026
1 check passed
WZ added a commit that referenced this pull request Apr 2, 2026
* feat: LLM-based time range extraction and report time window display

Replace regex-only time parsing with LLM-based extraction in the anomaly
step so natural language like "last Fri around 4PM" resolves to the correct
investigation window. Display the investigated time range in the RCA report
header across web, CLI, and markdown export.

- Add generateText call in anomaly step with 10s timeout and validation
  (no future dates, no >30d ago, from <= to)
- Fallback chain: LLM → regex (resolved to absolute UTC) → 8h default
- New resolveTimeRangeToAbsolute() converts Grafana-relative strings to
  absolute UTC for report persistence (prevents stale "now-8h" metadata)
- Thread timeRange through evidence → synthesis → post-synthesis pipeline
  via pass-through field in shared evidence factory
- Add timeRange to RcaReport type, all workflow schemas, and server adapter
- Display in RcaReport.tsx header with formatTimeRange helper
- Add to CLI App.tsx and formatRcaMarkdown.ts
- 7 new tests (resolveTimeRangeToAbsolute + markdown timeRange)

* fix: move clearTimeout to finally block in LLM time extraction

Prevents timeout callback leak when generateText throws (e.g., AbortController
fires). The callback was harmless (abort on aborted controller is a no-op) but
the finally block is the correct pattern.

* fix: address adversarial review findings

- Guard against invalid ISO dates in resolveTimeRangeToAbsolute (e.g.,
  "2026-02-30" which creates Invalid Date and throws on toISOString)
- Include changesFindings in timeRange fallback chain in synthesis step
- Use toLocaleString instead of toLocaleDateString for cross-day ranges
  (toLocaleDateString ignores hour/minute options in some environments)

* fix: report token usage from time extraction LLM call and guard regex fallback

- Add onTokenUsage callback for the generateText call so token usage
  is tracked in phase metrics (Codex adversarial finding #4)
- Wrap regex fallback in try/catch with ultimate 8h default so a bad
  date in the user message can never abort the workflow (Codex #2)

* fix: use correct AI SDK token usage property names

LanguageModelUsage uses inputTokens/outputTokens, not
promptTokens/completionTokens.

* fix: LLM prompt must convert local times to UTC

The LLM was treating user-stated times (e.g., "4PM") as UTC instead of
converting from the server's local timezone. Added explicit timezone
conversion instructions with worked examples showing the UTC offset
calculation. "around 4PM" in PDT (UTC-7) should produce 23:00Z, not 16:00Z.

---------

Co-authored-by: Wilson Li <wli02@fortinet.com>
@WZ WZ deleted the feature/time-range-extraction branch April 15, 2026 17:41
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