mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
fix(insights): let "Re-run analysis" reach a benched provider
The on-demand briefing (POST /api/insights/generate) runs through the briefing chokepoint, which named its chain walk "job" for the background share of the day's ceiling. Since background work leaves out a provider the health ledger benched, a person pressing "Re-run analysis" got no request at all: the call failed with a connection refusal that never happened, and a refused credential was never reported back. The chain runners now take the walk apart from the budget surface: a call can be rationed as a job and still have a person waiting on it (interactive). runBriefingCompletion takes the flag and the on-demand route's calls (the first pass and the grounding retry) pass it, so they keep the job share and walk a benched provider at the tail, as background-bench promises for interactive walks. Every other job-surface caller is a worker or is enqueued; the status, narrative and warm routes only read and queue, and document reads and lab scans resolve a single provider without a chain. Refs #1279
This commit is contained in:
1 parent
b90980f2a2
commit
bdfcea5e7c
7 files changed
+110
-42
No files matched your search
@@ -71,6 +71,9 @@ vi.mock("@/lib/db", () => ({
|
||||
consentReceipt: {
|
||||
findFirst: vi.fn(async () => ({ id: "receipt-1" })),
|
||||
},
|
||||
// The provider health ledger the chain walk reads. Nothing benched by
|
||||
// default; the bench case below seeds a row.
|
||||
providerHealth: { findMany: vi.fn(async () => []) },
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -284,6 +287,35 @@ describe("POST /api/insights/generate — daily token ceiling", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/insights/generate — a provider the health ledger benched", () => {
|
||||
// #1279 — "Re-run analysis" is a person waiting on the answer. Background
|
||||
// work leaves a benched provider out; this call walks it at the tail of
|
||||
// the chain, so it reaches the provider and reports what the provider
|
||||
// said, not a connection failure that never happened.
|
||||
it("still asks the benched provider and reports its own refusal", async () => {
|
||||
vi.mocked(prisma.providerHealth.findMany).mockResolvedValueOnce([
|
||||
{
|
||||
providerType: "admin-openai",
|
||||
lastResult: "auth_failed",
|
||||
consecutiveFailures: 1,
|
||||
nextRetryAt: new Date(Date.now() + 60 * 60 * 1000),
|
||||
},
|
||||
] as never);
|
||||
const err = Object.assign(new Error("OpenAI request failed (401)"), {
|
||||
httpStatus: 401,
|
||||
});
|
||||
makeProviderThatThrows(err);
|
||||
const provider = await vi.mocked(resolveProvider)("u-1");
|
||||
|
||||
const res = await POST(jsonRequest({ force: true }) as never);
|
||||
|
||||
expect(provider.generateCompletion).toHaveBeenCalledTimes(1);
|
||||
expect(res.status).toBe(422);
|
||||
const body = (await res.json()) as ApiErrorEnvelope;
|
||||
expect(body.error).toMatch(/AI provider rejected/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/insights/generate — provider error mapping", () => {
|
||||
it("rejects a body over the 16 KB cap with 413 before parsing", async () => {
|
||||
const res = await POST(
|
||||
|
||||
Reference in new issue
Block a user