mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
feat(coach): quote the real BP range, address by name, render action cards
Feed the user's own usual blood-pressure range — the median plus or minus MAD band, computed from the readings already in the snapshot — into the Coach context with a clause to quote those bounds verbatim, so a band is never invented from window means. Thread the stored first name into the prompt and surface it sparingly on roughly one turn in three, never as a rote greeting. Add the context-layer rule that names the relevant context before a suggestion, a single-chart-token allowance that works on every provider, and a per-turn opener hint. Wire the already-shipped suggested-action SSE frame to render: the streaming hook collects it and the message thread shows the action card, mirroring the reminder-suggestion path. Extend the eval golden set with band-verbatim, chart-token, and no-overclaim cases, and pin a red-team case proving the chart-token syntax cannot smuggle a dose past the outbound screen.
This commit is contained in:
1 parent
b393527cac
commit
5674f10b2f
10 files changed
+329
-13
No files matched your search
@@ -72,6 +72,11 @@ import {
|
||||
coachOutboundFallback,
|
||||
} from "@/lib/ai/coach/outbound-guard";
|
||||
import { getCoachSystemPrompt } from "@/lib/ai/coach/system-prompt";
|
||||
import {
|
||||
openerArchetypeHint,
|
||||
shouldUseNameForTurn,
|
||||
firstNameFromDisplayName,
|
||||
} from "@/lib/ai/prompts/opener-archetype";
|
||||
import { getSelfContextTextForUser } from "@/lib/ai/coach/about-me";
|
||||
import { buildCoachSnapshot } from "@/lib/ai/coach/snapshot";
|
||||
import {
|
||||
@@ -366,7 +371,7 @@ async function handleChatRequest(request: NextRequest): Promise<Response> {
|
||||
// we don't accidentally widen narrow per-call scopes.
|
||||
const prefsRow = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { coachPrefsJson: true },
|
||||
select: { coachPrefsJson: true, displayName: true },
|
||||
});
|
||||
const coachPrefs = parseCoachPrefs(prefsRow?.coachPrefsJson);
|
||||
const effectiveScope =
|
||||
@@ -386,7 +391,19 @@ async function handleChatRequest(request: NextRequest): Promise<Response> {
|
||||
// teaches the model to emit `---REMEMBER---` (durable "remind me" capture),
|
||||
// one teaches the closed `---SUGGEST-ACTION---` confirm-card allowlist. Both
|
||||
// are provider-neutral sentinels stripped from the prose before it streams.
|
||||
const systemPrompt = `${getCoachSystemPrompt(locale, coachPrefs, aboutMe)}\n\n${buildRememberAddendum(locale)}\n\n${buildSuggestActionAddendum(locale)}`;
|
||||
// v1.22 (W6) — per-turn personalization: a sparse, hash-gated first name and
|
||||
// an opener-archetype hint so multi-turn sessions vary. The turn index is the
|
||||
// count of prior turns, so the name surfaces on ~1-in-3 turns, varied and
|
||||
// never on a fixed cadence; both omit cleanly when no display name is set.
|
||||
const turnIndex = priorTurns.length;
|
||||
const firstName = firstNameFromDisplayName(prefsRow?.displayName ?? null);
|
||||
const coachPersonalization = {
|
||||
firstName,
|
||||
mayUseName:
|
||||
firstName != null && shouldUseNameForTurn(`${userId}:${turnIndex}`),
|
||||
openerHint: openerArchetypeHint(`${userId}:${turnIndex}`, locale),
|
||||
};
|
||||
const systemPrompt = `${getCoachSystemPrompt(locale, coachPrefs, aboutMe, coachPersonalization)}\n\n${buildRememberAddendum(locale)}\n\n${buildSuggestActionAddendum(locale)}`;
|
||||
const allTurns: CoachTurn[] = [
|
||||
...priorTurns,
|
||||
{ role: "user", content: message },
|
||||
|
||||
Reference in new issue
Block a user