mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
refactor(lens): remove dead live helpers and use generated in-flight types
This commit is contained in:
parent
d58c7a2187
commit
e50f53965d
5 changed files with 24 additions and 148 deletions
|
|
@ -5,9 +5,9 @@ import { useState, type ReactNode } from "react";
|
|||
import { StatusBadge, type StatusTone } from "@/components/shared/table_cells";
|
||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
||||
|
||||
import { conclusions, type InFlight } from "../../model/live";
|
||||
import { conclusions } from "../../model/live";
|
||||
import { releasedReviews } from "../../model/stage";
|
||||
import type { Job, Review, Settings } from "../../model/types";
|
||||
import type { InFlight, Job, Review, Settings } from "../../model/types";
|
||||
import { ConclusionsPanel } from "./ConclusionsPanel";
|
||||
import { ModelName } from "./LiveStrip";
|
||||
import { NowReading } from "./NowReading";
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ export function LiveRun({
|
|||
name: string;
|
||||
queue?: QueueContext;
|
||||
}) {
|
||||
const live = job.status === "queued" || job.status === "running";
|
||||
const reason = useQueueReason(job, queue);
|
||||
const [stripOpen, setStripOpen] = useStripOpen();
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import {
|
|||
newestFirst,
|
||||
nowLine,
|
||||
inFlight,
|
||||
liveRows,
|
||||
grownGroups,
|
||||
inGroup,
|
||||
share,
|
||||
|
|
@ -19,12 +18,10 @@ import {
|
|||
shortVerdict,
|
||||
stripState,
|
||||
liveJob,
|
||||
liveStats,
|
||||
outcome,
|
||||
providerOf,
|
||||
rateLabel,
|
||||
secondsToFinishReading,
|
||||
tokenLabel,
|
||||
polling,
|
||||
} from "./live";
|
||||
import type { Job, Review } from "./types";
|
||||
|
||||
|
|
@ -273,6 +270,13 @@ describe("incremental reviews", () => {
|
|||
expect(feed.reviews[0].execution_id).toBe("r50");
|
||||
expect(feed.cursor).toBe(250);
|
||||
});
|
||||
|
||||
it("keeps polling a finished run until every review it reported has arrived", () => {
|
||||
const fetched = { reviews: [], cursor: 28 };
|
||||
expect(polling({ status: "running", reviewed: 28 }, fetched)).toBe(true);
|
||||
expect(polling({ status: "completed", reviewed: 30 }, fetched)).toBe(true);
|
||||
expect(polling({ status: "completed", reviewed: 30 }, { ...fetched, cursor: 30 })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("time left reading", () => {
|
||||
|
|
@ -301,73 +305,6 @@ describe("short verdict", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("live stats", () => {
|
||||
const job = {
|
||||
reviewed: 30,
|
||||
cost: 0.042,
|
||||
created_at: "2026-10-03T16:00:00Z",
|
||||
finished_at: null,
|
||||
steps: [
|
||||
{
|
||||
at: "2026-10-03T16:00:05Z",
|
||||
kind: "stage",
|
||||
label: "Reading executions",
|
||||
model: "",
|
||||
purpose: "",
|
||||
cost: 0,
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
},
|
||||
{
|
||||
at: "2026-10-03T16:00:06Z",
|
||||
kind: "model",
|
||||
label: "extract",
|
||||
model: "m",
|
||||
purpose: "extract",
|
||||
cost: 0.01,
|
||||
prompt_tokens: 1200,
|
||||
completion_tokens: 300,
|
||||
},
|
||||
{
|
||||
at: "2026-10-03T16:00:07Z",
|
||||
kind: "model",
|
||||
label: "extract",
|
||||
model: "m",
|
||||
purpose: "extract",
|
||||
cost: 0.01,
|
||||
prompt_tokens: 800,
|
||||
completion_tokens: 200,
|
||||
},
|
||||
],
|
||||
} as unknown as Job;
|
||||
|
||||
it("measures rate from when reading started and sums model tokens", () => {
|
||||
const stats = liveStats(job, Date.parse("2026-10-03T16:00:15Z"));
|
||||
expect(stats.elapsedSeconds).toBe(10);
|
||||
expect(stats.perSecond).toBe(3);
|
||||
expect(stats.tokens).toBe(2500);
|
||||
expect(stats.cost).toBe(0.042);
|
||||
});
|
||||
|
||||
it("stops the clock when the job finishes", () => {
|
||||
const finished = { ...job, finished_at: "2026-10-03T16:00:25Z" };
|
||||
expect(liveStats(finished, Date.parse("2026-10-03T18:00:00Z")).elapsedSeconds).toBe(20);
|
||||
});
|
||||
|
||||
it("has no rate before anything is reviewed", () => {
|
||||
expect(liveStats({ ...job, reviewed: 0 }, Date.parse("2026-10-03T16:00:15Z")).perSecond).toBeNull();
|
||||
});
|
||||
|
||||
it("formats rate and tokens for the footer", () => {
|
||||
expect(rateLabel(null)).toBe("–");
|
||||
expect(rateLabel(2.25)).toBe("2.3 traces/s");
|
||||
expect(rateLabel(0.5)).toBe("30.0 traces/min");
|
||||
expect(tokenLabel(950)).toBe("950 tok");
|
||||
expect(tokenLabel(2500)).toBe("2.5k tok");
|
||||
expect(tokenLabel(3_400_000)).toBe("3.4M tok");
|
||||
});
|
||||
});
|
||||
|
||||
describe("honest live list", () => {
|
||||
it("lists completed reviews newest first in the order they finished, never re-sorted by time", () => {
|
||||
const reviews = [
|
||||
|
|
@ -387,33 +324,11 @@ describe("honest live list", () => {
|
|||
expect(nowLine(job(3, 0), 1)).toBe("3 done · 1 in flight");
|
||||
});
|
||||
|
||||
it("keeps a trace as the same row from in flight to finished", () => {
|
||||
const reading = (id: string) => ({
|
||||
execution_id: id,
|
||||
trace_id: `t-${id}`,
|
||||
agent: "bot",
|
||||
started_at: "2026-10-03T16:00:00Z",
|
||||
});
|
||||
const before = liveRows([reading("x"), reading("y")], [review("a")], 10);
|
||||
expect(before.map((row) => [row.kind, row.key])).toEqual([
|
||||
["reading", "x"],
|
||||
["reading", "y"],
|
||||
["done", "a"],
|
||||
]);
|
||||
const after = liveRows([reading("x"), reading("y")], [review("a"), review("y")], 10);
|
||||
expect(after.map((row) => [row.kind, row.key])).toEqual([
|
||||
["reading", "x"],
|
||||
["done", "y"],
|
||||
["done", "a"],
|
||||
]);
|
||||
});
|
||||
|
||||
it("only shows in-flight traces while the job runs", () => {
|
||||
const item = { execution_id: "x", trace_id: "t", agent: "bot", started_at: "2026-10-03T16:00:00Z" };
|
||||
const job = (status: Job["status"]) => ({ status, reading: [item] }) as unknown as Job;
|
||||
expect(inFlight(job("running"))).toEqual([item]);
|
||||
expect(inFlight(job("completed"))).toEqual([]);
|
||||
expect(inFlight({ status: "running" } as Job)).toEqual([]);
|
||||
});
|
||||
|
||||
it("sums up a finished run from when reading started", () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { durationText } from "./format";
|
||||
import type { Job, Review, ReviewVerdict, Settings } from "./types";
|
||||
import type { InFlight, Job, Review, ReviewVerdict, Settings } from "./types";
|
||||
|
||||
export type Outcome = "issue" | "clear" | "unknown";
|
||||
|
||||
|
|
@ -13,15 +13,8 @@ export interface Conclusion {
|
|||
issue: boolean;
|
||||
}
|
||||
|
||||
export interface LiveStats {
|
||||
perSecond: number | null;
|
||||
tokens: number;
|
||||
cost: number;
|
||||
elapsedSeconds: number;
|
||||
}
|
||||
|
||||
export function liveJob(jobs: readonly Job[]): Job | undefined {
|
||||
const active = jobs.find((job) => job.status === "queued" || job.status === "running");
|
||||
const active = jobs.find(isActive);
|
||||
if (active) return active;
|
||||
const latest = jobs[0];
|
||||
return latest && latest.reviewed > 0 ? latest : undefined;
|
||||
|
|
@ -104,6 +97,14 @@ export function appendPage(feed: ReviewFeed, page: { reviews: readonly Review[];
|
|||
return { reviews: [...feed.reviews, ...added].slice(-KEPT_REVIEWS), cursor: page.reviewed };
|
||||
}
|
||||
|
||||
export function polling(job: Pick<Job, "status" | "reviewed">, feed: ReviewFeed): boolean {
|
||||
return isActive(job) || feed.cursor < job.reviewed;
|
||||
}
|
||||
|
||||
export function isActive(job: Pick<Job, "status">): boolean {
|
||||
return job.status === "queued" || job.status === "running";
|
||||
}
|
||||
|
||||
export function unseen(reviews: readonly Review[], seen: ReadonlySet<string>): Review[] {
|
||||
return reviews.filter((review) => !seen.has(reviewKey(review)));
|
||||
}
|
||||
|
|
@ -185,53 +186,12 @@ export function secondsToFinishReading(
|
|||
return Math.ceil(remaining / (job.reviewed / elapsed));
|
||||
}
|
||||
|
||||
export function liveStats(job: Job, now: number): LiveStats {
|
||||
const models = job.steps.filter((step) => step.kind === "model");
|
||||
const end = job.finished_at ? Date.parse(job.finished_at) : now;
|
||||
const elapsedSeconds = Math.max(0, Math.floor((end - Date.parse(readingStart(job))) / 1000));
|
||||
return {
|
||||
perSecond: elapsedSeconds > 0 && job.reviewed > 0 ? job.reviewed / elapsedSeconds : null,
|
||||
tokens: models.reduce((sum, step) => sum + step.prompt_tokens + step.completion_tokens, 0),
|
||||
cost: job.cost,
|
||||
elapsedSeconds,
|
||||
};
|
||||
}
|
||||
|
||||
export function rateLabel(perSecond: number | null): string {
|
||||
if (perSecond === null) return "–";
|
||||
return perSecond >= 1 ? `${perSecond.toFixed(1)} traces/s` : `${(perSecond * 60).toFixed(1)} traces/min`;
|
||||
}
|
||||
|
||||
export function tokenLabel(tokens: number): string {
|
||||
if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M tok`;
|
||||
return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k tok` : `${tokens} tok`;
|
||||
}
|
||||
|
||||
export function newestFirst(reviews: readonly Review[], limit: number): Review[] {
|
||||
return [...reviews].reverse().slice(0, limit);
|
||||
}
|
||||
|
||||
export interface InFlight {
|
||||
execution_id: string;
|
||||
trace_id: string;
|
||||
agent: string;
|
||||
started_at: string;
|
||||
}
|
||||
|
||||
export function inFlight(job: Job): readonly InFlight[] {
|
||||
const reading = (job as Job & { reading?: readonly InFlight[] }).reading;
|
||||
return job.status === "running" ? reading ?? [] : [];
|
||||
}
|
||||
|
||||
export type LiveRow = { kind: "reading"; key: string; item: InFlight } | { kind: "done"; key: string; review: Review };
|
||||
|
||||
export function liveRows(reading: readonly InFlight[], reviews: readonly Review[], limit: number): LiveRow[] {
|
||||
const finished = new Set(reviews.map((review) => review.execution_id));
|
||||
const open = reading.filter((item) => !finished.has(item.execution_id));
|
||||
return [
|
||||
...open.map((item) => ({ kind: "reading" as const, key: item.execution_id, item })),
|
||||
...newestFirst(reviews, limit).map((review) => ({ kind: "done" as const, key: review.execution_id, review })),
|
||||
];
|
||||
export function inFlight(job: Pick<Job, "status" | "reading">): readonly InFlight[] {
|
||||
return job.status === "running" ? job.reading : [];
|
||||
}
|
||||
|
||||
export function nowLine(job: Pick<Job, "coverage" | "reviewed">, reading: number): string {
|
||||
|
|
|
|||
|
|
@ -35,4 +35,6 @@ export type Worker = LensList["workers"][number];
|
|||
|
||||
export type Review = components["schemas"]["Review"];
|
||||
|
||||
export type InFlight = components["schemas"]["InFlight"];
|
||||
|
||||
export type ReviewVerdict = components["schemas"]["ReviewVerdict"];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue