mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
fix(ui): order each lifecycle phase on its own clock
This commit is contained in:
parent
d67c7894dd
commit
6544671a31
2 changed files with 48 additions and 12 deletions
|
|
@ -42,6 +42,24 @@ const timedPreCall: Partial<GuardrailInformation> = {
|
|||
duration: 0.1,
|
||||
};
|
||||
|
||||
const latePreCall: Partial<GuardrailInformation> = {
|
||||
guardrail_name: "late-pre-rail",
|
||||
guardrail_status: "success",
|
||||
guardrail_mode: "pre_call",
|
||||
start_time: 1_700_000_500,
|
||||
end_time: 1_700_000_500.1,
|
||||
duration: 0.1,
|
||||
};
|
||||
|
||||
const untimedPostCall: Partial<GuardrailInformation> = {
|
||||
guardrail_name: "untimed-post-rail",
|
||||
guardrail_status: "success",
|
||||
guardrail_mode: "post_call",
|
||||
start_time: null,
|
||||
end_time: null,
|
||||
duration: null,
|
||||
};
|
||||
|
||||
const ranPostCall: Partial<GuardrailInformation> = {
|
||||
guardrail_name: "ran-rail",
|
||||
guardrail_status: "success",
|
||||
|
|
@ -141,6 +159,22 @@ describe("GuardrailViewer", () => {
|
|||
expect(untimedIndex).toBeLessThan(timedIndex);
|
||||
});
|
||||
|
||||
it("orders each phase on its own clock when a later pre-call outlives an earlier post-call", () => {
|
||||
const latePre = makeGuardrailInformation(latePreCall);
|
||||
const untimedPost = makeGuardrailInformation(untimedPostCall);
|
||||
const earlyPost = makeGuardrailInformation(ranPostCall);
|
||||
renderWithProviders(<GuardrailViewer data={[latePre, untimedPost, earlyPost]} />);
|
||||
|
||||
const rows = screen.getAllByTestId("lifecycle-row");
|
||||
const rowIndex = (label: RegExp): number => rows.findIndex((r) => within(r).queryByText(label) !== null);
|
||||
const untimedIndex = rowIndex(/Post-call guardrail: untimed-post-rail/);
|
||||
const earlyIndex = rowIndex(/Post-call guardrail: ran-rail/);
|
||||
|
||||
expect(untimedIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(earlyIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(untimedIndex).toBeLessThan(earlyIndex);
|
||||
});
|
||||
|
||||
it("anchors offsets on the timed entries and gives the untimed one no fabricated offset", () => {
|
||||
const untimed = makeGuardrailInformation(untimedPreCall);
|
||||
const ran = makeGuardrailInformation(ranPostCall);
|
||||
|
|
|
|||
|
|
@ -372,15 +372,17 @@ const isTimed = (e: GuardrailInformation): e is TimedGuardrailInformation =>
|
|||
|
||||
const belongsOnLifecycle = (e: GuardrailInformation): boolean => isTimed(e) || getEntryOutcome(e) !== "not_run";
|
||||
|
||||
// Sorts a phase's timed entries by start time while leaving its untimed entries in the
|
||||
// slots they were recorded in. Applied per phase, never globally: an entry can land in
|
||||
// more than one phase bucket, so a global pass can reorder one phase by another's clock.
|
||||
const orderWithinPhase = (group: GuardrailInformation[]): GuardrailInformation[] => {
|
||||
const byStart = group.filter(isTimed).sort((a, b) => a.start_time - b.start_time);
|
||||
const timedSlots = new Map(group.flatMap((e, i) => (isTimed(e) ? [i] : [])).map((slot, k) => [slot, byStart[k]]));
|
||||
return group.map((e, i) => timedSlots.get(i) ?? e);
|
||||
};
|
||||
|
||||
const RequestLifecycle = ({ entries }: { entries: GuardrailInformation[] }) => {
|
||||
const sorted = useMemo(() => {
|
||||
const onLifecycle = entries.filter(belongsOnLifecycle);
|
||||
const byStart = onLifecycle.filter(isTimed).sort((a, b) => a.start_time - b.start_time);
|
||||
const timedSlots = new Map(
|
||||
onLifecycle.flatMap((e, i) => (isTimed(e) ? [i] : [])).map((slot, k) => [slot, byStart[k]]),
|
||||
);
|
||||
return onLifecycle.map((e, i) => timedSlots.get(i) ?? e);
|
||||
}, [entries]);
|
||||
const sorted = useMemo(() => entries.filter(belongsOnLifecycle), [entries]);
|
||||
|
||||
const timeline = useMemo(() => {
|
||||
if (sorted.length === 0) return [];
|
||||
|
|
@ -396,11 +398,11 @@ const RequestLifecycle = ({ entries }: { entries: GuardrailInformation[] }) => {
|
|||
|
||||
// Pre-call guardrails — use modeMatches so array modes (e.g. ["pre_call", "post_call"])
|
||||
// place the entry in every matching bucket.
|
||||
const preCalls = sorted.filter((e) => modeMatches(e.guardrail_mode, "pre_call"));
|
||||
const postCalls = sorted.filter(
|
||||
(e) => modeMatches(e.guardrail_mode, "post_call") || modeMatches(e.guardrail_mode, "logging_only"),
|
||||
const preCalls = orderWithinPhase(sorted.filter((e) => modeMatches(e.guardrail_mode, "pre_call")));
|
||||
const postCalls = orderWithinPhase(
|
||||
sorted.filter((e) => modeMatches(e.guardrail_mode, "post_call") || modeMatches(e.guardrail_mode, "logging_only")),
|
||||
);
|
||||
const duringCalls = sorted.filter((e) => modeMatches(e.guardrail_mode, "during_call"));
|
||||
const duringCalls = orderWithinPhase(sorted.filter((e) => modeMatches(e.guardrail_mode, "during_call")));
|
||||
|
||||
for (const e of preCalls) {
|
||||
items.push({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue