mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 14:40:52 +00:00
fix(ai): background generation cannot spend the interactive day
The automatic generators — status and reference cards, the briefing, the
arrival reaction, the nudge, document summaries and the index backfill —
reserved against the same ceiling as the chat. On the operator's account
that meant 150-330 ledger rows a day, 172 metric generations before
06:42Z, and a ceiling already spent when he opened the chat.
Every background surface now reserves through
`resolveDailyCapFor("job", chain)`: half the cost owner's ceiling, all
job surfaces together. The interactive routes keep the whole ceiling, so
a day of background work can no longer take the user's own away.
This commit is contained in:
1 parent
2c46712e13
commit
d3196ee69e
38 files changed
+151
-17
No files matched your search
@@ -32,6 +32,7 @@ vi.mock("@/lib/ai/coach/budget", () => ({
|
||||
buildDateKey: vi.fn(() => "2026-07-16"),
|
||||
reserveBudget: vi.fn(),
|
||||
reconcileSpend: vi.fn().mockResolvedValue(undefined),
|
||||
resolveDailyCapFor: vi.fn(() => 100_000),
|
||||
}));
|
||||
vi.mock("@/lib/ai/ai-budgets", () => ({
|
||||
AI_BUDGETS: { ocrExtractText: { temperature: 0, maxTokens: 2000 } },
|
||||
@@ -99,6 +100,8 @@ beforeEach(() => {
|
||||
allowed: true,
|
||||
reserved: 10,
|
||||
totalAfter: 10,
|
||||
owner: "operator",
|
||||
operatorAfter: 10,
|
||||
} as Awaited<ReturnType<typeof reserveBudget>>);
|
||||
mockExtract.mockResolvedValue({
|
||||
reportDate: "2026-07-10",
|
||||
|
||||
@@ -29,6 +29,7 @@ vi.mock("@/lib/ai/coach/budget", () => ({
|
||||
reserveBudget: vi.fn().mockResolvedValue({ allowed: true, reserved: 600 }),
|
||||
reconcileSpend: vi.fn().mockResolvedValue(undefined),
|
||||
resolveDailyCap: vi.fn(() => 100000),
|
||||
resolveDailyCapFor: vi.fn(() => 100000),
|
||||
resolveCostOwner: vi.fn(() => "operator" as const),
|
||||
}));
|
||||
vi.mock("@/lib/ai/provider-runner", () => ({
|
||||
|
||||
@@ -45,6 +45,7 @@ vi.mock("@/lib/ai/coach/budget", () => ({
|
||||
reserveBudget: vi.fn(),
|
||||
reconcileSpend: vi.fn().mockResolvedValue(undefined),
|
||||
resolveDailyCap: vi.fn(() => 1000),
|
||||
resolveDailyCapFor: vi.fn(() => 1000),
|
||||
resolveCostOwner: vi.fn(() => "operator" as const),
|
||||
}));
|
||||
vi.mock("@/lib/ai/ai-budgets", () => ({
|
||||
|
||||
@@ -56,6 +56,7 @@ vi.mock("@/lib/ai/coach/budget", () => ({
|
||||
reserveBudget: vi.fn(),
|
||||
reconcileSpend: vi.fn().mockResolvedValue(undefined),
|
||||
resolveDailyCap: vi.fn(() => 1000),
|
||||
resolveDailyCapFor: vi.fn(() => 1000),
|
||||
resolveCostOwner: vi.fn(() => "operator" as const),
|
||||
}));
|
||||
vi.mock("@/lib/ai/ai-budgets", () => ({
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
buildDateKey,
|
||||
reconcileSpend,
|
||||
reserveBudget,
|
||||
resolveDailyCapFor,
|
||||
} from "@/lib/ai/coach/budget";
|
||||
import { loadDocumentChatText } from "@/lib/documents/content-index";
|
||||
import { resolveIndexProvider } from "@/lib/documents/index-document";
|
||||
@@ -276,13 +277,17 @@ export async function maybeAutoStageLabFacts(
|
||||
}
|
||||
|
||||
// A text-structuring pass, not a vision call — reserve the proportionate
|
||||
// ceiling under the same daily cap the auto-index resolved.
|
||||
// ceiling under the cost owner the auto-index resolved.
|
||||
// v1.38.19 (Wave E) — auto-staging runs off the upload, with no one waiting
|
||||
// on it: a background surface, so half the day's ceiling.
|
||||
const dateKey = buildDateKey();
|
||||
const reservation = await reserveBudget(
|
||||
userId,
|
||||
AI_BUDGETS.ocrExtractText.maxTokens,
|
||||
dateKey,
|
||||
provider.dailyCap,
|
||||
resolveDailyCapFor("job", [
|
||||
{ providerType: provider.pick.entry.providerType },
|
||||
]),
|
||||
provider.costOwner,
|
||||
);
|
||||
if (!reservation.allowed) return { staged: false, reason: "budget" };
|
||||
|
||||
Reference in new issue
Block a user