Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -5,29 +5,30 @@ from huggingface_hub import InferenceClient
|
|
| 5 |
# Token aus den Hugging Face Secrets laden
|
| 6 |
token = os.environ.get("HF_TOKEN")
|
| 7 |
text_client = InferenceClient("google/gemma-1.1-7b-it", token=token)
|
| 8 |
-
|
|
|
|
| 9 |
|
| 10 |
# Unser neues, ultra-atmosphärisches CSS (Gestaltungscode)
|
|
|
|
| 11 |
geiles_design = """
|
| 12 |
@import url('https://fonts.googleapis.com/css2?family=Special+Elite&display=swap');
|
| 13 |
|
| 14 |
body, .gradio-container {
|
| 15 |
-
/*
|
| 16 |
-
background-image: url('
|
| 17 |
background-size: cover !important;
|
| 18 |
background-position: center !important;
|
| 19 |
background-attachment: fixed !important;
|
| 20 |
font-family: sans-serif;
|
| 21 |
}
|
| 22 |
|
| 23 |
-
/* Der Hauptbereich wird schwarz und leicht durchsichtig */
|
| 24 |
.haupt-bereich {
|
| 25 |
background: rgba(0, 0, 0, 0.7) !important;
|
| 26 |
border-radius: 10px;
|
| 27 |
padding: 20px !important;
|
| 28 |
max-width: 800px !important;
|
| 29 |
margin: 0 auto !important;
|
| 30 |
-
backdrop-filter: blur(
|
| 31 |
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 32 |
}
|
| 33 |
|
|
@@ -45,25 +46,47 @@ h1 { color: #d4af37 !important; text-align: center; text-transform: uppercase; f
|
|
| 45 |
font-weight: bold;
|
| 46 |
}
|
| 47 |
|
| 48 |
-
/*
|
| 49 |
-
.
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
color: #1a1a1a !important;
|
| 52 |
-
font-family: 'Special Elite', monospace !important;
|
| 53 |
font-size: 1.3em !important;
|
| 54 |
line-height: 1.6 !important;
|
| 55 |
-
padding:
|
| 56 |
border-radius: 2px !important;
|
| 57 |
-
box-shadow:
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
border-left: 4px solid #8b0000 !important;
|
| 61 |
}
|
| 62 |
|
| 63 |
-
/*
|
| 64 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
-
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
input, textarea {
|
| 68 |
background: rgba(0, 0, 0, 0.6) !important;
|
| 69 |
color: #fff !important;
|
|
@@ -72,7 +95,6 @@ input, textarea {
|
|
| 72 |
border-radius: 5px !important;
|
| 73 |
}
|
| 74 |
|
| 75 |
-
/* Aggressiver Action-Button */
|
| 76 |
button {
|
| 77 |
background: #8b0000 !important;
|
| 78 |
color: #fff !important;
|
|
@@ -87,73 +109,112 @@ button {
|
|
| 87 |
button:hover { background: #aa0000 !important; }
|
| 88 |
"""
|
| 89 |
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
if not nachricht:
|
| 92 |
-
return
|
|
|
|
| 93 |
|
|
|
|
| 94 |
system_prompt = f"""Du bist 'Zombie Go'.
|
| 95 |
Inventar des Spielers: {aktueller_rucksack}.
|
| 96 |
-
Regel: Der Spieler darf NUR Dinge nutzen, die im Inventar sind.
|
| 97 |
-
Aktion: '{nachricht}'.
|
| 98 |
|
| 99 |
-
Antworte in
|
| 100 |
-
STORY: [
|
| 101 |
-
RUCKSACK: [
|
| 102 |
|
| 103 |
try:
|
| 104 |
-
antwort = text_client.text_generation(system_prompt, max_new_tokens=
|
| 105 |
|
|
|
|
| 106 |
if "RUCKSACK:" in antwort:
|
| 107 |
teile = antwort.split("RUCKSACK:")
|
| 108 |
story_text = teile[0].replace("STORY:", "").strip()
|
| 109 |
neuer_rucksack = teile[1].strip()
|
| 110 |
else:
|
|
|
|
| 111 |
story_text = antwort.replace("STORY:", "").strip()
|
| 112 |
neuer_rucksack = aktueller_rucksack
|
|
|
|
| 113 |
except Exception as e:
|
| 114 |
-
story_text = "
|
| 115 |
neuer_rucksack = aktueller_rucksack
|
| 116 |
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
try:
|
| 119 |
-
|
|
|
|
| 120 |
except:
|
| 121 |
bild = None
|
| 122 |
|
| 123 |
rucksack_ui_text = f"🎒 INVENTAR: {neuer_rucksack}"
|
| 124 |
|
| 125 |
-
|
|
|
|
| 126 |
|
| 127 |
|
|
|
|
| 128 |
with gr.Blocks(css=geiles_design, theme=gr.themes.Base()) as spiel_oberflaeche:
|
| 129 |
with gr.Column(elem_classes="haupt-bereich"):
|
| 130 |
gr.Markdown("# 🧟 ZOMBIE GO")
|
| 131 |
|
| 132 |
-
#
|
|
|
|
| 133 |
rucksack_speicher = gr.State("Brecheisen, leere Wasserflasche | GO: 0")
|
|
|
|
|
|
|
| 134 |
rucksack_ui = gr.Markdown("🎒 INVENTAR: Brecheisen, leere Wasserflasche | GO: 0", elem_classes="rucksack-anzeige")
|
| 135 |
|
| 136 |
-
# Das Bild
|
| 137 |
szene_bild = gr.Image(label="Visuelles Echo", type="pil", elem_classes="output-bild")
|
| 138 |
|
| 139 |
-
#
|
| 140 |
-
|
| 141 |
-
|
| 142 |
|
| 143 |
gr.Markdown("---")
|
| 144 |
|
| 145 |
-
|
|
|
|
| 146 |
senden_btn = gr.Button("▶ AKTION EINTRAGEN")
|
| 147 |
|
|
|
|
|
|
|
| 148 |
senden_btn.click(
|
| 149 |
spiel_zug,
|
| 150 |
-
inputs=[eingabe, rucksack_speicher],
|
| 151 |
-
outputs=[
|
| 152 |
)
|
| 153 |
eingabe.submit(
|
| 154 |
spiel_zug,
|
| 155 |
-
inputs=[eingabe, rucksack_speicher],
|
| 156 |
-
outputs=[
|
| 157 |
)
|
| 158 |
|
|
|
|
| 159 |
spiel_oberflaeche.launch()
|
|
|
|
| 5 |
# Token aus den Hugging Face Secrets laden
|
| 6 |
token = os.environ.get("HF_TOKEN")
|
| 7 |
text_client = InferenceClient("google/gemma-1.1-7b-it", token=token)
|
| 8 |
+
# Wir nutzen SDXL Turbo für extrem schnelle Bilder am Handy!
|
| 9 |
+
bild_client = InferenceClient("stabilityai/sdxl-turbo", token=token)
|
| 10 |
|
| 11 |
# Unser neues, ultra-atmosphärisches CSS (Gestaltungscode)
|
| 12 |
+
# Wir fügen die Styles für das gestapelte Logbuch hinzu.
|
| 13 |
geiles_design = """
|
| 14 |
@import url('https://fonts.googleapis.com/css2?family=Special+Elite&display=swap');
|
| 15 |
|
| 16 |
body, .gradio-container {
|
| 17 |
+
/* Lädt das Bild DIREKT von deinem eigenen Hugging Face Space! */
|
| 18 |
+
background-image: url('file/image.png') !important;
|
| 19 |
background-size: cover !important;
|
| 20 |
background-position: center !important;
|
| 21 |
background-attachment: fixed !important;
|
| 22 |
font-family: sans-serif;
|
| 23 |
}
|
| 24 |
|
|
|
|
| 25 |
.haupt-bereich {
|
| 26 |
background: rgba(0, 0, 0, 0.7) !important;
|
| 27 |
border-radius: 10px;
|
| 28 |
padding: 20px !important;
|
| 29 |
max-width: 800px !important;
|
| 30 |
margin: 0 auto !important;
|
| 31 |
+
backdrop-filter: blur(2px);
|
| 32 |
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 33 |
}
|
| 34 |
|
|
|
|
| 46 |
font-weight: bold;
|
| 47 |
}
|
| 48 |
|
| 49 |
+
/* Rahmen für das generierte Bild */
|
| 50 |
+
.output-bild { border: 2px solid #555 !important; border-radius: 3px !important; box-shadow: 0 5px 15px rgba(0,0,0,0.9) !important;}
|
| 51 |
+
|
| 52 |
+
/* --- Das Logbuch --- */
|
| 53 |
+
/* Dies ist der Container für den Chat-Verlauf, der wie ein Stapel Notizen aussieht */
|
| 54 |
+
.logbuch-stapel {
|
| 55 |
+
margin: 20px 0 !important;
|
| 56 |
+
height: 400px !important;
|
| 57 |
+
overflow-y: scroll !important; /* Scrollen, wenn der Stapel zu lang wird */
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
/* Jeder Eintrag (dein Text ODER der KI-Text) wird zu einem eigenen Notizzettel */
|
| 61 |
+
.notizzettel-eintrag {
|
| 62 |
+
background: #e3dec6 !important;
|
| 63 |
color: #1a1a1a !important;
|
| 64 |
+
font-family: 'Special Elite', monospace !important;
|
| 65 |
font-size: 1.3em !important;
|
| 66 |
line-height: 1.6 !important;
|
| 67 |
+
padding: 15px !important;
|
| 68 |
border-radius: 2px !important;
|
| 69 |
+
box-shadow: 2px 2px 10px rgba(0,0,0,0.8) !important;
|
| 70 |
+
margin-bottom: -15px !important; /* Stapelungseffekt */
|
| 71 |
+
position: relative !important;
|
| 72 |
+
border-left: 4px solid #8b0000 !important;
|
| 73 |
}
|
| 74 |
|
| 75 |
+
/* Deine Notizen sind links */
|
| 76 |
+
.user-notiz {
|
| 77 |
+
transform: rotate(0.5deg);
|
| 78 |
+
margin-right: 15% !important;
|
| 79 |
+
border-left-color: #d4af37 !important;
|
| 80 |
+
opacity: 0.9;
|
| 81 |
+
}
|
| 82 |
|
| 83 |
+
/* Die Notizen der KI sind rechts */
|
| 84 |
+
.ai-notiz {
|
| 85 |
+
transform: rotate(-0.5deg);
|
| 86 |
+
margin-left: 15% !important;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/* --- Eingabe --- */
|
| 90 |
input, textarea {
|
| 91 |
background: rgba(0, 0, 0, 0.6) !important;
|
| 92 |
color: #fff !important;
|
|
|
|
| 95 |
border-radius: 5px !important;
|
| 96 |
}
|
| 97 |
|
|
|
|
| 98 |
button {
|
| 99 |
background: #8b0000 !important;
|
| 100 |
color: #fff !important;
|
|
|
|
| 109 |
button:hover { background: #aa0000 !important; }
|
| 110 |
"""
|
| 111 |
|
| 112 |
+
# Eine Hilfsfunktion, die den Chat-Verlauf in HTML-Notizzettel umwandelt
|
| 113 |
+
def generiere_logbuch_html(verlauf):
|
| 114 |
+
html = '<div class="logbuch-stapel">'
|
| 115 |
+
# Start-Text hinzufügen, wenn der Verlauf leer ist
|
| 116 |
+
if not verlauf:
|
| 117 |
+
start_text = "Tag 1. Mein Kopf dröhnt. Ich bin in einem verlassenen Supermarkt aufgewacht. Die Regale sind geplündert, draußen brennen Autos. Ich höre ein ekliges Kratzen an den vernagelten Fenstern. Neben mir liegt ein altes Brecheisen. Ich muss hier raus..."
|
| 118 |
+
html += f'<div class="notizzettel-eintrag ai-notiz"> {start_text} </div>'
|
| 119 |
+
else:
|
| 120 |
+
# Den Verlauf durchgehen und Notizen stapeln
|
| 121 |
+
for user_text, ai_text in verlauf:
|
| 122 |
+
if user_text:
|
| 123 |
+
html += f'<div class="notizzettel-eintrag user-notiz"> {user_text} </div>'
|
| 124 |
+
if ai_text:
|
| 125 |
+
html += f'<div class="notizzettel-eintrag ai-notiz"> {ai_text} </div>'
|
| 126 |
+
html += '</div>'
|
| 127 |
+
return html
|
| 128 |
+
|
| 129 |
+
def spiel_zug(nachricht, verlauf, aktueller_rucksack):
|
| 130 |
if not nachricht:
|
| 131 |
+
return (generiere_logbuch_html(verlauf), verlauf, None, aktueller_rucksack,
|
| 132 |
+
f"🎒 INVENTAR: {aktueller_rucksack}", "")
|
| 133 |
|
| 134 |
+
# Die KI soll die Story UND den Rucksack verwalten
|
| 135 |
system_prompt = f"""Du bist 'Zombie Go'.
|
| 136 |
Inventar des Spielers: {aktueller_rucksack}.
|
| 137 |
+
Regel: Der Spieler darf NUR Dinge nutzen, die im Inventar sind. Erfinde keine Waffen für ihn.
|
| 138 |
+
Aktion des Spielers: '{nachricht}'.
|
| 139 |
|
| 140 |
+
Antworte EXAKT in dieser Form (ohne Ausnahmen):
|
| 141 |
+
STORY: [Beschreibe in max 3 spannenden Sätzen, was passiert. Füge neu gefundene Gegenstände logisch zum Rucksack hinzu.]
|
| 142 |
+
RUCKSACK: [Liste der Items] | GO: [Anzahl]"""
|
| 143 |
|
| 144 |
try:
|
| 145 |
+
antwort = text_client.text_generation(system_prompt, max_new_tokens=250)
|
| 146 |
|
| 147 |
+
# Split (Text zerteilen): Wir trennen Story und Rucksack am Wort "RUCKSACK:"
|
| 148 |
if "RUCKSACK:" in antwort:
|
| 149 |
teile = antwort.split("RUCKSACK:")
|
| 150 |
story_text = teile[0].replace("STORY:", "").strip()
|
| 151 |
neuer_rucksack = teile[1].strip()
|
| 152 |
else:
|
| 153 |
+
# Falls die KI den Befehl vermasselt, behalten wir den alten Rucksack
|
| 154 |
story_text = antwort.replace("STORY:", "").strip()
|
| 155 |
neuer_rucksack = aktueller_rucksack
|
| 156 |
+
|
| 157 |
except Exception as e:
|
| 158 |
+
story_text = "Die Tinte versagt... (Verbindung zur KI unterbrochen)"
|
| 159 |
neuer_rucksack = aktueller_rucksack
|
| 160 |
|
| 161 |
+
# Den Chat-Verlauf aktualisieren
|
| 162 |
+
verlauf.append((nachricht, story_text))
|
| 163 |
+
|
| 164 |
+
# Das Logbuch-HTML neu generieren (inklusive neuer Aktionen)
|
| 165 |
+
logbuch_html = generiere_logbuch_html(verlauf)
|
| 166 |
+
|
| 167 |
+
# Bild-KI (SDXL Turbo - EXTREM SCHNELL!)
|
| 168 |
+
bild_prompt = f"dark zombie apocalypse survival scene, first person view, {nachricht}, eerie green lighting from glowing crystals, highly detailed, realistic texture, derelict ruined city, atmospheric"
|
| 169 |
try:
|
| 170 |
+
# SDXL Turbo braucht extrem wenige Schritte und ist fast sofort fertig!
|
| 171 |
+
bild = bild_client.text_to_image(bild_prompt, num_inference_steps=1)
|
| 172 |
except:
|
| 173 |
bild = None
|
| 174 |
|
| 175 |
rucksack_ui_text = f"🎒 INVENTAR: {neuer_rucksack}"
|
| 176 |
|
| 177 |
+
# Gibt alles zurück: Logbuch, Chat-History (gr.State), Bild, leeres Textfeld, schicke Rucksack-Anzeige, unsichtbarer Rucksack-Speicher
|
| 178 |
+
return logbuch_html, verlauf, bild, "", rucksack_ui_text, neuer_rucksack
|
| 179 |
|
| 180 |
|
| 181 |
+
# Wir laden das eigene CSS direkt in den Baukasten
|
| 182 |
with gr.Blocks(css=geiles_design, theme=gr.themes.Base()) as spiel_oberflaeche:
|
| 183 |
with gr.Column(elem_classes="haupt-bereich"):
|
| 184 |
gr.Markdown("# 🧟 ZOMBIE GO")
|
| 185 |
|
| 186 |
+
# Unsichtbare Speicher (gr.State)
|
| 187 |
+
chat_verlauf = gr.State([]) # Speichert den kompletten Verlauf
|
| 188 |
rucksack_speicher = gr.State("Brecheisen, leere Wasserflasche | GO: 0")
|
| 189 |
+
|
| 190 |
+
# Die sichtbare Anzeige für das Inventar
|
| 191 |
rucksack_ui = gr.Markdown("🎒 INVENTAR: Brecheisen, leere Wasserflasche | GO: 0", elem_classes="rucksack-anzeige")
|
| 192 |
|
| 193 |
+
# Das generierte Bild
|
| 194 |
szene_bild = gr.Image(label="Visuelles Echo", type="pil", elem_classes="output-bild")
|
| 195 |
|
| 196 |
+
# Das Logbuch (HTML Container für die gestapelten Notizen)
|
| 197 |
+
# Wir rufen direkt am Anfang die Funktion auf, um den Start-Text zu zeigen.
|
| 198 |
+
logbuch_ui = gr.HTML(generiere_logbuch_html([]), elem_classes="logbuch-stapel")
|
| 199 |
|
| 200 |
gr.Markdown("---")
|
| 201 |
|
| 202 |
+
# Eingabe-Bereich
|
| 203 |
+
eingabe = gr.Textbox(label="", placeholder="Was tust du? (z.B. Umschauen)", lines=1)
|
| 204 |
senden_btn = gr.Button("▶ AKTION EINTRAGEN")
|
| 205 |
|
| 206 |
+
# Verknüpfungen der Knöpfe (wichtige Reihenfolge der Outputs!)
|
| 207 |
+
# Wir aktualisieren jetzt das logbuch_ui und chat_verlauf
|
| 208 |
senden_btn.click(
|
| 209 |
spiel_zug,
|
| 210 |
+
inputs=[eingabe, chat_verlauf, rucksack_speicher],
|
| 211 |
+
outputs=[logbuch_ui, chat_verlauf, szene_bild, eingabe, rucksack_ui, rucksack_speicher]
|
| 212 |
)
|
| 213 |
eingabe.submit(
|
| 214 |
spiel_zug,
|
| 215 |
+
inputs=[eingabe, chat_verlauf, rucksack_speicher],
|
| 216 |
+
outputs=[logbuch_ui, chat_verlauf, szene_bild, eingabe, rucksack_ui, rucksack_speicher]
|
| 217 |
)
|
| 218 |
|
| 219 |
+
# Startet das Game
|
| 220 |
spiel_oberflaeche.launch()
|