mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
feat(insights): per-score assessment text + shared metric signal block
Add an additive, non-breaking assessment field to GET /api/insights/derived:
{ text, source, updatedAt } | null, keyed to the requested metric id for the
five per-score ids (READINESS, SLEEP_SCORE, RECOVERY_SCORE, STRAIN_SCORE,
STRESS_SCORE). The field is null when the metric is not one of those or when
its status is not "ok". A deterministic text always fills it — composed from
the score's own contributors, so provider-less accounts and the demo are never
empty — and warmer provider prose overrides it once cached, warmed out of band
so the GET never blocks on a model round-trip. de/en, following the v1.12.12
deterministic-narrative model. The batch grid read returns assessment: null;
the detail sheets use the single-metric route.
Introduce a shared buildMetricSignal() helper: a token-lean, signal-rich
descriptor (current, own baseline, signed delta, spread, normal-swing verdict,
normal-range placement, contributors) computed server-side so the model states
the comparison instead of re-deriving it from raw buckets. Thread it into the
generic per-metric assessment snapshot and lead the base prompt from it. Add a
PROMPT_VERSION constant to the base system prompt so prompt changes are
traceable.
Regenerate docs/api/openapi.yaml.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7db9608c4c
commit
30a7437f4d
14 files changed
+1282
-12
No files matched your search
@@ -167,6 +167,11 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
provenance: derived.provenance,
|
||||
reason:
|
||||
derived.status === "insufficient" ? derived.reason : null,
|
||||
// The batch backs the dashboard GRID; per-score assessment text is
|
||||
// a detail-sheet concern served by the single-metric route. Always
|
||||
// null here so the wire shape matches `DerivedMetricResponse`
|
||||
// without paying the assessment cost on a 17-item grid read.
|
||||
assessment: null,
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -31,6 +31,8 @@ import {
|
||||
DERIVED_METRIC_IDS,
|
||||
type DerivedMetricId,
|
||||
} from "@/lib/insights/derived";
|
||||
import { resolveDerivedAssessment } from "@/lib/insights/derived/derived-assessment-ai";
|
||||
import { resolveServerLocale } from "@/lib/i18n/server-locale";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -80,6 +82,26 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
meta: { metric, status: derived.status },
|
||||
});
|
||||
|
||||
// v1.13.2 — additive per-score assessment: a short "why is this score what
|
||||
// it is" explanation, keyed to the SAME id the caller passed (READINESS,
|
||||
// SLEEP_SCORE, RECOVERY_SCORE, STRAIN_SCORE, STRESS_SCORE). Null for any
|
||||
// other id, and null when status !== "ok". Always a non-empty deterministic
|
||||
// text when present (the demo + provider-less accounts fill); warmer AI
|
||||
// prose overrides it once cached. The warm enqueue is fire-and-forget — this
|
||||
// never blocks on an LLM round-trip.
|
||||
const localeParam = request.nextUrl.searchParams.get("locale");
|
||||
const resolvedLocale = await resolveServerLocale({
|
||||
request,
|
||||
userLocale: user.locale ?? null,
|
||||
override: localeParam,
|
||||
});
|
||||
const assessment = await resolveDerivedAssessment({
|
||||
metric,
|
||||
userId: user.id,
|
||||
derived,
|
||||
locale: resolvedLocale,
|
||||
});
|
||||
|
||||
// Flatten the discriminated union for the wire: `metric` tags it,
|
||||
// `value`/`reason` are nullable so iOS decodes one stable shape.
|
||||
return apiSuccess({
|
||||
@@ -90,5 +112,6 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
confidence: derived.status === "ok" ? derived.confidence : null,
|
||||
provenance: derived.provenance,
|
||||
reason: derived.status === "insufficient" ? derived.reason : null,
|
||||
assessment,
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user