address greptile review: per-provider deficit notch, startedAt filter, weekRange refresh, deduplicate providerDisplayName

This commit is contained in:
Sai Shankar
2026-03-08 03:35:23 +05:30
committed by Dotta
parent 94018e0239
commit 82bc00a3ae
4 changed files with 36 additions and 42 deletions

View File

@@ -172,8 +172,8 @@ export function costService(db: Db) {
.orderBy(desc(sql`coalesce(sum(${costEvents.costCents}), 0)::int`)); .orderBy(desc(sql`coalesce(sum(${costEvents.costCents}), 0)::int`));
const runConditions: ReturnType<typeof eq>[] = [eq(heartbeatRuns.companyId, companyId)]; const runConditions: ReturnType<typeof eq>[] = [eq(heartbeatRuns.companyId, companyId)];
if (range?.from) runConditions.push(gte(heartbeatRuns.finishedAt, range.from)); if (range?.from) runConditions.push(gte(heartbeatRuns.startedAt, range.from));
if (range?.to) runConditions.push(lte(heartbeatRuns.finishedAt, range.to)); if (range?.to) runConditions.push(lte(heartbeatRuns.startedAt, range.to));
const runRows = await db const runRows = await db
.select({ .select({

View File

@@ -1,7 +1,7 @@
import type { CostByProviderModel, CostWindowSpendRow } from "@paperclipai/shared"; import type { CostByProviderModel, CostWindowSpendRow } from "@paperclipai/shared";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import { QuotaBar } from "./QuotaBar"; import { QuotaBar } from "./QuotaBar";
import { formatCents, formatTokens } from "@/lib/utils"; import { formatCents, formatTokens, providerDisplayName } from "@/lib/utils";
interface ProviderQuotaCardProps { interface ProviderQuotaCardProps {
provider: string; provider: string;
@@ -17,17 +17,6 @@ interface ProviderQuotaCardProps {
showDeficitNotch: boolean; showDeficitNotch: boolean;
} }
function providerLabel(provider: string): string {
const map: Record<string, string> = {
anthropic: "Anthropic",
openai: "OpenAI",
google: "Google",
cursor: "Cursor",
jetbrains: "JetBrains AI",
};
return map[provider.toLowerCase()] ?? provider;
}
export function ProviderQuotaCard({ export function ProviderQuotaCard({
provider, provider,
rows, rows,
@@ -76,7 +65,7 @@ export function ProviderQuotaCard({
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="min-w-0"> <div className="min-w-0">
<CardTitle className="text-sm font-semibold"> <CardTitle className="text-sm font-semibold">
{providerLabel(provider)} {providerDisplayName(provider)}
</CardTitle> </CardTitle>
<CardDescription className="text-xs mt-0.5"> <CardDescription className="text-xs mt-0.5">
<span className="font-mono">{formatTokens(totalInputTokens)}</span> in <span className="font-mono">{formatTokens(totalInputTokens)}</span> in

View File

@@ -48,6 +48,18 @@ export function formatTokens(n: number): string {
return String(n); return String(n);
} }
/** Map a raw provider slug to a display-friendly name. */
export function providerDisplayName(provider: string): string {
const map: Record<string, string> = {
anthropic: "Anthropic",
openai: "OpenAI",
google: "Google",
cursor: "Cursor",
jetbrains: "JetBrains AI",
};
return map[provider.toLowerCase()] ?? provider;
}
/** Build an issue URL using the human-readable identifier when available. */ /** Build an issue URL using the human-readable identifier when available. */
export function issueUrl(issue: { id: string; identifier?: string | null }): string { export function issueUrl(issue: { id: string; identifier?: string | null }): string {
return `/issues/${issue.identifier ?? issue.id}`; return `/issues/${issue.identifier ?? issue.id}`;

View File

@@ -9,7 +9,7 @@ import { EmptyState } from "../components/EmptyState";
import { PageSkeleton } from "../components/PageSkeleton"; import { PageSkeleton } from "../components/PageSkeleton";
import { ProviderQuotaCard } from "../components/ProviderQuotaCard"; import { ProviderQuotaCard } from "../components/ProviderQuotaCard";
import { PageTabBar } from "../components/PageTabBar"; import { PageTabBar } from "../components/PageTabBar";
import { formatCents, formatTokens } from "../lib/utils"; import { formatCents, formatTokens, providerDisplayName } from "../lib/utils";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Tabs, TabsContent } from "@/components/ui/tabs"; import { Tabs, TabsContent } from "@/components/ui/tabs";
import { Gauge } from "lucide-react"; import { Gauge } from "lucide-react";
@@ -52,17 +52,6 @@ function computeRange(preset: DatePreset): { from: string; to: string } {
} }
} }
function providerDisplayName(provider: string): string {
const map: Record<string, string> = {
anthropic: "Anthropic",
openai: "OpenAI",
google: "Google",
cursor: "Cursor",
jetbrains: "JetBrains AI",
};
return map[provider.toLowerCase()] ?? provider;
}
/** current week mon-sun boundaries as iso strings */ /** current week mon-sun boundaries as iso strings */
function currentWeekRange(): { from: string; to: string } { function currentWeekRange(): { from: string; to: string } {
const now = new Date(); const now = new Date();
@@ -121,7 +110,9 @@ export function Usage() {
return range; return range;
}, [preset, customFrom, customTo]); }, [preset, customFrom, customTo]);
const weekRange = useMemo(() => currentWeekRange(), []); // key to today's date string so the range auto-refreshes after midnight on the next 30s refetch
const today = new Date().toDateString();
const weekRange = useMemo(() => currentWeekRange(), [today]);
// for custom preset, only fetch once both dates are selected // for custom preset, only fetch once both dates are selected
const customReady = preset !== "custom" || (!!customFrom && !!customTo); const customReady = preset !== "custom" || (!!customFrom && !!customTo);
@@ -190,21 +181,23 @@ export function Usage() {
return map; return map;
}, [windowData]); }, [windowData]);
// deficit notch: projected spend exceeds remaining budget — only meaningful for mtd preset // compute deficit notch per provider: only meaningful for mtd — projects spend to month end
// (other presets use a different date range than the monthly budget, so the projection is nonsensical) // and flags when that projection exceeds the provider's pro-rata budget share.
const showDeficitNotch = useMemo(() => { function providerDeficitNotch(providerKey: string): boolean {
if (preset !== "mtd") return false; if (preset !== "mtd") return false;
const budget = summary?.budgetCents ?? 0; const budget = summary?.budgetCents ?? 0;
if (budget <= 0) return false; if (budget <= 0) return false;
const spend = summary?.spendCents ?? 0; const totalSpend = summary?.spendCents ?? 0;
const today = new Date(); const providerCostCents = (byProvider.get(providerKey) ?? []).reduce((s, r) => s + r.costCents, 0);
const daysElapsed = today.getDate(); const providerShare = totalSpend > 0 ? providerCostCents / totalSpend : 0;
const daysInMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate(); const providerBudget = budget * providerShare;
const daysRemaining = daysInMonth - daysElapsed; if (providerBudget <= 0) return false;
const burnRatePerDay = spend / Math.max(daysElapsed, 1); const now = new Date();
const projected = spend + burnRatePerDay * daysRemaining; const daysElapsed = now.getDate();
return projected > budget; const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
}, [summary, preset]); const burnRate = providerCostCents / Math.max(daysElapsed, 1);
return providerCostCents + burnRate * (daysInMonth - daysElapsed) > providerBudget;
}
const providers = Array.from(byProvider.keys()); const providers = Array.from(byProvider.keys());
@@ -298,7 +291,7 @@ export function Usage() {
totalCompanySpendCents={summary?.spendCents ?? 0} totalCompanySpendCents={summary?.spendCents ?? 0}
weekSpendCents={weekSpendByProvider.get(p) ?? 0} weekSpendCents={weekSpendByProvider.get(p) ?? 0}
windowRows={windowSpendByProvider.get(p) ?? []} windowRows={windowSpendByProvider.get(p) ?? []}
showDeficitNotch={showDeficitNotch} showDeficitNotch={providerDeficitNotch(p)}
/> />
))} ))}
</div> </div>
@@ -314,7 +307,7 @@ export function Usage() {
totalCompanySpendCents={summary?.spendCents ?? 0} totalCompanySpendCents={summary?.spendCents ?? 0}
weekSpendCents={weekSpendByProvider.get(p) ?? 0} weekSpendCents={weekSpendByProvider.get(p) ?? 0}
windowRows={windowSpendByProvider.get(p) ?? []} windowRows={windowSpendByProvider.get(p) ?? []}
showDeficitNotch={showDeficitNotch} showDeficitNotch={providerDeficitNotch(p)}
/> />
</TabsContent> </TabsContent>
))} ))}