| 1 | /** |
| 2 | * Effort and suggestions, as the pages say them (components/effort.tsx). |
| 3 | * Pure, so it is tested on its own. Every cost |
| 4 | * shown comes from the agents service's measured sessions; a level an |
| 5 | * agent has not run at says so instead of a number. |
| 6 | */ |
| 7 | import type { AgentEffort, AgentEffortCosts, AgentRecommendation, AgentRecommendations, EffortCost, EffortLevel } from "@g1t/contracts"; |
| 8 | |
| 9 | /** Dollars as lib/usage.ts `money` writes them; here so this file imports nothing at run time. */ |
| 10 | function money(micros: number): string { |
| 11 | const d = Math.abs(micros) / 1_000_000; |
| 12 | const digits = d < 0.0001 || d >= 0.01 ? 2 : d >= 0.001 ? 3 : 4; |
| 13 | return `${micros < 0 ? "−" : ""}$${d.toLocaleString("en-US", { minimumFractionDigits: digits, maximumFractionDigits: digits })}`; |
| 14 | } |
| 15 | |
| 16 | export const EFFORT_OPTIONS: { key: AgentEffort; label: string; about: string }[] = [ |
| 17 | { key: "auto", label: "Auto", about: "Picks per piece of work: medium, and high on a session someone has had to steer." }, |
| 18 | { key: "low", label: "Low", about: "The fast tier, light reasoning, up to 5 steps a session. For routine work." }, |
| 19 | { key: "medium", label: "Medium", about: "Starts where the work would, moderate reasoning, up to 8 steps a session." }, |
| 20 | { key: "high", label: "High", about: "The standard tier or above, deep reasoning, up to 10 steps a session." }, |
| 21 | { key: "max", label: "Max", about: "The most capable tier, the most reasoning, up to 12 steps a session." }, |
| 22 | ]; |
| 23 | |
| 24 | export function effortLabel(effort: AgentEffort | null | undefined): string { |
| 25 | return EFFORT_OPTIONS.find((o) => o.key === effort)?.label ?? "Auto"; |
| 26 | } |
| 27 | |
| 28 | /** An agent's setting from its routing; none is Auto. */ |
| 29 | export function effortSetting(routing: { effort?: AgentEffort } | null | undefined): AgentEffort { |
| 30 | const value = routing?.effort; |
| 31 | return EFFORT_OPTIONS.some((o) => o.key === value) ? (value as AgentEffort) : "auto"; |
| 32 | } |
| 33 | |
| 34 | /** One level's measured cost, or null when the agent has not run at it. */ |
| 35 | export function costAt(costs: AgentEffortCosts | null, level: EffortLevel): EffortCost | null { |
| 36 | const found = costs?.levels.find((l) => l.effort === level); |
| 37 | return found && found.sessions > 0 && found.typical_micros != null ? found : null; |
| 38 | } |
| 39 | |
| 40 | /** What a setting has cost per typical task: "$0.42 a typical task, from 14 sessions", or why there is no figure. */ |
| 41 | export function costLine(costs: AgentEffortCosts | null, effort: AgentEffort): string { |
| 42 | if (!costs) return "Costs couldn't be read right now."; |
| 43 | // Auto runs as medium, and high once steered: its figure is medium's. |
| 44 | const level: EffortLevel = effort === "auto" ? "medium" : effort; |
| 45 | const at = costAt(costs, level); |
| 46 | const days = `in the last ${costs.window_days} days`; |
| 47 | if (!at) return effort === "auto" ? `No sessions at Medium ${days}, so no figure yet.` : `No sessions at ${effortLabel(effort)} ${days}, so no figure yet.`; |
| 48 | const sessions = `${at.sessions.toLocaleString("en-US")} session${at.sessions === 1 ? "" : "s"}`; |
| 49 | return `${money(at.typical_micros!)} a typical task${effort === "auto" ? " at Medium" : ""}, from ${sessions} ${days}`; |
| 50 | } |
| 51 | |
| 52 | /** "92%" of sessions finished with nobody stepping in, or empty. */ |
| 53 | export function acceptedLabel(cost: EffortCost | null): string { |
| 54 | return cost?.accepted_share != null ? `${Math.round(cost.accepted_share * 100)}%` : ""; |
| 55 | } |
| 56 | |
| 57 | /** What the open suggestions together would save a month. */ |
| 58 | export function totalSaving(recs: AgentRecommendations | null): number { |
| 59 | return (recs?.open ?? []).reduce((n, r) => n + (r.saving_month_micros ?? 0), 0); |
| 60 | } |
| 61 | |
| 62 | /** "About $31 a month" from a suggestion's measured saving. */ |
| 63 | export function savingLabel(rec: Pick<AgentRecommendation, "saving_month_micros">): string { |
| 64 | return rec.saving_month_micros != null && rec.saving_month_micros > 0 ? `About ${money(rec.saving_month_micros)} a month` : ""; |
| 65 | } |