Studio: harden the profile stats aggregation
Four fixes from review: - _as_float raised OverflowError on a JSON integer wider than float, so one oversized counter returned 500 for the whole panel. It degrades to zero now, like every other unreadable field. - Fork dedup elected one winner per source thread, but fork_chat_thread copies a single parent_id branch, so sibling forks of a retry and a regeneration hold different rows. Electing per original message keeps each branch when the source is deleted. - create_run claims a resume source before the continuation logs its first step, so a continuation that failed early took the source's completed steps and tokens with it. Supersession now needs the continuation to have reached the source's step. - Cumulative activity is a running total over the displayed window, but a narrow card trims older weeks without rebasing, opening the first visible bar at the hidden total and flattening the rest.
This commit is contained in:
parent
8644b4c208
commit
df7300a3e1
5 changed files with 218 additions and 27 deletions
|
|
@ -12,6 +12,7 @@ import {
|
|||
heatLevel,
|
||||
parseDayKey,
|
||||
seriesForMode,
|
||||
windowBaseline,
|
||||
} from "../../utils/stats-format";
|
||||
import { StatsCard } from "./stat-primitives";
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ function buildColumns(
|
|||
daily: ProfileStatsDay[],
|
||||
values: number[],
|
||||
columns: number,
|
||||
mode: ActivityMode,
|
||||
): Cell[][] {
|
||||
if (daily.length === 0 || columns <= 0) return [];
|
||||
|
||||
|
|
@ -52,6 +54,9 @@ function buildColumns(
|
|||
const capacity = columns * DAYS_PER_WEEK - trailing;
|
||||
const start = Math.max(0, daily.length - capacity);
|
||||
const visible = daily.slice(start);
|
||||
// Cumulative is a running total over what the grid shows, so a narrow card
|
||||
// that drops older weeks has to rebase off the last hidden day.
|
||||
const baseline = windowBaseline(values, start, mode);
|
||||
|
||||
const cells: Cell[] = [];
|
||||
// Pad so every column is a Monday-started week.
|
||||
|
|
@ -62,7 +67,11 @@ function buildColumns(
|
|||
cells.push({ key: `pad-${index}`, day: null, value: 0 });
|
||||
}
|
||||
for (const [index, day] of visible.entries()) {
|
||||
cells.push({ key: day.date, day, value: values[start + index] ?? 0 });
|
||||
cells.push({
|
||||
key: day.date,
|
||||
day,
|
||||
value: (values[start + index] ?? 0) - baseline,
|
||||
});
|
||||
}
|
||||
|
||||
const grid: Cell[][] = [];
|
||||
|
|
@ -245,8 +254,8 @@ export function TokenActivityCard({ daily }: { daily: ProfileStatsDay[] }) {
|
|||
const shaded = mode === "daily";
|
||||
const values = useMemo(() => seriesForMode(daily, mode), [daily, mode]);
|
||||
const grid = useMemo(
|
||||
() => buildColumns(daily, values, columns),
|
||||
[daily, values, columns],
|
||||
() => buildColumns(daily, values, columns, mode),
|
||||
[daily, values, columns, mode],
|
||||
);
|
||||
// The app language, not the browser's: those differ whenever the user picks
|
||||
// a language in Settings, and it has to be a dependency so switching while
|
||||
|
|
|
|||
|
|
@ -103,6 +103,20 @@ export type ActivityMode = "daily" | "weekly" | "cumulative";
|
|||
* since the backend caps the series and seeding it with everything older would
|
||||
* flatten every bar against a baseline the grid cannot show.
|
||||
*/
|
||||
/**
|
||||
* What to subtract from a cumulative series once the grid drops older days.
|
||||
* Without it the first visible bar opens at the hidden total and the whole
|
||||
* window flattens against a baseline the user cannot see.
|
||||
*/
|
||||
export function windowBaseline(
|
||||
values: number[],
|
||||
start: number,
|
||||
mode: ActivityMode,
|
||||
): number {
|
||||
if (mode !== "cumulative" || start <= 0) return 0;
|
||||
return values[start - 1] ?? 0;
|
||||
}
|
||||
|
||||
export function seriesForMode(
|
||||
daily: Array<{ date: string; tokens: number }>,
|
||||
mode: ActivityMode,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue