fix(coach): markdown-strip safety-net for LLM responses
LLMs (especially Haiku) keep emitting markdown despite explicit "no markdown" prompt rule. Mobile app has no markdown renderer — users see raw asterisks. - New stripMarkdown() util handles **bold**, bullet-lists, headings, code-fences, links, blockquotes - /api/coach/message: applies stripMarkdown(text) post-LLM as safety-net Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b40b8465b9
commit
30ed4191b6
@ -243,6 +243,7 @@ import { PLAN_LIMITS } from "../../utils/plan-features";
|
||||
import { usePrisma } from "../../utils/prisma";
|
||||
import { getMemoriesForUser, markReferenced } from "../../db/lyraMemory";
|
||||
import { extractAndStoreMemories } from "../../utils/lyraMemoryExtract";
|
||||
import { stripMarkdown } from "../../utils/strip-markdown";
|
||||
|
||||
/**
|
||||
* Lyra-Plan-Beschreibung dynamisch aus PLAN_LIMITS generieren.
|
||||
@ -599,6 +600,11 @@ export default defineEventHandler(async (event) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Markdown-Strip safety-net: trotz expliziter Prompt-Regel emittieren manche
|
||||
// Modelle (insbesondere Haiku) weiterhin **bold** + Bullet-Lists. RN-Mobile
|
||||
// rendert kein Markdown — User sieht rohe Sterne. Hier final cleanen.
|
||||
text = stripMarkdown(text);
|
||||
|
||||
const feedbackSaved = await feedbackPromise;
|
||||
|
||||
// Memory: markReferenced + Extraction fire-and-forget
|
||||
|
||||
58
backend/server/utils/strip-markdown.ts
Normal file
58
backend/server/utils/strip-markdown.ts
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Entfernt Markdown-Formatierung aus LLM-Antworten.
|
||||
*
|
||||
* Hintergrund: Trotz expliziter "kein Markdown"-Anweisung im System-Prompt
|
||||
* emittieren manche Modelle (insbesondere Claude Haiku) weiterhin **bold**,
|
||||
* Bullet-Lists und ähnliches. Das wirkt in der Mobile-App unsauber, weil dort
|
||||
* kein Markdown-Renderer aktiv ist — User sehen rohe Sterne.
|
||||
*
|
||||
* Diese Funktion ist ein Safety-Net: nach LLM-Call angewendet, garantiert
|
||||
* sie sauberen Klartext, ohne den eigentlichen Inhalt zu beschädigen.
|
||||
*/
|
||||
export function stripMarkdown(input: string): string {
|
||||
if (!input) return input;
|
||||
|
||||
let out = input;
|
||||
|
||||
// Bold/italic: **text** / __text__ / *text* / _text_
|
||||
out = out.replace(/\*\*\*(.+?)\*\*\*/g, "$1");
|
||||
out = out.replace(/___(.+?)___/g, "$1");
|
||||
out = out.replace(/\*\*(.+?)\*\*/g, "$1");
|
||||
out = out.replace(/__(.+?)__/g, "$1");
|
||||
// Single * or _ als italic — nur wenn auf beiden Seiten Wort-Boundary,
|
||||
// sonst zerstören wir Listen-Bullets oder Multiplikationen
|
||||
out = out.replace(/(^|\s)\*([^\s*][^*]*?)\*(?=\s|$|[.,;:!?])/g, "$1$2");
|
||||
out = out.replace(/(^|\s)_([^\s_][^_]*?)_(?=\s|$|[.,;:!?])/g, "$1$2");
|
||||
|
||||
// Headings: # / ## / ### am Zeilenanfang
|
||||
out = out.replace(/^#{1,6}\s+/gm, "");
|
||||
|
||||
// Bullet-Lists: "- foo", "* foo", "+ foo" am Zeilenanfang
|
||||
// Behalte einfachen Aufzählungs-Bindestrich aber entferne markdown-marker-Charakter
|
||||
// → "- Schutz" wird "• Schutz" (Bullet ohne Markdown-Semantik)
|
||||
out = out.replace(/^[ \t]*[-*+][ \t]+/gm, "• ");
|
||||
|
||||
// Numbered Lists: "1. foo", "2. foo" am Zeilenanfang
|
||||
// Zahlen behalten (informativer als bullet) — nur falls strikt entfernt werden soll
|
||||
// out = out.replace(/^[ \t]*\d+\.[ \t]+/gm, "");
|
||||
|
||||
// Inline-Code: `text`
|
||||
out = out.replace(/`([^`]+)`/g, "$1");
|
||||
|
||||
// Code-Blocks: ```...```
|
||||
out = out.replace(/```[a-zA-Z]*\n?([\s\S]*?)```/g, "$1");
|
||||
|
||||
// Links: [text](url) → text
|
||||
out = out.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
||||
|
||||
// Blockquotes: "> foo" → "foo"
|
||||
out = out.replace(/^>[ \t]?/gm, "");
|
||||
|
||||
// Horizontal rules: --- / *** / ___ alleinstehend
|
||||
out = out.replace(/^[\t ]*[-*_]{3,}[\t ]*$/gm, "");
|
||||
|
||||
// Multiple Leerzeilen → max 1 Leerzeile
|
||||
out = out.replace(/\n{3,}/g, "\n\n");
|
||||
|
||||
return out.trim();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user