mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(ai): name the acute glucose and fever floors in both units
The acute red-flag clause stated the glucose floors only in mg/dL and the sustained-fever floor only in °C. The clause is matched against what the person writes, so a reading of 2.9 mmol/L or 103 °F did not meet any of its numbers. Each floor now carries both units, mg/dL and °C first, in the English and German contracts. Refs #1067
This commit is contained in:
1 parent
9cab8e74da
commit
6dbf57f4f4
3 files changed
+40
-4
No files matched your search
@@ -220,4 +220,17 @@ describe("safetyAcute numbers are bound to clinical-floors", () => {
|
||||
String(FEVER_RED_FLAG_C).replace(".", ","),
|
||||
);
|
||||
});
|
||||
|
||||
// A person reading mmol/L or °F describes a crisis in that unit. A clause
|
||||
// that names only mg/dL and °C cannot match "my sugar is 2.9" or "103 °F".
|
||||
it("states every glucose and fever floor in both units", () => {
|
||||
expect(safetyAcute.en).toContain("below 54 mg/dL (3.0 mmol/L)");
|
||||
expect(safetyAcute.en).toContain("below 70 mg/dL (3.9 mmol/L)");
|
||||
expect(safetyAcute.en).toContain("250 mg/dL (13.9 mmol/L)");
|
||||
expect(safetyAcute.en).toContain("38.5 °C (101.3 °F)");
|
||||
expect(safetyAcute.de).toContain("unter 54 mg/dL (3,0 mmol/L)");
|
||||
expect(safetyAcute.de).toContain("unter 70 mg/dL (3,9 mmol/L)");
|
||||
expect(safetyAcute.de).toContain("250 mg/dL (13,9 mmol/L)");
|
||||
expect(safetyAcute.de).toContain("38,5 °C (101,3 °F)");
|
||||
});
|
||||
});
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
GLUCOSE_HYPER_FLOOR,
|
||||
FEVER_RED_FLAG_C,
|
||||
} from "@/lib/clinical-floors";
|
||||
import { mgdlToMmol } from "@/lib/glucose";
|
||||
|
||||
export type ContractLocale = "de" | "en";
|
||||
|
||||
@@ -49,6 +50,25 @@ function deNum(value: number): string {
|
||||
return String(value).replace(".", ",");
|
||||
}
|
||||
|
||||
/**
|
||||
* A glucose floor in both clinical units, mg/dL first (the unit the floor is
|
||||
* defined in). The acute clause is read against what the person writes, not
|
||||
* against the snapshot, and a person reading mmol/L writes "2.9" — a rule
|
||||
* that only names 54 mg/dL cannot fire on it.
|
||||
*/
|
||||
function glucoseFloor(mgdl: number, locale: ContractLocale): string {
|
||||
const mmol = mgdlToMmol(mgdl).toFixed(1);
|
||||
return `${mgdl} mg/dL (${locale === "de" ? mmol.replace(".", ",") : mmol} mmol/L)`;
|
||||
}
|
||||
|
||||
/** A temperature floor in both scales, °C first (the scale it is defined in). */
|
||||
function temperatureFloor(celsius: number, locale: ContractLocale): string {
|
||||
const fahrenheit = (celsius * 1.8 + 32).toFixed(1);
|
||||
return locale === "de"
|
||||
? `${deNum(celsius)} °C (${fahrenheit.replace(".", ",")} °F)`
|
||||
: `${celsius} °C (${fahrenheit} °F)`;
|
||||
}
|
||||
|
||||
/** A named contract fragment with both hand-composed locale texts. */
|
||||
export type SharedContract = Record<ContractLocale, string>;
|
||||
|
||||
@@ -125,9 +145,9 @@ Verschreibe, empfiehl oder ändere NIEMALS Medikamenten-Dosen, auch wenn der Sna
|
||||
*/
|
||||
export const safetyAcute: SharedContract = {
|
||||
en: `ACUTE RED FLAGS (contract, not style)
|
||||
If the user describes an acute crisis sign — chest pain or chest pressure, fainting or near-fainting (syncope), a sudden severe symptom (e.g. worst-ever headache, sudden weakness or trouble speaking, trouble breathing), a hypertensive-crisis reading (systolic ≥ ${BP_SYS_CRITICAL} or diastolic ≥ ${BP_DIA_CRITICAL} with symptoms), a severe-low or very-high glucose reading (below ${GLUCOSE_HYPO_SEVERE_FLOOR} mg/dL, or below ${GLUCOSE_HYPO_FLOOR} mg/dL with symptoms, or at/above ${GLUCOSE_HYPER_FLOOR} mg/dL with symptoms), a sustained fever at/above ${FEVER_RED_FLAG_C} °C, or any mention of suicidal thoughts or self-harm — say plainly, in one calm sentence, that this needs prompt medical attention or emergency services now, and do NOT data-coach it. Do not diagnose, do not name a condition, do not estimate severity from the numbers — just point to prompt/emergency care and stop. This is a closed list; outside it, stay calm and non-alarmist as usual.`,
|
||||
If the user describes an acute crisis sign — chest pain or chest pressure, fainting or near-fainting (syncope), a sudden severe symptom (e.g. worst-ever headache, sudden weakness or trouble speaking, trouble breathing), a hypertensive-crisis reading (systolic ≥ ${BP_SYS_CRITICAL} or diastolic ≥ ${BP_DIA_CRITICAL} with symptoms), a severe-low or very-high glucose reading (below ${glucoseFloor(GLUCOSE_HYPO_SEVERE_FLOOR, "en")}, or below ${glucoseFloor(GLUCOSE_HYPO_FLOOR, "en")} with symptoms, or at/above ${glucoseFloor(GLUCOSE_HYPER_FLOOR, "en")} with symptoms), a sustained fever at/above ${temperatureFloor(FEVER_RED_FLAG_C, "en")}, or any mention of suicidal thoughts or self-harm — say plainly, in one calm sentence, that this needs prompt medical attention or emergency services now, and do NOT data-coach it. Do not diagnose, do not name a condition, do not estimate severity from the numbers — just point to prompt/emergency care and stop. This is a closed list; outside it, stay calm and non-alarmist as usual.`,
|
||||
de: `AKUTE WARNZEICHEN (Vertrag, kein Stil)
|
||||
Beschreibt der Nutzer ein akutes Krisenzeichen — Brustschmerz oder Druck auf der Brust, Ohnmacht oder Beinahe-Ohnmacht (Synkope), ein plötzliches schweres Symptom (z. B. stärkster Kopfschmerz aller Zeiten, plötzliche Schwäche oder Sprachstörung, Atemnot), einen hypertensiven Notfallwert (systolisch ≥ ${BP_SYS_CRITICAL} oder diastolisch ≥ ${BP_DIA_CRITICAL} mit Symptomen), einen schwer-niedrigen oder sehr hohen Glukosewert (unter ${GLUCOSE_HYPO_SEVERE_FLOOR} mg/dL, oder unter ${GLUCOSE_HYPO_FLOOR} mg/dL mit Symptomen, oder ≥ ${GLUCOSE_HYPER_FLOOR} mg/dL mit Symptomen), anhaltendes Fieber ≥ ${deNum(FEVER_RED_FLAG_C)} °C oder Gedanken an Suizid bzw. Selbstverletzung — sage in einem ruhigen Satz klar, dass das jetzt umgehende ärztliche Hilfe oder den Notruf braucht, und coache es NICHT anhand der Daten. Diagnostiziere nicht, benenne keine Erkrankung, schätze keinen Schweregrad aus den Zahlen — verweise nur auf umgehende Hilfe bzw. den Notruf und höre auf. Das ist eine geschlossene Liste; außerhalb davon bleibe wie gewohnt ruhig und nicht alarmierend.`,
|
||||
Beschreibt der Nutzer ein akutes Krisenzeichen — Brustschmerz oder Druck auf der Brust, Ohnmacht oder Beinahe-Ohnmacht (Synkope), ein plötzliches schweres Symptom (z. B. stärkster Kopfschmerz aller Zeiten, plötzliche Schwäche oder Sprachstörung, Atemnot), einen hypertensiven Notfallwert (systolisch ≥ ${BP_SYS_CRITICAL} oder diastolisch ≥ ${BP_DIA_CRITICAL} mit Symptomen), einen schwer-niedrigen oder sehr hohen Glukosewert (unter ${glucoseFloor(GLUCOSE_HYPO_SEVERE_FLOOR, "de")}, oder unter ${glucoseFloor(GLUCOSE_HYPO_FLOOR, "de")} mit Symptomen, oder ≥ ${glucoseFloor(GLUCOSE_HYPER_FLOOR, "de")} mit Symptomen), anhaltendes Fieber ≥ ${temperatureFloor(FEVER_RED_FLAG_C, "de")} oder Gedanken an Suizid bzw. Selbstverletzung — sage in einem ruhigen Satz klar, dass das jetzt umgehende ärztliche Hilfe oder den Notruf braucht, und coache es NICHT anhand der Daten. Diagnostiziere nicht, benenne keine Erkrankung, schätze keinen Schweregrad aus den Zahlen — verweise nur auf umgehende Hilfe bzw. den Notruf und höre auf. Das ist eine geschlossene Liste; außerhalb davon bleibe wie gewohnt ruhig und nicht alarmierend.`,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,10 +74,13 @@ function promptHash(locale: "de" | "en"): string {
|
||||
* Hashes captured from the prompts as they stood at NARRATIVE_PROMPT_VERSION
|
||||
* 1.13.0, before the output-language change. If either moves, the de or en
|
||||
* prompt drifted — which this change promised would not happen.
|
||||
*
|
||||
* Re-captured once since: the shared acute red-flag clause now states its
|
||||
* glucose and fever floors in both units.
|
||||
*/
|
||||
const FROZEN_PROMPT_HASHES: Record<"de" | "en", string> = {
|
||||
de: "be667f16beeb44f51a81fedd929716b2249579fe914b45bb8cba0cd9e64a8c6d",
|
||||
en: "bb0b080e21a2b9729bdf0047ae221c326ec29ecd74d0999f19ea8deabcbf078a",
|
||||
de: "1bb2246e991fcc28f9a009996284529beebf0d469b29b157cbad273b822626c6",
|
||||
en: "d34fa0fcb4a855f805f1a30338d309c19b07d5a54533f75de412cbe107b4f772",
|
||||
};
|
||||
|
||||
describe("period-narrative output language", () => {
|
||||
|
||||
Reference in new issue
Block a user