mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(ai-usage): never regenerate more often on frugal than on standard
The frugal sync gap is the larger of a day and the operator's sync gap, so an instance gap above 24 hours no longer leaves frugal ahead of standard.
This commit is contained in:
1 parent
b99b70127d
commit
120b0966b1
5 files changed
+22
-13
No files matched your search
@@ -81006,11 +81006,12 @@ components:
|
||||
- standard
|
||||
- full
|
||||
description: "How much background AI work an account asks for, in ceiling order. `frugal`: status cards regenerate at
|
||||
most every 24 h after a sync and every 6 h after a hand entry, a smaller snapshot, no background reasoning, a
|
||||
short daily briefing (`recommendations` holds at most one entry, every field stays present), no automatic
|
||||
document reading, no supplement label reading. `standard`: the operator's sync gap (default 6 h), 1 h after a
|
||||
hand entry, background reasoning at the job default. `full`: 1 h after any ingest (the behaviour before v1.43).
|
||||
The interactive Coach is the same at every level."
|
||||
most every 24 h after a sync (or the operator's sync gap when that is longer, so never more often than
|
||||
`standard`) and every 6 h after a hand entry, a smaller snapshot, no background reasoning, a short daily
|
||||
briefing (`recommendations` holds at most one entry, every field stays present), no automatic document reading,
|
||||
no supplement label reading. `standard`: the operator's sync gap (default 6 h), 1 h after a hand entry,
|
||||
background reasoning at the job default. `full`: 1 h after any ingest (the behaviour before v1.43). The
|
||||
interactive Coach is the same at every level."
|
||||
CoachReasoningState:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -61,6 +61,9 @@ describe("resolveAiUsageLevel — every choice x default x ceiling", () => {
|
||||
describe("what each level does", () => {
|
||||
it("regenerates status cards after a sync: a day, the operator's gap, the hour", () => {
|
||||
expect(statusRegenGapHours("frugal", "sync", 6)).toBe(24);
|
||||
// Never more often than standard: an operator gap above a day wins.
|
||||
expect(statusRegenGapHours("frugal", "sync", 48)).toBe(48);
|
||||
expect(statusRegenGapHours("standard", "sync", 48)).toBe(48);
|
||||
expect(statusRegenGapHours("standard", "sync", 6)).toBe(6);
|
||||
expect(statusRegenGapHours("standard", "sync", 12)).toBe(12);
|
||||
expect(statusRegenGapHours("full", "sync", 12)).toBe(1);
|
||||
|
||||
@@ -44,9 +44,12 @@ export interface AiUsageLevelEffect {
|
||||
* Minimum hours between two status-card regenerations of one scope after a
|
||||
* sync (a watch, an integration). `"instance"` reads the operator's sync
|
||||
* gap (`AppSettings.aiSyncRegenGapHours`, else `AI_SYNC_REGEN_GAP_HOURS`,
|
||||
* else six).
|
||||
* else six). `{ instanceAtLeast }` reads the same gap but never goes
|
||||
* below the floor, so a level meant to regenerate less often than
|
||||
* `standard` cannot end up regenerating more often on an instance whose
|
||||
* operator set a gap above the floor.
|
||||
*/
|
||||
syncRegenGapHours: number | "instance";
|
||||
syncRegenGapHours: number | "instance" | { instanceAtLeast: number };
|
||||
/** The same gap after a hand entry. */
|
||||
manualRegenGapHours: number;
|
||||
/**
|
||||
@@ -89,7 +92,7 @@ export const AI_USAGE_LEVEL_EFFECTS: Readonly<
|
||||
Record<AiUsageLevel, Readonly<AiUsageLevelEffect>>
|
||||
> = Object.freeze({
|
||||
frugal: {
|
||||
syncRegenGapHours: 24,
|
||||
syncRegenGapHours: { instanceAtLeast: 24 },
|
||||
manualRegenGapHours: 6,
|
||||
statusSnapshotChars: 10_000,
|
||||
backgroundReasoning: false,
|
||||
@@ -235,9 +238,10 @@ export function statusRegenGapHours(
|
||||
): number {
|
||||
const effect = AI_USAGE_LEVEL_EFFECTS[level];
|
||||
if (source === "manual") return effect.manualRegenGapHours;
|
||||
return effect.syncRegenGapHours === "instance"
|
||||
? instanceSyncGapHours
|
||||
: effect.syncRegenGapHours;
|
||||
const gap = effect.syncRegenGapHours;
|
||||
if (gap === "instance") return instanceSyncGapHours;
|
||||
if (typeof gap === "number") return gap;
|
||||
return Math.max(gap.instanceAtLeast, instanceSyncGapHours);
|
||||
}
|
||||
|
||||
// ── Estimate ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -63,7 +63,8 @@ export type StatusIngestSource = IngestSource;
|
||||
* day. Since v1.43 (#1217) the gap is chosen by source and by the record's AI
|
||||
* usage level (`statusRegenGapHours` in `@/lib/ai/usage-level`): a hand entry
|
||||
* keeps the hour (six on `frugal`), a sync waits the operator's sync gap on
|
||||
* `standard` (default six hours), a day on `frugal`, the hour on `full`.
|
||||
* `standard` (default six hours), a day on `frugal` (or the operator's gap
|
||||
* when it is longer), the hour on `full`.
|
||||
*/
|
||||
async function ingestRegenGapMs(
|
||||
userId: string,
|
||||
|
||||
@@ -16,7 +16,7 @@ import { dataEnvelope, errorEnvelope, stdResponses } from "./shared";
|
||||
export const aiUsageLevel = z.enum(AI_USAGE_LEVELS).meta({
|
||||
id: "AiUsageLevel",
|
||||
description:
|
||||
"How much background AI work an account asks for, in ceiling order. `frugal`: status cards regenerate at most every 24 h after a sync and every 6 h after a hand entry, a smaller snapshot, no background reasoning, a short daily briefing (`recommendations` holds at most one entry, every field stays present), no automatic document reading, no supplement label reading. `standard`: the operator's sync gap (default 6 h), 1 h after a hand entry, background reasoning at the job default. `full`: 1 h after any ingest (the behaviour before v1.43). The interactive Coach is the same at every level.",
|
||||
"How much background AI work an account asks for, in ceiling order. `frugal`: status cards regenerate at most every 24 h after a sync (or the operator's sync gap when that is longer, so never more often than `standard`) and every 6 h after a hand entry, a smaller snapshot, no background reasoning, a short daily briefing (`recommendations` holds at most one entry, every field stays present), no automatic document reading, no supplement label reading. `standard`: the operator's sync gap (default 6 h), 1 h after a hand entry, background reasoning at the job default. `full`: 1 h after any ingest (the behaviour before v1.43). The interactive Coach is the same at every level.",
|
||||
});
|
||||
|
||||
export const aiUsageState = z
|
||||
|
||||
Reference in new issue
Block a user