fix(ui): keep untimed guardrail entries on the request lifecycle - #41374
Merged
yuneng-berri merged 5 commits intoSep 17, 2026
Merged
Conversation
#39050 changed RequestLifecycle from sorting every entry with (a.start_time ?? 0) to filtering on isTimed, which drops any entry whose start_time/end_time are null. That was the right call for the not_run entries the PR introduced, but it also drops entries that DID run and simply carry no timing, and those are pre-existing: add_standard_logging_guardrail_information_to_request_data defaults start_time, end_time and duration to None, and the conduct guardrail passes none of them. One such entry used to draw the whole four-row lifecycle and now draws nothing, so an admin opening that log sees an empty Request Lifecycle panel. An entry now stays on the lifecycle when it is timed OR when it ran, so not_run keeps the exclusion #39050 wanted and every other shape comes back. Offsets are number | null and render as an em dash rather than a fabricated T+0ms, which is what a null minus a null used to produce on the base. Entries without timing sort after the timed ones and the base time comes from the timed entries, so real offsets are unchanged. The two new tests fail on the base component and pass here; #39050's own not_run tests keep passing untouched, which is what makes this additive rather than a revert.
…_lifecycle_untimed_entries
Contributor
|
…ment The four .parentElement reads in the new lifecycle tests pushed testing-library/no-node-access to 712 against a 707 budget, failing frontend-lint. The rows now carry data-testid="lifecycle-row" and the test picks a row with within(), which keeps the assertion tied to the specific row rather than the whole panel and takes the count back to 707.
Contributor
Author
|
@greptileai review |
Contributor
Author
ryan-crabbe-berri
approved these changes
Sep 17, 2026
Contributor
Author
|
@greptileai review |
yuneng-berri
deleted the
litellm_fix_guardrail_lifecycle_untimed_entries
branch
September 17, 2026 20:44
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.
TLDR
Problem this solves:
How it solves it:
not_runkeeps the exclusion fix(guardrails): record not_run evaluation when scoping leaves nothing to scan #39050 added, untouchedT+0msUser Flow
Before: an admin looking at a log for a guardrail that reports no timing gets an empty Request Lifecycle panel
After: the same log shows the lifecycle again, with no invented timings
Relevant issues
Affected release
Linear ticket
Pre-Submission checklist
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Screenshots / Proof of Fix
Both legs were captured by driving the real Admin UI, served by a live proxy on :4010 against its own Postgres, opening the same real log row from one real OpenAI call. The guardrail is a custom guardrail that records its decision without timing, which is the shape the conduct guardrail emits today.
Shared setup,
config.yaml:untimed_guardrail.py, next to the config and on PYTHONPATH:The one request both legs read back:
which stored exactly the shape this PR is about:
[{"duration": null, "end_time": null, "start_time": null, "guardrail_mode": "pre_call", "guardrail_name": "conduct-style-untimed", "guardrail_status": "success"}]Before (174c1ac)
After (51a243e)
Type
🐛 Bug Fix
Caveats
Low
not_runentries that do carry timing still appear, exactly as they do today; this PR does not change thatFinal Attestation