From 82c6c61a5ea1df6978c0154082bf211260607d2c Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Thu, 7 May 2026 21:03:27 +0200 Subject: [PATCH] fix(sos-stream): profile is not defined ReferenceError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit let profile vor try-block hoisten, damit es im plan-routing-Fallback (line 210, profile?.plan) sichtbar ist. Vorher: const profile innerhalb try-block → block-scoped → ReferenceError außerhalb. Demographics-Block-Injection added — gracefully no-op wenn neue Felder (birthYear/gender/etc.) noch nicht im DB-Schema sind (optional chaining). Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/server/api/coach/sos-stream.get.ts | 59 ++++++++++++++++++++-- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/backend/server/api/coach/sos-stream.get.ts b/backend/server/api/coach/sos-stream.get.ts index 94f94ef..d3ab4e3 100644 --- a/backend/server/api/coach/sos-stream.get.ts +++ b/backend/server/api/coach/sos-stream.get.ts @@ -128,20 +128,67 @@ export default defineEventHandler(async (event) => { console.error("[lyra-memory] load error (non-fatal):", e); } - // Nickname-Injektion (Pattern aus message.post.ts) — sonst halluziniert Lyra - // Namen wie "Max" weil sie keinen Anker hat. + // Nickname-Injektion + Demographics-Block (Pattern aus message.post.ts) — + // sonst halluziniert Lyra Namen wie "Max" weil sie keinen Anker hat. + // WICHTIG: Demographie-Daten werden NUR vom User über die Profile-Form + // gesetzt (memory/feedback_demographics_user_initiated.md) — Lyra + // darf sie LESEN aber NIE EXTRAHIEREN. let nicknamePrefix = ""; + let demographicsBlock = ""; + let profile: Awaited> = null; try { - const profile = await getProfile(user.id); + profile = await getProfile(user.id); const nickname = profile?.nickname || profile?.username; if (nickname) { nicknamePrefix = `NUTZER-NAME: Der Nutzer heißt "${nickname}" – nenne ihn gelegentlich bei seinem Namen wenn es natürlich passt.\n\n`; } + + // Demographics-Block GANZ separat von memoryBlock — strukturierte + // DiGA-Daten, NICHT extrahierbar, NICHT änderbar durch Lyra. + const demoLines: string[] = []; + if (profile?.birthYear) { + const age = new Date().getFullYear() - profile.birthYear; + demoLines.push(`- Alter: ca. ${age} Jahre (Geburtsjahr ${profile.birthYear})`); + } + if (profile?.gender) { + const GENDER_LABEL: Record = { + male: "männlich", + female: "weiblich", + diverse: "divers", + no_answer: "keine Angabe", + }; + demoLines.push(`- Geschlecht: ${GENDER_LABEL[profile.gender] ?? profile.gender}`); + } + if (profile?.maritalStatus) { + const MS_LABEL: Record = { + single: "ledig", + partnered: "in Partnerschaft", + married: "verheiratet", + divorced: "geschieden", + widowed: "verwitwet", + no_answer: "keine Angabe", + }; + demoLines.push( + `- Familienstand: ${MS_LABEL[profile.maritalStatus] ?? profile.maritalStatus}`, + ); + } + if (profile?.profession) { + demoLines.push(`- Beruf: ${profile.profession}`); + } + if (profile?.bundesland) { + demoLines.push(`- Bundesland: ${profile.bundesland}`); + } + if (profile?.city) { + demoLines.push(`- Stadt: ${profile.city}`); + } + if (demoLines.length > 0) { + demographicsBlock = `[USER-DEMOGRAPHIE — vom User selbst angegeben]\n${demoLines.join("\n")}\nNutze diese Infos nur für Empathie + Kontext (z.B. "Schichtarbeit erschwert deinen Rhythmus"). Frage NIEMALS nach diesen Daten — der User pflegt sie selbst in der Profile-Form. Diese Daten persistieren in keinem Memory-Store, sondern sind strukturierte DiGA-Daten.\n\n`; + } } catch (e) { console.error("[sos-stream] profile load (non-fatal):", e); } - const systemPrompt = `${nicknamePrefix}${memoryBlock}${lang}\n\n${COACH_SYSTEM_PROMPT.replace("{{PLAN_DETAILS}}", "")}${SOS_INSTRUCTION}`; + const systemPrompt = `${nicknamePrefix}${demographicsBlock}${memoryBlock}${lang}\n\n${COACH_SYSTEM_PROMPT.replace("{{PLAN_DETAILS}}", "")}${SOS_INSTRUCTION}`; // Erste Nachricht muss user sein const firstUserIdx = messages.findIndex((m) => m.role === "user"); @@ -160,7 +207,9 @@ export default defineEventHandler(async (event) => { if (userToggle && userToggle !== "auto") { llmProvider = userToggle; } else { - const plan = ((profile as any)?.plan ?? "free").toLowerCase(); + const planRaw = (profile?.plan ?? "free").toLowerCase(); + // legacy "premium"/"standard" → legend/pro + const plan = planRaw === "premium" ? "legend" : planRaw === "standard" ? "pro" : planRaw; llmProvider = plan === "legend" ? "openrouter-haiku" : "groq-llama"; } let upstreamUrl: string;