fix(sos-stream): profile is not defined ReferenceError

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) <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-05-07 21:03:27 +02:00
parent e53911e844
commit 82c6c61a5e

View File

@ -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<ReturnType<typeof getProfile>> = 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<string, string> = {
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<string, string> = {
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;