feat: LLM time range extraction + report time window display#35
Merged
Conversation
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)
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
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>
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
generateTextin the anomaly step with 10s timeout → regex fallback (resolved to absolute UTC) → 8h defaultImplementation
generateTextcall extracts{from, to}from natural language with date validation (no future, no >30d ago, from ≤ to)resolveTimeRangeToAbsolute()converts Grafana-relative strings to absolute UTC (prevents stalenow-8hin persisted reports)timeRangeflows through evidence (pass-through in shared factory) → synthesis → post-synthesis → server adapterTimeRangeSchemaadded toEvidenceOutputSchema,SynthesisOutputSchema,PostSynthesisOutputSchemaagents.tsreport mapping updated to includetimeRange(without this, the field was silently dropped)RcaReport.tsxdisplays "Investigated: Fri Mar 20, 3:00 PM → 5:00 PM" in header with semantic<time>elementApp.tsxandformatRcaMarkdown.tsboth include the investigation windowTest plan
resolveTimeRangeToAbsolute— 5 tests covering ISO passthrough, relative resolution, multi-unit supportformatRcaMarkdown— 2 tests covering timeRange present/absentnpx tsc --noEmit)DOPS_DEBUG=1logs)