Chat: Klickbare Links zu Beiträgen ermöglicht

- Backend: Markdown-Links [text](/beitrag/ID) → HTML <a> im Reply
- Backend: [text](https://...) → klickbare externe Links
- Backend: \n → <br> für korrekte Darstellung mit innerHTML
- Frontend: textContent → innerHTML (Links werden klickbar)
- Ausverkauft-Meldungen: Beitrags-ID im Context für Verlinkung
- System-Prompt: Klarere Link-Anweisungen für LLM
This commit is contained in:
Hermes Agent
2026-06-19 20:28:06 +02:00
parent 0b970f06ed
commit fa63d228f3
+21 -9
View File
@@ -3505,7 +3505,7 @@ def public_frontend():
const div = document.createElement('div'); const div = document.createElement('div');
div.className = 'chat-msg ' + role; div.className = 'chat-msg ' + role;
div.textContent = text; div.innerHTML = text;
wrapper.appendChild(div); wrapper.appendChild(div);
if (role === 'assistant') { if (role === 'assistant') {
@@ -4104,7 +4104,7 @@ def api_chat():
try: try:
for t in meaningful_terms: for t in meaningful_terms:
rows = db.execute( rows = db.execute(
"SELECT spiel_titel, content, halle, published_at FROM submissions " "SELECT id, spiel_titel, content, halle, published_at FROM submissions "
"WHERE status='published' AND deleted_at IS NULL " "WHERE status='published' AND deleted_at IS NULL "
"AND category='ausverkauft' AND spiel_titel IS NOT NULL " "AND category='ausverkauft' AND spiel_titel IS NOT NULL "
"AND LOWER(spiel_titel) LIKE ? LIMIT 5", "AND LOWER(spiel_titel) LIKE ? LIMIT 5",
@@ -4122,7 +4122,7 @@ def api_chat():
halle = r['halle'] or '' halle = r['halle'] or ''
snippet = (r['content'] or '').strip()[:120] snippet = (r['content'] or '').strip()[:120]
context_parts.append( context_parts.append(
f"[Ausverkauft-Meldung] {title}" f"[Beitrag #{r['id']}] 🔴 AUSVERKAUFT: {title}"
+ (f" | {halle}" if halle else '') + (f" | {halle}" if halle else '')
+ (f" | {snippet}" if snippet else '') + (f" | {snippet}" if snippet else '')
) )
@@ -4142,12 +4142,10 @@ def api_chat():
"- [Aussteller]-Einträge enthalten Verlage/Shops mit Halle, Stand und Land.\n" "- [Aussteller]-Einträge enthalten Verlage/Shops mit Halle, Stand und Land.\n"
"- [Neuheit]-Einträge enthalten Brettspiel-Neuheiten mit Verlag, Halle, Stand und Kategorien.\n" "- [Neuheit]-Einträge enthalten Brettspiel-Neuheiten mit Verlag, Halle, Stand und Kategorien.\n"
"- [Neuheit] mit 'AUSVERKAUFT' = am Stand vergriffen — erwähne das aktiv.\n" "- [Neuheit] mit 'AUSVERKAUFT' = am Stand vergriffen — erwähne das aktiv.\n"
"- [Ausverkauft-Meldung]-Einträge = Community-Berichte über vergriffene Spiele.\n" "- [Beitrag #ID]-Einträge = Community-Beiträge und Ausverkauft-Meldungen.\n"
"- Bei Fragen wie 'ist X noch da?' prüfe Ausverkauft-Status und warne.\n" "- Verlinke Community-Beiträge IMMER so: [Kurzbeschreibung](/beitrag/ID)\n"
"- Wenn du einen Beitrag erwähnst, verlinke ihn IMMER mit der Beitrags-ID aus dem Kontext.\n" " Beispiel: 'Lies [diesen Erfahrungsbericht](/beitrag/53)!'\n"
" Format: [Kurzbeschreibung](/beitrag/ID)\n" " Nimm die ID aus dem [Beitrag #ID]-Tag im Kontext.\n\n"
" Beispiel: 'Schau dir [diesen Erfahrungsbericht](/beitrag/53) an!'\n"
" Nutze die Nummer aus 'Beitrag #ID' im Kontext als Linkziel.\n\n"
f"KONTEXT (veröffentlichte Community-Beiträge + SPIEL Essen Aussteller):\n{context[:10000]}" f"KONTEXT (veröffentlichte Community-Beiträge + SPIEL Essen Aussteller):\n{context[:10000]}"
) )
@@ -4183,6 +4181,20 @@ def api_chat():
if r.status_code != 200: if r.status_code != 200:
return jsonify({'reply': 'Der BSN Assistent antwortet gerade nicht. Bitte versuch es später nochmal.'}) return jsonify({'reply': 'Der BSN Assistent antwortet gerade nicht. Bitte versuch es später nochmal.'})
reply = r.json()["choices"][0]["message"]["content"].strip() reply = r.json()["choices"][0]["message"]["content"].strip()
# Markdown-Links [text](url) → klickbare HTML-Links
reply = re.sub(
r'\[([^\]]+)\]\((https?://[^)]+)\)',
r'<a href="\2" target="_blank" rel="noopener" style="color:#20228a;text-decoration:underline;">\1</a>',
reply
)
# Relative Links /beitrag/ID
reply = re.sub(
r'\[([^\]]+)\]\((/beitrag/\d+)\)',
r'<a href="\2" style="color:#20228a;text-decoration:underline;">\1</a>',
reply
)
# Zeilenumbrüche in <br> für innerHTML-Darstellung
reply = reply.replace('\n', '<br>')
except Exception: except Exception:
return jsonify({'reply': 'Der BSN Assistent antwortet gerade nicht. Bitte versuch es später nochmal.'}) return jsonify({'reply': 'Der BSN Assistent antwortet gerade nicht. Bitte versuch es später nochmal.'})