Files
memind/health-extraction.mjs
T
john f96fdcb3b9 feat(health): add P0 health channel kernel and encrypted health zone.
Lock health recording behind confirm-only state machines, reject public publishes for the health category, and ship the 14-day experiment Page Data templates with tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 18:04:26 +08:00

58 lines
2.0 KiB
JavaScript

import { validateBloodPressurePair, validateMetricValue } from './health-observation-validate.mjs';
export const EXTRACTION_CONFIDENCE_MIN = 0.9;
const METRIC_SET_FIELDS = Object.freeze({
blood_pressure: ['systolic', 'diastolic'],
heart_rate: ['hr'],
weight: ['weight'],
spo2: ['spo2'],
temperature: ['temperature'],
});
export function validateExtractionResult(extracted = {}, { metricSetHint = null } = {}) {
const metricSet = extracted.metric_set || metricSetHint;
if (!metricSet || !METRIC_SET_FIELDS[metricSet]) {
return { ok: false, error: 'unknown_metric_set', unreadable: true };
}
const confidence = extracted.confidence && typeof extracted.confidence === 'object'
? extracted.confidence
: {};
const lowConfidence = [];
const values = {};
if (metricSet === 'blood_pressure') {
const pair = validateBloodPressurePair(extracted.systolic, extracted.diastolic);
if (!pair.ok) {
return { ok: false, error: pair.error, unreadable: pair.error === 'out_of_range', field: pair.field };
}
values.systolic = pair.systolic;
values.diastolic = pair.diastolic;
if (extracted.pulse != null) {
const pulse = validateMetricValue('hr', extracted.pulse);
if (!pulse.ok) return { ok: false, error: pulse.error, field: 'pulse' };
values.pulse = pulse.value;
}
} else {
const field = METRIC_SET_FIELDS[metricSet][0];
const metricType = metricSet === 'heart_rate' ? 'hr' : metricSet === 'weight' ? 'weight' : metricSet;
const checked = validateMetricValue(metricType, extracted[field] ?? extracted.value);
if (!checked.ok) return { ok: false, error: checked.error, unreadable: checked.error === 'out_of_range' };
values[field] = checked.value;
}
for (const field of Object.keys(values)) {
const score = Number(confidence[field] ?? extracted.confidence?.[field] ?? 1);
if (score < EXTRACTION_CONFIDENCE_MIN) lowConfidence.push(field);
}
return {
ok: true,
metricSet,
values,
lowConfidence,
requireConfirm: true,
unreadable: false,
};
}