mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
feat(insights): generic per-metric assessment coverage for HealthKit metrics
Extend the assessment-card system beyond the seven specialised scopes to the ~30 HealthKit metric pages, without hand-writing a prompt per metric. - Add a metric-metadata registry keyed by HealthKit id (display name, unit, direction, optional normal range, archetype, scope), covering the data-bearing HealthKit metrics. The seven specialised metrics keep their own scopes and are excluded from the generic set; an unregistered id gets no card. - Add a generic data-driven generator that reuses the existing graded snapshot, provider runner, stale-while-revalidate cache, and persist path. One prompt template per archetype (physiological-vital, activity-fitness, body-composition, mobility-gait, environmental-exposure) plus a dedicated sleep template, parameterised by metric metadata. Empty-data metrics return an insufficient marker with no provider call. Normal ranges use age + sex where the profile stores them; no ancestry/region ranges. - Add GET /api/insights/metric-status?metric=<id>: apiHandler-wrapped, Zod-validated against the closed registry id set (422 on unknown), read-only with stale-while-revalidate. First insights route with OpenAPI coverage; regenerate docs/api/openapi.yaml. - Route the generic metric:<id> scope through the on-demand status queue worker dispatch. - Extend the nightly warm pass to the generic metric caches for a user's data-bearing metrics only (one distinct read gates the set), keeping de+en warm and never calling a provider for an empty metric. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 41453fbdfee60793c16269b9a9e5f04b6c3ea450)
This commit is contained in:
1 parent
2717297dae
commit
e33911e2d4
14 files changed
+1799
-4
No files matched your search
@@ -1927,6 +1927,78 @@ paths:
|
||||
"401": *a1
|
||||
"422": *a2
|
||||
"429": *a3
|
||||
/api/insights/metric-status:
|
||||
get:
|
||||
tags:
|
||||
- Insights
|
||||
summary: Generic per-HealthKit-metric assessment
|
||||
description: "v1.8.7.1 — data-driven plain-language assessment for any registered HealthKit metric (resting heart rate,
|
||||
sleep, glucose, body composition, gait, audio exposure, …). One generic route covering ~30 metric pages via
|
||||
archetype prompt templates + per-metric metadata. Read-only: a cache miss warms a generation out of band and
|
||||
serves the last-good text meanwhile (stale-while-revalidate). An unknown `metric` 422s against the closed
|
||||
registry enum. Auth via cookie or Bearer."
|
||||
parameters:
|
||||
- in: query
|
||||
name: metric
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- RESTING_HEART_RATE
|
||||
- HEART_RATE_VARIABILITY
|
||||
- OXYGEN_SATURATION
|
||||
- RESPIRATORY_RATE
|
||||
- BODY_TEMPERATURE
|
||||
- SKIN_TEMPERATURE
|
||||
- BLOOD_GLUCOSE
|
||||
- WALKING_HEART_RATE_AVERAGE
|
||||
- PULSE_WAVE_VELOCITY
|
||||
- VASCULAR_AGE
|
||||
- STEPS
|
||||
- ACTIVE_ENERGY
|
||||
- FLIGHTS_CLIMBED
|
||||
- WALKING_RUNNING_DISTANCE
|
||||
- TIME_IN_DAYLIGHT
|
||||
- VO2_MAX
|
||||
- TOTAL_BODY_WATER
|
||||
- BONE_MASS
|
||||
- FAT_FREE_MASS
|
||||
- FAT_MASS
|
||||
- MUSCLE_MASS
|
||||
- LEAN_BODY_MASS
|
||||
- VISCERAL_FAT
|
||||
- WALKING_STEADINESS
|
||||
- WALKING_ASYMMETRY
|
||||
- WALKING_DOUBLE_SUPPORT
|
||||
- WALKING_STEP_LENGTH
|
||||
- WALKING_SPEED
|
||||
- AUDIO_EXPOSURE_ENV
|
||||
- AUDIO_EXPOSURE_HEADPHONE
|
||||
- AUDIO_EXPOSURE_EVENT
|
||||
- SLEEP_DURATION
|
||||
description: "HealthKit metric id to assess (e.g. RESTING_HEART_RATE, SLEEP_DURATION). Closed enum: an unknown id 422s.
|
||||
The seven specialised metrics are served by their own routes and are not accepted here."
|
||||
required: true
|
||||
description: "HealthKit metric id to assess (e.g. RESTING_HEART_RATE, SLEEP_DURATION). Closed enum: an unknown id 422s.
|
||||
The seven specialised metrics are served by their own routes and are not accepted here."
|
||||
- in: query
|
||||
name: locale
|
||||
schema:
|
||||
description: Optional UI-locale override; defaults to the session locale.
|
||||
type: string
|
||||
enum:
|
||||
- de
|
||||
- en
|
||||
description: Optional UI-locale override; defaults to the session locale.
|
||||
responses:
|
||||
"200":
|
||||
description: Assessment envelope (fresh, cached, or preparing).
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/MetricStatusResponseEnvelope"
|
||||
"401": *a1
|
||||
"422": *a2
|
||||
"429": *a3
|
||||
components:
|
||||
schemas:
|
||||
PasskeyLoginVerifyRequest:
|
||||
@@ -6171,6 +6243,62 @@ components:
|
||||
additionalProperties: false
|
||||
description: AI-generated insights bundle. Strict-schema validated server-side; Coach-routed when the insight surface
|
||||
needs day-level grounding.
|
||||
MetricStatusResponseEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: "#/components/schemas/MetricStatusResponse"
|
||||
error:
|
||||
type: "null"
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
requestId:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
required:
|
||||
- data
|
||||
- error
|
||||
additionalProperties: false
|
||||
MetricStatusResponse:
|
||||
type: object
|
||||
properties:
|
||||
hasProvider:
|
||||
type: boolean
|
||||
description: False when the user has no usable AI provider — `text` then carries the generic no-key guidance.
|
||||
text:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
description: The assessment narrative (plain text, rendered as React text children). Null while a first generation is
|
||||
preparing, or when the metric has insufficient data.
|
||||
cached:
|
||||
type: boolean
|
||||
description: True when `text` is served from cache (incl. last-good).
|
||||
updatedAt:
|
||||
anyOf:
|
||||
- type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||||
- type: "null"
|
||||
description: When the served assessment was generated; null when none.
|
||||
preparing:
|
||||
description: True when a first assessment is being generated out of band and no prior text exists yet — the client polls
|
||||
until it lands.
|
||||
type: boolean
|
||||
insufficient:
|
||||
description: True when the metric has no readings; no assessment is generated (no LLM call). The card shows its
|
||||
insufficient-data state.
|
||||
type: boolean
|
||||
required:
|
||||
- hasProvider
|
||||
- text
|
||||
- cached
|
||||
- updatedAt
|
||||
additionalProperties: false
|
||||
description: "Generic per-metric assessment envelope. Identical shape to the seven specialised `*-status` cards so the
|
||||
`InsightStatusCard` consumes it unchanged. Read-only + stale-while-revalidate: a cache miss warms a generation
|
||||
out of band and serves the last-good text meanwhile."
|
||||
MedicationScheduleOutput:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in new issue
Block a user