Vorschlagssystem: Sonnet-Prompt, Auto-Reject, Titellimit
- Sonnet: max 3 Lösungen, echte Umlaute erzwingen - Auto-Reject: deactivate_source wird abgelehnt wenn fix_url akzeptiert - Titel-Limit von 80 auf 120 Zeichen erhöht Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -1,9 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
"""Grundquellen-Verwaltung und Kundenquellen-Übersicht."""
|
"""Grundquellen-Verwaltung und Kundenquellen-Übersicht."""
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Monitor-Source-Rules verfügbar machen
|
# Monitor-Source-Rules verfügbar machen
|
||||||
sys.path.insert(0, "/home/claude-dev/AegisSight-Monitor/src")
|
sys.path.insert(0, "/home/claude-dev/AegisSight-Monitor/src")
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
@@ -297,7 +297,7 @@ async def add_discovered_sources(
|
|||||||
existing_urls.add(feed["url"])
|
existing_urls.add(feed["url"])
|
||||||
added += 1
|
added += 1
|
||||||
|
|
||||||
# Web-Source für die Domain anlegen wenn noch nicht vorhanden
|
# Web-Source für die Domain anlegen wenn noch nicht vorhanden
|
||||||
if feeds and feeds[0].get("domain"):
|
if feeds and feeds[0].get("domain"):
|
||||||
domain = feeds[0]["domain"]
|
domain = feeds[0]["domain"]
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
@@ -318,7 +318,7 @@ async def add_discovered_sources(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# --- Health-Check & Vorschläge ---
|
# --- Health-Check & Vorschläge ---
|
||||||
|
|
||||||
@router.get("/health")
|
@router.get("/health")
|
||||||
async def get_health(
|
async def get_health(
|
||||||
@@ -326,7 +326,7 @@ async def get_health(
|
|||||||
db: aiosqlite.Connection = Depends(db_dependency),
|
db: aiosqlite.Connection = Depends(db_dependency),
|
||||||
):
|
):
|
||||||
"""Health-Check-Ergebnisse abrufen."""
|
"""Health-Check-Ergebnisse abrufen."""
|
||||||
# Prüfen ob Tabelle existiert
|
# Prüfen ob Tabelle existiert
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='source_health_checks'"
|
"SELECT name FROM sqlite_master WHERE type='table' AND name='source_health_checks'"
|
||||||
)
|
)
|
||||||
@@ -368,7 +368,7 @@ async def get_suggestions(
|
|||||||
admin: dict = Depends(get_current_admin),
|
admin: dict = Depends(get_current_admin),
|
||||||
db: aiosqlite.Connection = Depends(db_dependency),
|
db: aiosqlite.Connection = Depends(db_dependency),
|
||||||
):
|
):
|
||||||
"""Alle Vorschläge abrufen (pending zuerst, dann letzte 20 bearbeitete)."""
|
"""Alle Vorschläge abrufen (pending zuerst, dann letzte 20 bearbeitete)."""
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='source_suggestions'"
|
"SELECT name FROM sqlite_master WHERE type='table' AND name='source_suggestions'"
|
||||||
)
|
)
|
||||||
@@ -431,7 +431,7 @@ async def update_suggestion(
|
|||||||
"SELECT id FROM sources WHERE url = ? AND tenant_id IS NULL", (url,)
|
"SELECT id FROM sources WHERE url = ? AND tenant_id IS NULL", (url,)
|
||||||
)
|
)
|
||||||
if await cursor.fetchone():
|
if await cursor.fetchone():
|
||||||
result_action = "übersprungen (URL bereits vorhanden)"
|
result_action = "übersprungen (URL bereits vorhanden)"
|
||||||
new_status = "rejected"
|
new_status = "rejected"
|
||||||
else:
|
else:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
@@ -441,7 +441,7 @@ async def update_suggestion(
|
|||||||
)
|
)
|
||||||
result_action = f"Quelle '{name}' angelegt"
|
result_action = f"Quelle '{name}' angelegt"
|
||||||
else:
|
else:
|
||||||
result_action = "übersprungen (keine URL)"
|
result_action = "übersprungen (keine URL)"
|
||||||
new_status = "rejected"
|
new_status = "rejected"
|
||||||
|
|
||||||
elif stype == "deactivate_source":
|
elif stype == "deactivate_source":
|
||||||
@@ -454,7 +454,7 @@ async def update_suggestion(
|
|||||||
source_id = suggestion["source_id"]
|
source_id = suggestion["source_id"]
|
||||||
if source_id:
|
if source_id:
|
||||||
await db.execute("DELETE FROM sources WHERE id = ?", (source_id,))
|
await db.execute("DELETE FROM sources WHERE id = ?", (source_id,))
|
||||||
result_action = "Quelle gelöscht"
|
result_action = "Quelle gelöscht"
|
||||||
|
|
||||||
elif stype == "fix_url":
|
elif stype == "fix_url":
|
||||||
source_id = suggestion["source_id"]
|
source_id = suggestion["source_id"]
|
||||||
@@ -463,6 +463,15 @@ async def update_suggestion(
|
|||||||
await db.execute("UPDATE sources SET url = ? WHERE id = ?", (new_url, source_id))
|
await db.execute("UPDATE sources SET url = ? WHERE id = ?", (new_url, source_id))
|
||||||
result_action = f"URL aktualisiert"
|
result_action = f"URL aktualisiert"
|
||||||
|
|
||||||
|
# Auto-Reject: Wenn fix_url oder add_source akzeptiert wird,
|
||||||
|
# zugehörige deactivate_source-Vorschläge automatisch ablehnen
|
||||||
|
if stype in ("fix_url", "add_source") and suggestion.get("source_id"):
|
||||||
|
await db.execute(
|
||||||
|
"UPDATE source_suggestions SET status = 'rejected', reviewed_at = CURRENT_TIMESTAMP "
|
||||||
|
"WHERE source_id = ? AND suggestion_type = 'deactivate_source' AND status = 'pending' AND id != ?",
|
||||||
|
(suggestion["source_id"], suggestion_id),
|
||||||
|
)
|
||||||
|
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"UPDATE source_suggestions SET status = ?, reviewed_at = CURRENT_TIMESTAMP WHERE id = ?",
|
"UPDATE source_suggestions SET status = ?, reviewed_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||||
(new_status, suggestion_id),
|
(new_status, suggestion_id),
|
||||||
@@ -525,7 +534,7 @@ async def search_fix_for_source(
|
|||||||
admin: dict = Depends(get_current_admin),
|
admin: dict = Depends(get_current_admin),
|
||||||
db: aiosqlite.Connection = Depends(db_dependency),
|
db: aiosqlite.Connection = Depends(db_dependency),
|
||||||
):
|
):
|
||||||
"""Sonnet mit WebSearch nach Lösung für eine kaputte Quelle suchen lassen."""
|
"""Sonnet mit WebSearch nach Lösung für eine kaputte Quelle suchen lassen."""
|
||||||
import json as _json
|
import json as _json
|
||||||
|
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
@@ -538,7 +547,7 @@ async def search_fix_for_source(
|
|||||||
|
|
||||||
source = dict(source)
|
source = dict(source)
|
||||||
|
|
||||||
# Health-Check-Probleme für diese Quelle laden
|
# Health-Check-Probleme für diese Quelle laden
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
"SELECT check_type, status, message FROM source_health_checks WHERE source_id = ?",
|
"SELECT check_type, status, message FROM source_health_checks WHERE source_id = ?",
|
||||||
(source_id,),
|
(source_id,),
|
||||||
@@ -557,10 +566,14 @@ Kategorie: {source['category']}
|
|||||||
Probleme:
|
Probleme:
|
||||||
{issues_text}
|
{issues_text}
|
||||||
|
|
||||||
Aufgabe: Suche im Internet nach funktionierenden Alternativen für diese Quelle.
|
Aufgabe: Suche im Internet nach funktionierenden Alternativen für diese Quelle.
|
||||||
- Finde konkrete RSS-Feed-URLs die tatsächlich funktionieren
|
- Finde konkrete RSS-Feed-URLs die tatsächlich funktionieren
|
||||||
- Prüfe ob es alternative Zugangswege gibt (andere Subdomains, Feed-Aggregatoren, alternative URLs)
|
- Prüfe ob es alternative Zugangswege gibt (andere Subdomains, Feed-Aggregatoren, alternative URLs)
|
||||||
- Gibt es eine Lösung oder ist die Quelle nur noch per WebSearch erreichbar?
|
- Gibt es eine Lösung oder ist die Quelle nur noch per WebSearch erreichbar?
|
||||||
|
|
||||||
|
Regeln:
|
||||||
|
- Maximal 3 Lösungen vorschlagen (die besten)
|
||||||
|
- Verwende echte deutsche Umlaute (ü, ä, ö, ß), keine Umschreibungen (ue, ae, oe, ss)
|
||||||
|
|
||||||
Antworte NUR mit einem JSON-Objekt:
|
Antworte NUR mit einem JSON-Objekt:
|
||||||
{{
|
{{
|
||||||
@@ -570,10 +583,10 @@ Antworte NUR mit einem JSON-Objekt:
|
|||||||
"type": "replace_url|add_feed|deactivate",
|
"type": "replace_url|add_feed|deactivate",
|
||||||
"name": "Anzeigename",
|
"name": "Anzeigename",
|
||||||
"url": "https://...",
|
"url": "https://...",
|
||||||
"description": "Kurze Begründung"
|
"description": "Kurze Begründung"
|
||||||
}}
|
}}
|
||||||
],
|
],
|
||||||
"summary": "Zusammenfassung in 1-2 Sätzen"
|
"summary": "Zusammenfassung in 1-2 Sätzen"
|
||||||
}}
|
}}
|
||||||
|
|
||||||
Nur das JSON, kein anderer Text."""
|
Nur das JSON, kein anderer Text."""
|
||||||
@@ -591,7 +604,7 @@ Nur das JSON, kein anderer Text."""
|
|||||||
else:
|
else:
|
||||||
result = {"fixable": False, "solutions": [], "summary": response[:500]}
|
result = {"fixable": False, "solutions": [], "summary": response[:500]}
|
||||||
|
|
||||||
# Lösungen als Vorschläge speichern
|
# Lösungen als Vorschläge speichern
|
||||||
await db.executescript("""
|
await db.executescript("""
|
||||||
CREATE TABLE IF NOT EXISTS source_suggestions (
|
CREATE TABLE IF NOT EXISTS source_suggestions (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -615,7 +628,7 @@ Nur das JSON, kein anderer Text."""
|
|||||||
"deactivate": "deactivate_source",
|
"deactivate": "deactivate_source",
|
||||||
}.get(sol_type, "add_source")
|
}.get(sol_type, "add_source")
|
||||||
|
|
||||||
title = f"{source['name']}: {sol.get('description', sol_type)[:80]}"
|
title = f"{source['name']}: {sol.get('description', sol_type)[:120]}"
|
||||||
|
|
||||||
# Duplikat-Check
|
# Duplikat-Check
|
||||||
cursor = await db.execute(
|
cursor = await db.execute(
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren