import json from . import config _client = None def _client_or_none(): global _client if not config.AI_ENABLED: return None if _client is None: from openai import OpenAI _client = OpenAI(api_key=config.OPENAI_API_KEY) return _client SYSTEM_PROMPT = ( "Du bist ein Assistent, der Web-Links organisiert. Analysiere den gegebenen " "Inhalt und liefere strukturierte Metadaten als JSON. Verwende möglichst eine " "der bereits existierenden Kategorien bzw. Hersteller/Quellen, wenn sie passt; " "sonst erstelle eine kurze, allgemeingültige neue Bezeichnung. Antworte " "ausschließlich auf Deutsch." ) def categorize( url: str, title: str, text: str, existing_categories: list[str], existing_manufacturers: list[str], ) -> dict: """Erzeuge Titel, Zusammenfassung, Kategorie, Hersteller und Tags.""" fallback = { "title": title or url, "summary": "", "category": "Sonstiges", "manufacturer": "", "tags": [], } client = _client_or_none() if client is None: return fallback user_prompt = f"""URL: {url} Seitentitel: {title or "(unbekannt)"} Bereits vorhandene Kategorien: {", ".join(existing_categories) or "(noch keine)"} Bereits vorhandene Hersteller/Quellen: {", ".join(existing_manufacturers) or "(noch keine)"} Seiteninhalt (gekürzt): {text[:4000] or "(kein Inhalt abrufbar - nutze URL und Titel)"} Gib ein JSON-Objekt mit genau diesen Feldern zurück: - "title": kurzer, aussagekräftiger Titel - "summary": 2-3 Sätze Zusammenfassung, worum es geht - "category": eine passende Themen-Kategorie - "manufacturer": Hersteller, Marke oder Quelle (leer lassen, wenn nicht erkennbar) - "tags": Array aus 3-6 kurzen Schlagwörtern """ try: resp = client.chat.completions.create( model=config.OPENAI_MODEL, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_prompt}, ], response_format={"type": "json_object"}, temperature=0.2, ) data = json.loads(resp.choices[0].message.content) except Exception: return fallback tags = data.get("tags") or [] if not isinstance(tags, list): tags = [] return { "title": (str(data.get("title") or title or url)).strip()[:300], "summary": (str(data.get("summary") or "")).strip()[:1000], "category": (str(data.get("category") or "Sonstiges")).strip()[:120] or "Sonstiges", "manufacturer": (str(data.get("manufacturer") or "")).strip()[:120], "tags": [str(t).strip()[:40] for t in tags if str(t).strip()][:8], } PROMPT_SYSTEM_PROMPT = ( "Du bist ein Assistent, der eine Sammlung von KI-Prompts organisiert. " "Analysiere den gegebenen Prompt-Text und liefere strukturierte Metadaten " "als JSON. Verwende möglichst eine der bereits existierenden Kategorien, " "wenn sie passt; sonst erstelle eine kurze, allgemeingültige neue " "Bezeichnung (z.B. Schreiben, Code, Bild, Analyse, Sonstiges). Antworte " "ausschließlich auf Deutsch." ) def categorize_prompt(content: str, existing_categories: list[str]) -> dict: """Erzeuge Titel, Kategorie und Tags für einen gespeicherten Prompt.""" fallback_title = (content.strip().splitlines() or [""])[0].strip()[:80] or "Prompt" fallback = {"title": fallback_title, "category": "Sonstiges", "tags": []} client = _client_or_none() if client is None: return fallback user_prompt = f"""Prompt-Text: {content[:4000]} Bereits vorhandene Kategorien: {", ".join(existing_categories) or "(noch keine)"} Gib ein JSON-Objekt mit genau diesen Feldern zurück: - "title": kurzer, aussagekräftiger Titel (max. 80 Zeichen) - "category": eine passende Kategorie - "tags": Array aus 3-6 kurzen Schlagwörtern """ try: resp = client.chat.completions.create( model=config.OPENAI_MODEL, messages=[ {"role": "system", "content": PROMPT_SYSTEM_PROMPT}, {"role": "user", "content": user_prompt}, ], response_format={"type": "json_object"}, temperature=0.2, ) data = json.loads(resp.choices[0].message.content) except Exception: return fallback tags = data.get("tags") or [] if not isinstance(tags, list): tags = [] return { "title": (str(data.get("title") or fallback_title)).strip()[:120] or fallback_title, "category": (str(data.get("category") or "Sonstiges")).strip()[:120] or "Sonstiges", "tags": [str(t).strip()[:40] for t in tags if str(t).strip()][:8], } def embed(text: str) -> list[float]: """Erzeuge einen Embedding-Vektor für die semantische Suche.""" client = _client_or_none() if client is None or not text.strip(): return [] try: resp = client.embeddings.create( model=config.EMBEDDING_MODEL, input=text[:8000] ) return list(resp.data[0].embedding) except Exception: return []