feat: meter AI usage
CI and Deploy / test (push) Failing after 58s
CI and Deploy / deploy (push) Has been skipped

This commit is contained in:
cesnimda
2026-07-30 22:39:24 +02:00
parent f8466c2ebc
commit c08232b9d7
15 changed files with 2482 additions and 11 deletions
@@ -14,7 +14,7 @@ import ReplayIcon from "@mui/icons-material/Replay";
import { getApiErrorMessage } from "../api";
import { useToast } from "../toast";
import Markdown from "./Markdown";
import { AI_MODULES, AiInteraction, COVER_LETTER_MODES, aiWorkspaceApi } from "../aiWorkspace";
import { AI_MODULES, AiInteraction, AiUsage, COVER_LETTER_MODES, aiWorkspaceApi } from "../aiWorkspace";
// Phase 5 — the central AI Workspace for one job application. Every result is a suggestion the user
// reviews and copies; nothing is applied automatically.
@@ -24,6 +24,7 @@ export default function AiWorkspacePanel({ jobId }: { jobId: number }) {
const [mode, setMode] = useState("professional");
const [extra, setExtra] = useState("");
const [provider, setProvider] = useState("");
const [usage, setUsage] = useState<AiUsage | null>(null);
const [busy, setBusy] = useState(false);
const [current, setCurrent] = useState<AiInteraction | null>(null);
const [history, setHistory] = useState<AiInteraction[]>([]);
@@ -44,6 +45,7 @@ export default function AiWorkspacePanel({ jobId }: { jobId: number }) {
useEffect(() => {
aiWorkspaceApi.modules(jobId).then((r) => setProvider(r.provider)).catch(() => undefined);
aiWorkspaceApi.usage().then(setUsage).catch(() => undefined);
loadHistory();
}, [jobId, loadHistory]);
@@ -54,6 +56,7 @@ export default function AiWorkspacePanel({ jobId }: { jobId: number }) {
const res = await aiWorkspaceApi.generate(jobId, { module, mode: module === "cover-letter" ? mode : undefined, extraContext: extra || undefined });
setCurrent(res);
setHistory((h) => [res, ...h]);
aiWorkspaceApi.usage().then(setUsage).catch(() => undefined);
} catch (err) {
toast(getApiErrorMessage(err, "AI generation failed."), "error");
} finally {
@@ -85,6 +88,7 @@ export default function AiWorkspacePanel({ jobId }: { jobId: number }) {
<Alert severity="info" sx={{ py: 0.5 }}>
AI suggestions never change your profile, CVs, or this application. Review, then copy what you want to keep.
{provider && <> Provider: <strong>{provider}</strong>.</>}
{usage && <> This month: <strong>{usage.currentMonth.calls}</strong> runs · approximately <strong>{usage.currentMonth.estimatedTokens.toLocaleString()}</strong> tokens.</>}
</Alert>
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 1 }}>