mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
feat(insights): inject guideline band placement into generic metric assessments
Thread the interpretation block into the generic per-metric assessment path (the ~40 HealthKit metric cards). For a metric the knowledge base covers, the prompt now carries the band table plus the server-computed position of the current value, so the assessment names where the value sits on the guideline scale and judges the trend by that position — the visceral-fat 2.7 case now reads as "comfortably in the healthy band" rather than reciting how many times it was logged. The block is absent for uncovered metrics and for a sex-split band with no known sex, so those cards fall back to exactly today's personal-relative behaviour. Band edges carry no strip risk here — the generic assessment path runs no post-generation numeric grounding gate.
This commit is contained in:
1 parent
3ce396081f
commit
52d09d17c7
3 files changed
+52
-2
No files matched your search
@@ -119,6 +119,25 @@ describe("metric-archetype user prompt — context block plumbing", () => {
|
||||
expect(out).not.toContain("undefined");
|
||||
});
|
||||
|
||||
it("appends the interpretation block when provided (Welle J)", () => {
|
||||
const out = getMetricArchetypeUserPrompt(
|
||||
meta,
|
||||
"{}",
|
||||
"2026-06-05",
|
||||
"en",
|
||||
"",
|
||||
"",
|
||||
"INTERPRETATION CONTEXT (guideline bands): the healthy band.",
|
||||
);
|
||||
expect(out).toContain("INTERPRETATION CONTEXT");
|
||||
});
|
||||
|
||||
it("omits the interpretation block cleanly when absent", () => {
|
||||
const out = getMetricArchetypeUserPrompt(meta, "{}", "2026-06-05", "en");
|
||||
expect(out).not.toContain("INTERPRETATION CONTEXT");
|
||||
expect(out).not.toContain("undefined");
|
||||
});
|
||||
|
||||
it("makes the closing step conditional in the user instruction (both locales)", () => {
|
||||
expect(
|
||||
getMetricArchetypeUserPrompt(meta, "{}", "2026-06-05", "en"),
|
||||
|
||||
@@ -127,6 +127,13 @@ export function getMetricArchetypeUserPrompt(
|
||||
* grounded in already-computed data; optional and may be empty.
|
||||
*/
|
||||
assessmentContextBlock?: string,
|
||||
/**
|
||||
* v1.27.13 (Welle J) — the guideline interpretation block (band table +
|
||||
* server-computed position) for metrics the knowledge base covers. Absent
|
||||
* for metrics with no guideline band — the prompt then stays personal-
|
||||
* relative, exactly as before.
|
||||
*/
|
||||
interpretationBlock?: string,
|
||||
): string {
|
||||
const ctxBlock =
|
||||
previousContextBlock && previousContextBlock.trim().length > 0
|
||||
@@ -136,14 +143,18 @@ export function getMetricArchetypeUserPrompt(
|
||||
assessmentContextBlock && assessmentContextBlock.trim().length > 0
|
||||
? `\n\n${assessmentContextBlock}\n`
|
||||
: "";
|
||||
const interpBlock =
|
||||
interpretationBlock && interpretationBlock.trim().length > 0
|
||||
? `\n\n${interpretationBlock}\n`
|
||||
: "";
|
||||
if (locale === "en") {
|
||||
return `Date: ${todayKey} (Europe/Berlin)
|
||||
Write one short assessment of this person's ${meta.displayName.toLowerCase()}: name the current level with a concrete number from the snapshot, place the recent window against their own weekly/monthly baseline, and — when something is genuinely actionable — close with one doable step; when nothing is, skip the step rather than manufacture filler. Judge confidence from the measurement count and recency.${ctxBlock}${extraBlock}
|
||||
Write one short assessment of this person's ${meta.displayName.toLowerCase()}: name the current level with a concrete number from the snapshot, place the recent window against their own weekly/monthly baseline, and — when something is genuinely actionable — close with one doable step; when nothing is, skip the step rather than manufacture filler. Judge confidence from the measurement count and recency.${ctxBlock}${extraBlock}${interpBlock}
|
||||
|
||||
${snapshotJson}`;
|
||||
}
|
||||
return `Datum: ${todayKey} (Europe/Berlin)
|
||||
Schreibe eine kurze Einschätzung zu ${meta.displayName} dieser Person: benenne das aktuelle Niveau mit einer konkreten Zahl aus dem Snapshot, ordne das recent-Fenster gegen die eigene Wochen-/Monats-Baseline ein und schließe — wenn etwas wirklich umsetzbar ist — mit einem machbaren Schritt; ist nichts umsetzbar, lass den Schritt weg statt Fülltext zu erfinden. Konfidenz aus Messanzahl und Aktualität ableiten.${ctxBlock}${extraBlock}
|
||||
Schreibe eine kurze Einschätzung zu ${meta.displayName} dieser Person: benenne das aktuelle Niveau mit einer konkreten Zahl aus dem Snapshot, ordne das recent-Fenster gegen die eigene Wochen-/Monats-Baseline ein und schließe — wenn etwas wirklich umsetzbar ist — mit einem machbaren Schritt; ist nichts umsetzbar, lass den Schritt weg statt Fülltext zu erfinden. Konfidenz aus Messanzahl und Aktualität ableiten.${ctxBlock}${extraBlock}${interpBlock}
|
||||
|
||||
${snapshotJson}`;
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
getMetricArchetypeSystemPrompt,
|
||||
getMetricArchetypeUserPrompt,
|
||||
} from "@/lib/ai/prompts/metric-archetypes";
|
||||
import { buildInterpretationBlock } from "@/lib/ai/prompts/interpretation-block";
|
||||
import {
|
||||
getMetricStatusMeta,
|
||||
metricStatusScope,
|
||||
@@ -461,6 +462,24 @@ export async function generateMetricStatus(args: {
|
||||
locale,
|
||||
);
|
||||
|
||||
// v1.27.13 (Welle J) — the guideline interpretation block for metrics the
|
||||
// knowledge base covers (visceral fat, resting HR, SpO₂, respiratory rate,
|
||||
// body temperature, sleep duration, waist, PWV). The band position is
|
||||
// computed from the signal's current value (or the latest day when no signal
|
||||
// window exists); absent for uncovered metrics (fail-soft to personal-
|
||||
// relative). Sex-split bands (waist) resolve only when the profile carries a
|
||||
// sex.
|
||||
const interpretationValue = signal?.current ?? latest?.value ?? null;
|
||||
const interpretationBlock =
|
||||
interpretationValue !== null
|
||||
? buildInterpretationBlock({
|
||||
metricKey: meta.id,
|
||||
value: interpretationValue,
|
||||
sex,
|
||||
locale,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const outcome = await runStatusCompletion({
|
||||
userId: args.userId,
|
||||
cacheAction,
|
||||
@@ -473,6 +492,7 @@ export async function generateMetricStatus(args: {
|
||||
locale,
|
||||
previousContextBlock,
|
||||
contextBlock,
|
||||
interpretationBlock,
|
||||
),
|
||||
// v1.12.1 (D1) — the phrasing task benefits from a touch more sampling
|
||||
// entropy while the FACTS stay pinned by the snapshot + the
|
||||
|
||||
Reference in new issue
Block a user