Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -9,12 +9,12 @@ MODEL_ID = "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8"
|
|
| 9 |
|
| 10 |
# ββ System Prompts βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 11 |
|
| 12 |
-
PROMPT_TO_LINKEDIN = """Du bist ein LinkedIn-Influencer-Generator. Deine einzige Aufgabe ist es, banale,
|
| 13 |
|
| 14 |
Regeln:
|
| 15 |
- Alles ist eine "Journey", ein "Gamechanger" oder eine "powerful lesson"
|
| 16 |
- Nutze mindestens 3 Emojis strategisch
|
| 17 |
-
-
|
| 18 |
- Fuege eine persoenliche Anekdote hinzu, die niemand braucht
|
| 19 |
- Endet mit einer rhetorischen Frage an die Community
|
| 20 |
- Benutze dramatische Zeilenumbrueche fuer Effekt
|
|
@@ -23,7 +23,7 @@ Regeln:
|
|
| 23 |
- Klingt wie jemand, der gerade ein Buch ueber sich selbst schreiben wuerde
|
| 24 |
- Formatiere mit Markdown: Eroeffnungssatz als ## Ueberschrift, Schluesselbegriffe wie **Gamechanger**, **Growth**, **Mindset**, **Journey** fett hervorheben, Abschnitte mit Leerzeilen trennen
|
| 25 |
|
| 26 |
-
Antworte NUR mit dem LinkedIn-Post in Markdown. Kein Vorwort, keine
|
| 27 |
|
| 28 |
PROMPT_FROM_LINKEDIN = """Du bist ein gnadenloser semantischer Reduzierer. Du hasst Floskeln. Deine Aufgabe: LinkedIn-Texte auf das absolute, brutalste Minimum eindampfen.
|
| 29 |
|
|
@@ -31,29 +31,24 @@ Regeln:
|
|
| 31 |
- EIN Satz. Nicht zwei. Einer.
|
| 32 |
- Kuerze bis es wehtut. Dann nochmal kuerzen.
|
| 33 |
- Null Emotion, null Wertung, null Kontext der niemanden interessiert
|
| 34 |
-
- Streiche alles was keine neue Information
|
| 35 |
- Wenn der gesamte Post nur bedeutet "Ich hab heute Kaffee getrunken" schreib genau das
|
| 36 |
- Maximal 15 Woerter
|
| 37 |
|
| 38 |
-
Antworte NUR mit diesem einen Satz. Kein Vorwort, keine
|
| 39 |
|
| 40 |
-
PROMPT_BINGO = """
|
| 41 |
|
| 42 |
-
|
| 43 |
|
| 44 |
-
|
| 45 |
-
{"metrics":[{"
|
| 46 |
|
| 47 |
-
|
| 48 |
-
1.
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
5. icon="Sinnlosigkeits-Index" label="Sinnlosigkeits-Index" - Koennte man den Post loeschen ohne Informationsverlust?
|
| 53 |
-
|
| 54 |
-
score ist ein Integer von 1-10. 10 = maximaler LinkedIn-Exzess.
|
| 55 |
-
comment ist max. 6 Woerter, sarkastisch.
|
| 56 |
-
verdict ist ein einziger vernichtender Satz, max. 12 Woerter."""
|
| 57 |
|
| 58 |
|
| 59 |
# ββ LLM-Calls βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -81,17 +76,43 @@ def translate(text, direction):
|
|
| 81 |
return f"Fehler: {e}"
|
| 82 |
|
| 83 |
|
| 84 |
-
def
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
try:
|
| 88 |
-
raw = _call_llm(PROMPT_BINGO, text, max_tokens=512)
|
| 89 |
start = raw.find("{")
|
| 90 |
end = raw.rfind("}") + 1
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
except Exception
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
|
| 97 |
def _render_bingo(data):
|
|
@@ -100,8 +121,8 @@ def _render_bingo(data):
|
|
| 100 |
|
| 101 |
ICONS = {
|
| 102 |
"Buzzword-Dichte": "π£οΈ",
|
| 103 |
-
"
|
| 104 |
-
"
|
| 105 |
"Hashtag-Overload": "#οΈβ£",
|
| 106 |
"Sinnlosigkeits-Index": "π",
|
| 107 |
}
|
|
|
|
| 9 |
|
| 10 |
# ββ System Prompts βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 11 |
|
| 12 |
+
PROMPT_TO_LINKEDIN = """Du bist ein LinkedIn-Influencer-Generator. Deine einzige Aufgabe ist es, banale, alltaegliche Aussagen in absurd ausschweifende, klischeebeladene LinkedIn-Posts zu verwandeln.
|
| 13 |
|
| 14 |
Regeln:
|
| 15 |
- Alles ist eine "Journey", ein "Gamechanger" oder eine "powerful lesson"
|
| 16 |
- Nutze mindestens 3 Emojis strategisch
|
| 17 |
+
- Erwaehne "Growth", "Mindset", "Passion" oder "Impact" wo immer moeglich
|
| 18 |
- Fuege eine persoenliche Anekdote hinzu, die niemand braucht
|
| 19 |
- Endet mit einer rhetorischen Frage an die Community
|
| 20 |
- Benutze dramatische Zeilenumbrueche fuer Effekt
|
|
|
|
| 23 |
- Klingt wie jemand, der gerade ein Buch ueber sich selbst schreiben wuerde
|
| 24 |
- Formatiere mit Markdown: Eroeffnungssatz als ## Ueberschrift, Schluesselbegriffe wie **Gamechanger**, **Growth**, **Mindset**, **Journey** fett hervorheben, Abschnitte mit Leerzeilen trennen
|
| 25 |
|
| 26 |
+
Antworte NUR mit dem LinkedIn-Post in Markdown. Kein Vorwort, keine Erklaerung."""
|
| 27 |
|
| 28 |
PROMPT_FROM_LINKEDIN = """Du bist ein gnadenloser semantischer Reduzierer. Du hasst Floskeln. Deine Aufgabe: LinkedIn-Texte auf das absolute, brutalste Minimum eindampfen.
|
| 29 |
|
|
|
|
| 31 |
- EIN Satz. Nicht zwei. Einer.
|
| 32 |
- Kuerze bis es wehtut. Dann nochmal kuerzen.
|
| 33 |
- Null Emotion, null Wertung, null Kontext der niemanden interessiert
|
| 34 |
+
- Streiche alles was keine neue Information traegt
|
| 35 |
- Wenn der gesamte Post nur bedeutet "Ich hab heute Kaffee getrunken" schreib genau das
|
| 36 |
- Maximal 15 Woerter
|
| 37 |
|
| 38 |
+
Antworte NUR mit diesem einen Satz. Kein Vorwort, keine Erklaerung."""
|
| 39 |
|
| 40 |
+
PROMPT_BINGO = """You are a sarcastic LinkedIn prose analyst. Rate the following LinkedIn post on 5 metrics.
|
| 41 |
|
| 42 |
+
Reply ONLY with a single valid JSON object. No markdown fences, no backticks, no explanation before or after. Raw JSON only.
|
| 43 |
|
| 44 |
+
Use exactly this structure:
|
| 45 |
+
{"metrics":[{"label":"Buzzword-Dichte","score":7,"comment":"kurz sarkastisch"},{"label":"Laenge vs. Inhalt","score":5,"comment":"kurz sarkastisch"},{"label":"Selbstbeweihraeuche","score":8,"comment":"kurz sarkastisch"},{"label":"Hashtag-Overload","score":3,"comment":"kurz sarkastisch"},{"label":"Sinnlosigkeits-Index","score":6,"comment":"kurz sarkastisch"}],"verdict":"Ein vernichtender Satz auf Deutsch."}
|
| 46 |
|
| 47 |
+
Rules:
|
| 48 |
+
- score is an integer 1-10. 10 = worst LinkedIn excess possible.
|
| 49 |
+
- comment is max 5 words in German, sarcastic. Use only basic ASCII characters, no special chars.
|
| 50 |
+
- verdict is one devastating sentence in German, max 12 words. Use only basic ASCII characters.
|
| 51 |
+
- ALL string values must be valid JSON strings. Escape nothing special, just use plain ASCII."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
|
| 54 |
# ββ LLM-Calls βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 76 |
return f"Fehler: {e}"
|
| 77 |
|
| 78 |
|
| 79 |
+
def _extract_json(raw: str) -> dict:
|
| 80 |
+
"""Mehrere Strategien um JSON aus LLM-Antwort zu extrahieren."""
|
| 81 |
+
# 1. Direkt parsen
|
| 82 |
+
try:
|
| 83 |
+
return json.loads(raw)
|
| 84 |
+
except Exception:
|
| 85 |
+
pass
|
| 86 |
+
# 2. JSON-Block per Index extrahieren
|
| 87 |
try:
|
|
|
|
| 88 |
start = raw.find("{")
|
| 89 |
end = raw.rfind("}") + 1
|
| 90 |
+
if start >= 0 and end > start:
|
| 91 |
+
return json.loads(raw[start:end])
|
| 92 |
+
except Exception:
|
| 93 |
+
pass
|
| 94 |
+
# 3. Whitespace bereinigen und nochmal versuchen
|
| 95 |
+
try:
|
| 96 |
+
cleaned = raw[raw.find("{"):raw.rfind("}")+1]
|
| 97 |
+
cleaned = " ".join(cleaned.split())
|
| 98 |
+
return json.loads(cleaned)
|
| 99 |
+
except Exception:
|
| 100 |
+
pass
|
| 101 |
+
raise ValueError("Kein valides JSON gefunden")
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def get_bingo(text: str) -> str:
|
| 105 |
+
if not text.strip() or not HF_TOKEN:
|
| 106 |
+
return ""
|
| 107 |
+
last_err = None
|
| 108 |
+
for attempt in range(3):
|
| 109 |
+
try:
|
| 110 |
+
raw = _call_llm(PROMPT_BINGO, text, max_tokens=600)
|
| 111 |
+
data = _extract_json(raw)
|
| 112 |
+
return _render_bingo(data)
|
| 113 |
+
except Exception as e:
|
| 114 |
+
last_err = e
|
| 115 |
+
return f"<p style='color:#c00;font-size:.85rem;padding:8px;'>Analyse fehlgeschlagen nach 3 Versuchen: {last_err}</p>"
|
| 116 |
|
| 117 |
|
| 118 |
def _render_bingo(data):
|
|
|
|
| 121 |
|
| 122 |
ICONS = {
|
| 123 |
"Buzzword-Dichte": "π£οΈ",
|
| 124 |
+
"Laenge vs. Inhalt": "π",
|
| 125 |
+
"Selbstbeweihraeuche": "πͺ",
|
| 126 |
"Hashtag-Overload": "#οΈβ£",
|
| 127 |
"Sinnlosigkeits-Index": "π",
|
| 128 |
}
|