mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +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 { StatusBadge, type StatusTone } from "@/components/shared/table_cells";
|
||||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
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 { 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 { ConclusionsPanel } from "./ConclusionsPanel";
|
||||||
import { ModelName } from "./LiveStrip";
|
import { ModelName } from "./LiveStrip";
|
||||||
import { NowReading } from "./NowReading";
|
import { NowReading } from "./NowReading";
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ export function LiveRun({
|
||||||
name: string;
|
name: string;
|
||||||
queue?: QueueContext;
|
queue?: QueueContext;
|
||||||
}) {
|
}) {
|
||||||
const live = job.status === "queued" || job.status === "running";
|
|
||||||
const reason = useQueueReason(job, queue);
|
const reason = useQueueReason(job, queue);
|
||||||
const [stripOpen, setStripOpen] = useStripOpen();
|
const [stripOpen, setStripOpen] = useStripOpen();
|
||||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import {
|
||||||
newestFirst,
|
newestFirst,
|
||||||
nowLine,
|
nowLine,
|
||||||
inFlight,
|
inFlight,
|
||||||
liveRows,
|
|
||||||
grownGroups,
|
grownGroups,
|
||||||
inGroup,
|
inGroup,
|
||||||
share,
|
share,
|
||||||
|
|
@ -19,12 +18,10 @@ import {
|
||||||
shortVerdict,
|
shortVerdict,
|
||||||
stripState,
|
stripState,
|
||||||
liveJob,
|
liveJob,
|
||||||
liveStats,
|
|
||||||
outcome,
|
outcome,
|
||||||
providerOf,
|
providerOf,
|
||||||
rateLabel,
|
|
||||||
secondsToFinishReading,
|
secondsToFinishReading,
|
||||||
tokenLabel,
|
polling,
|
||||||
} from "./live";
|
} from "./live";
|
||||||
import type { Job, Review } from "./types";
|
import type { Job, Review } from "./types";
|
||||||
|
|
||||||
|
|
@ -273,6 +270,13 @@ describe("incremental reviews", () => {
|
||||||
expect(feed.reviews[0].execution_id).toBe("r50");
|
expect(feed.reviews[0].execution_id).toBe("r50");
|
||||||
expect(feed.cursor).toBe(250);
|
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", () => {
|
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", () => {
|
describe("honest live list", () => {
|
||||||
it("lists completed reviews newest first in the order they finished, never re-sorted by time", () => {
|
it("lists completed reviews newest first in the order they finished, never re-sorted by time", () => {
|
||||||
const reviews = [
|
const reviews = [
|
||||||
|
|
@ -387,33 +324,11 @@ describe("honest live list", () => {
|
||||||
expect(nowLine(job(3, 0), 1)).toBe("3 done · 1 in flight");
|
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", () => {
|
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 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;
|
const job = (status: Job["status"]) => ({ status, reading: [item] }) as unknown as Job;
|
||||||
expect(inFlight(job("running"))).toEqual([item]);
|
expect(inFlight(job("running"))).toEqual([item]);
|
||||||
expect(inFlight(job("completed"))).toEqual([]);
|
expect(inFlight(job("completed"))).toEqual([]);
|
||||||
expect(inFlight({ status: "running" } as Job)).toEqual([]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sums up a finished run from when reading started", () => {
|
it("sums up a finished run from when reading started", () => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { durationText } from "./format";
|
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";
|
export type Outcome = "issue" | "clear" | "unknown";
|
||||||
|
|
||||||
|
|
@ -13,15 +13,8 @@ export interface Conclusion {
|
||||||
issue: boolean;
|
issue: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LiveStats {
|
|
||||||
perSecond: number | null;
|
|
||||||
tokens: number;
|
|
||||||
cost: number;
|
|
||||||
elapsedSeconds: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function liveJob(jobs: readonly Job[]): Job | undefined {
|
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;
|
if (active) return active;
|
||||||
const latest = jobs[0];
|
const latest = jobs[0];
|
||||||
return latest && latest.reviewed > 0 ? latest : undefined;
|
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 };
|
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[] {
|
export function unseen(reviews: readonly Review[], seen: ReadonlySet<string>): Review[] {
|
||||||
return reviews.filter((review) => !seen.has(reviewKey(review)));
|
return reviews.filter((review) => !seen.has(reviewKey(review)));
|
||||||
}
|
}
|
||||||
|
|
@ -185,53 +186,12 @@ export function secondsToFinishReading(
|
||||||
return Math.ceil(remaining / (job.reviewed / elapsed));
|
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[] {
|
export function newestFirst(reviews: readonly Review[], limit: number): Review[] {
|
||||||
return [...reviews].reverse().slice(0, limit);
|
return [...reviews].reverse().slice(0, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InFlight {
|
export function inFlight(job: Pick<Job, "status" | "reading">): readonly InFlight[] {
|
||||||
execution_id: string;
|
return job.status === "running" ? job.reading : [];
|
||||||
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 nowLine(job: Pick<Job, "coverage" | "reviewed">, reading: number): string {
|
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 Review = components["schemas"]["Review"];
|
||||||
|
|
||||||
|
export type InFlight = components["schemas"]["InFlight"];
|
||||||
|
|
||||||
export type ReviewVerdict = components["schemas"]["ReviewVerdict"];
|
export type ReviewVerdict = components["schemas"]["ReviewVerdict"];
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue