mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 15:02:04 +00:00
feat(insights): let the usage level decide the background AI work
The level now sets, per record: the status-card regeneration gap after a sync (frugal 24 h, standard the operator's gap, full 1 h) and after a hand entry (frugal 6 h, otherwise 1 h); the status snapshot cap (10 000 compact characters on frugal, 14 000 above); background reasoning for the briefing and the period narratives (off on frugal); the daily briefing's form (a short form on frugal that keeps every field and at most one recommendation, the full contract on standard and full); and whether the automatic document read may act (frugal reads on request only, the stored switch is kept). A deterministic simulation of a watch-synced day through the invalidator counts 122 ingest-driven status generations on full (the v1.42 behaviour), 25 on standard and 2 on frugal, plus the nightly warm on each. Refs #1217
This commit is contained in:
1 parent
d3770b9010
commit
74dcc67ef7
23 files changed
+840
-83
No files matched your search
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* #1217 — the automatic document read acts only while both the person's
|
||||
* switch is on and their AI usage level allows it. On `frugal` documents are
|
||||
* read on request only; the stored switch is left alone.
|
||||
*/
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const db = vi.hoisted(() => ({
|
||||
user: { documentsAutoAiRead: true, aiUsageLevel: null as string | null },
|
||||
settings: {
|
||||
aiUsageMaxLevel: "FULL",
|
||||
aiUsageDefaultLevel: "STANDARD",
|
||||
} as Record<string, unknown> | null,
|
||||
failSettings: false,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/db", () => ({
|
||||
prisma: {
|
||||
user: { findUnique: vi.fn(async () => db.user) },
|
||||
appSettings: {
|
||||
findUnique: vi.fn(async () => {
|
||||
if (db.failSettings) throw new Error("db down");
|
||||
return db.settings;
|
||||
}),
|
||||
},
|
||||
},
|
||||
}));
|
||||
vi.mock("@/lib/logging/context", () => ({
|
||||
annotate: vi.fn(),
|
||||
getEvent: () => undefined,
|
||||
}));
|
||||
|
||||
import { documentAutoReadEnabled } from "../document-settings";
|
||||
|
||||
beforeEach(() => {
|
||||
db.user = { documentsAutoAiRead: true, aiUsageLevel: null };
|
||||
db.settings = { aiUsageMaxLevel: "FULL", aiUsageDefaultLevel: "STANDARD" };
|
||||
db.failSettings = false;
|
||||
});
|
||||
|
||||
describe("documentAutoReadEnabled", () => {
|
||||
it.each([
|
||||
["FRUGAL", false],
|
||||
["STANDARD", true],
|
||||
["FULL", true],
|
||||
[null, true],
|
||||
])("level %s with the switch on reads %s", async (level, expected) => {
|
||||
db.user = { documentsAutoAiRead: true, aiUsageLevel: level };
|
||||
expect(await documentAutoReadEnabled("u1")).toBe(expected);
|
||||
});
|
||||
|
||||
it("stays off with the switch off, whatever the level", async () => {
|
||||
db.user = { documentsAutoAiRead: false, aiUsageLevel: "FULL" };
|
||||
expect(await documentAutoReadEnabled("u1")).toBe(false);
|
||||
});
|
||||
|
||||
it("rests on a frugal instance default", async () => {
|
||||
db.settings = {
|
||||
aiUsageMaxLevel: "STANDARD",
|
||||
aiUsageDefaultLevel: "FRUGAL",
|
||||
};
|
||||
expect(await documentAutoReadEnabled("u1")).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -12,13 +12,21 @@
|
||||
* - the summary job and its catch-up pass run only while it is ON;
|
||||
* - the auto-index-on-upload job stays strictly local when it is OFF and uses
|
||||
* the document-order external pick when it is ON.
|
||||
*
|
||||
* v1.43 (#1217) — on the `frugal` AI usage level the setting rests: documents
|
||||
* are read on request only, whatever the switch says, and the switch acts
|
||||
* again as soon as the level allows it. The stored choice is never changed.
|
||||
*/
|
||||
import { prisma } from "@/lib/db";
|
||||
import { loadRecordUsageLevel } from "@/lib/ai/capabilities/usage";
|
||||
import { AI_USAGE_LEVEL_EFFECTS } from "@/lib/ai/usage-level";
|
||||
|
||||
/**
|
||||
* True when the user has opted into automatic AI reading of uploaded documents.
|
||||
* A missing row (deleted account raced against a job) resolves to `false` —
|
||||
* fail-closed to the local-first posture, never to external egress.
|
||||
* True when the user has opted into automatic AI reading of uploaded documents
|
||||
* and their AI usage level lets the opt-in act. A missing row (deleted account
|
||||
* raced against a job) resolves to `false` — fail-closed to the local-first
|
||||
* posture, never to external egress; so does a usage level that cannot be
|
||||
* read.
|
||||
*/
|
||||
export async function documentAutoReadEnabled(
|
||||
userId: string,
|
||||
@@ -27,5 +35,11 @@ export async function documentAutoReadEnabled(
|
||||
where: { id: userId },
|
||||
select: { documentsAutoAiRead: true },
|
||||
});
|
||||
return row?.documentsAutoAiRead ?? false;
|
||||
if (!(row?.documentsAutoAiRead ?? false)) return false;
|
||||
try {
|
||||
const level = await loadRecordUsageLevel(userId);
|
||||
return AI_USAGE_LEVEL_EFFECTS[level].documentsAutoRead;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user