Update app.py
Browse files
app.py
CHANGED
|
@@ -1,139 +1,151 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
from
|
| 3 |
|
| 4 |
-
# ---
|
| 5 |
-
# We use the Instruct version of the 1B model as it follows rules best
|
| 6 |
-
MODEL_ID = "meta-llama/Llama-3.2-1B-Instruct"
|
| 7 |
|
| 8 |
-
#
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
RIDDLES = {
|
| 11 |
-
"
|
| 12 |
-
"scenario": "
|
| 13 |
-
"solution": "
|
| 14 |
},
|
| 15 |
-
"
|
| 16 |
-
"scenario": "
|
| 17 |
-
"solution": "
|
| 18 |
},
|
| 19 |
-
"
|
| 20 |
-
"scenario": "
|
| 21 |
-
"solution": "
|
| 22 |
},
|
| 23 |
-
"
|
| 24 |
-
"scenario": "
|
| 25 |
-
"solution": "
|
| 26 |
}
|
| 27 |
}
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
def respond(
|
| 30 |
message,
|
| 31 |
history: list[dict[str, str]],
|
| 32 |
-
|
| 33 |
max_tokens,
|
| 34 |
temperature,
|
| 35 |
top_p,
|
| 36 |
-
|
| 37 |
-
selected_riddle, # The dropdown input
|
| 38 |
):
|
| 39 |
-
|
| 40 |
-
This function handles the game logic. It injects the hidden solution
|
| 41 |
-
into the system prompt based on the user's selection.
|
| 42 |
-
"""
|
| 43 |
-
client = InferenceClient(token=hf_token.token, model=MODEL_ID)
|
| 44 |
-
|
| 45 |
-
# 1. Get the current riddle data
|
| 46 |
current_game = RIDDLES[selected_riddle]
|
| 47 |
|
| 48 |
-
# 2.
|
| 49 |
-
#
|
| 50 |
game_master_prompt = (
|
| 51 |
-
f"
|
| 52 |
-
f"
|
| 53 |
-
f"
|
| 54 |
-
f"\
|
| 55 |
-
f"
|
| 56 |
-
f"
|
| 57 |
-
f"
|
| 58 |
-
f"
|
| 59 |
-
f"4. If the user guesses the solution correctly, say 'CORRECT! You solved it: [Explain solution]'."
|
| 60 |
-
f"5. Do NOT give hints. Do NOT explain your 'Yes/No' answers."
|
| 61 |
)
|
| 62 |
|
| 63 |
-
# 3.
|
| 64 |
-
|
| 65 |
-
messages.extend(history)
|
| 66 |
-
messages.append({"role": "user", "content": message})
|
| 67 |
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
messages,
|
| 73 |
-
max_tokens=max_tokens,
|
| 74 |
-
stream=True,
|
| 75 |
-
temperature=temperature, # Low temp = more strict adherence to Yes/No
|
| 76 |
-
top_p=top_p,
|
| 77 |
-
):
|
| 78 |
-
choices = message.choices
|
| 79 |
-
token = ""
|
| 80 |
-
if len(choices) and choices[0].delta.content:
|
| 81 |
-
token = choices[0].delta.content
|
| 82 |
|
| 83 |
-
response += token
|
| 84 |
-
yield response
|
| 85 |
|
|
|
|
| 86 |
|
| 87 |
-
# --- UI SETUP ---
|
| 88 |
-
# We use Blocks to add the Dropdown menu cleanly above the chat
|
| 89 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 90 |
-
|
| 91 |
-
# Header
|
| 92 |
-
gr.Markdown("# 🕵️ The Yes/No Detective")
|
| 93 |
gr.Markdown(
|
| 94 |
-
"**
|
| 95 |
-
"
|
| 96 |
)
|
| 97 |
|
|
|
|
| 98 |
with gr.Row():
|
| 99 |
-
# The Dropdown to select the "Level"
|
| 100 |
riddle_select = gr.Dropdown(
|
| 101 |
choices=list(RIDDLES.keys()),
|
| 102 |
-
value="
|
| 103 |
-
label="
|
| 104 |
interactive=True
|
| 105 |
)
|
| 106 |
|
| 107 |
-
#
|
| 108 |
scenario_display = gr.Textbox(
|
| 109 |
-
label="
|
| 110 |
-
value=RIDDLES["
|
| 111 |
interactive=False
|
| 112 |
)
|
| 113 |
|
| 114 |
-
#
|
| 115 |
def update_scenario(choice):
|
| 116 |
return RIDDLES[choice]["scenario"]
|
| 117 |
|
| 118 |
riddle_select.change(fn=update_scenario, inputs=riddle_select, outputs=scenario_display)
|
| 119 |
|
| 120 |
-
#
|
| 121 |
-
# Note: We hide the system message input because we hardcode it in the function
|
| 122 |
chatbot = gr.ChatInterface(
|
| 123 |
respond,
|
| 124 |
type="messages",
|
| 125 |
additional_inputs=[
|
| 126 |
-
gr.Textbox(value="
|
| 127 |
-
gr.Slider(minimum=1, maximum=512, value=100, step=1, label="Max tokens"
|
| 128 |
-
gr.Slider(minimum=0.1, maximum=2.0, value=0.2, step=0.1, label="Temperature
|
| 129 |
-
gr.Slider(minimum=0.1, maximum=1.0, value=0.
|
| 130 |
-
riddle_select #
|
| 131 |
],
|
| 132 |
)
|
| 133 |
-
|
| 134 |
-
# Sidebar for login
|
| 135 |
-
with gr.Sidebar():
|
| 136 |
-
gr.LoginButton()
|
| 137 |
|
| 138 |
if __name__ == "__main__":
|
| 139 |
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
from llama_cpp import Llama
|
| 3 |
|
| 4 |
+
# --- 1. SETUP & DATEN ---
|
|
|
|
|
|
|
| 5 |
|
| 6 |
+
# Das lokale Modell laden (dein angegebener Pfad)
|
| 7 |
+
llm = Llama.from_pretrained(
|
| 8 |
+
repo_id="simonper/Llama-3.2-1B-bnb-4bit_finetome-100k_gguf_4bit",
|
| 9 |
+
filename="Llama-3.2-1B.Q4_K_M.gguf",
|
| 10 |
+
n_ctx=4096, # Context window
|
| 11 |
+
n_threads=8, # CPU threads (anpassbar)
|
| 12 |
+
n_gpu_layers=0, # 0 für CPU, höher setzen wenn du GPU hast
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
# Die Datenbank der Rätsel (Szenario = Öffentlich, Lösung = Versteckt)
|
| 16 |
RIDDLES = {
|
| 17 |
+
"Der tote Mann": {
|
| 18 |
+
"scenario": "Ein Mann liegt tot auf einer Wiese. Neben ihm liegt ein ungeöffnetes Paket. Es sind keine Fußspuren zu sehen. Wie ist er gestorben?",
|
| 19 |
+
"solution": "Der Mann ist aus einem Flugzeug gesprungen. Das Paket war sein Fallschirm, der sich nicht geöffnet hat."
|
| 20 |
},
|
| 21 |
+
"Der Barman": {
|
| 22 |
+
"scenario": "Ein Mann geht in eine Bar und bestellt ein Glas Wasser. Der Barkeeper zieht eine Waffe und richtet sie auf ihn. Der Mann sagt 'Danke' und geht. Warum?",
|
| 23 |
+
"solution": "Der Mann hatte Schluckauf. Der Schreck durch die Waffe hat ihn geheilt, weshalb er dankbar war."
|
| 24 |
},
|
| 25 |
+
"Die Hütte": {
|
| 26 |
+
"scenario": "Zwei Männer sind in einer Hütte im Wald. Einer ist tot. Die Hütte ist nicht abgebrannt, aber der tote Mann ist verkohlt. Was ist passiert?",
|
| 27 |
+
"solution": "Die Hütte ist die Kabine eines abgestürzten Flugzeugs. Der Mann starb beim Absturzfeuer."
|
| 28 |
},
|
| 29 |
+
"Der Koffer": {
|
| 30 |
+
"scenario": "Eine Frau öffnet ihren Koffer und findet einen toten Mann darin. Sie wird nicht verhaftet und hat keine Angst. Warum?",
|
| 31 |
+
"solution": "Der 'Koffer' ist eigentlich ein Sarg. Die Frau ist auf einer Beerdigung."
|
| 32 |
}
|
| 33 |
}
|
| 34 |
|
| 35 |
+
# --- 2. HELPER FUNCTIONS ---
|
| 36 |
+
|
| 37 |
+
def build_prompt(system_message: str, history: list[dict], user_message: str) -> str:
|
| 38 |
+
"""
|
| 39 |
+
Konstruiert den Prompt für das Llama Modell.
|
| 40 |
+
Da wir Llama-3 nutzen, ist ein Format wichtig, das System-Instruktionen klar trennt.
|
| 41 |
+
"""
|
| 42 |
+
lines = []
|
| 43 |
+
|
| 44 |
+
# System Prompt Injection
|
| 45 |
+
if system_message:
|
| 46 |
+
lines.append(f"System: {system_message}\n")
|
| 47 |
+
|
| 48 |
+
# History aufbauen
|
| 49 |
+
for turn in history:
|
| 50 |
+
role = turn["role"]
|
| 51 |
+
content = turn["content"]
|
| 52 |
+
if role == "user":
|
| 53 |
+
lines.append(f"User: {content}")
|
| 54 |
+
elif role == "assistant":
|
| 55 |
+
lines.append(f"Assistant: {content}")
|
| 56 |
+
|
| 57 |
+
# Aktuelle Nachricht
|
| 58 |
+
lines.append(f"User: {user_message}")
|
| 59 |
+
lines.append("Assistant:")
|
| 60 |
+
|
| 61 |
+
return "\n".join(lines)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
def respond(
|
| 65 |
message,
|
| 66 |
history: list[dict[str, str]],
|
| 67 |
+
system_message_dummy, # Wir ignorieren den Input vom Slider und bauen unseren eigenen
|
| 68 |
max_tokens,
|
| 69 |
temperature,
|
| 70 |
top_p,
|
| 71 |
+
selected_riddle, # Das kommt vom Dropdown
|
|
|
|
| 72 |
):
|
| 73 |
+
# 1. Das aktuelle Rätsel laden
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
current_game = RIDDLES[selected_riddle]
|
| 75 |
|
| 76 |
+
# 2. Den "Game Master" System Prompt bauen (Hier passiert das Context Learning)
|
| 77 |
+
# Wir injizieren die Lösung direkt in den Kontext des Modells
|
| 78 |
game_master_prompt = (
|
| 79 |
+
f"Du bist der Spielleiter eines Laterale-Denk-Rätsels (Black Stories). "
|
| 80 |
+
f"AKTUELLES SZENARIO: '{current_game['scenario']}' "
|
| 81 |
+
f"VERSTECKTE LÖSUNG (Der User kennt diese NICHT!): '{current_game['solution']}' "
|
| 82 |
+
f"\nREGELN FÜR DICH:\n"
|
| 83 |
+
f"1. Analysiere die Frage des Users logisch in Bezug auf die versteckte Lösung.\n"
|
| 84 |
+
f"2. Antworte AUSSCHLIESSLICH mit: 'Ja', 'Nein', oder 'Irrelevant'.\n"
|
| 85 |
+
f"3. Wenn der User die Lösung errät, sage: 'KORREKT! Du hast es gelöst: [Erklärung]'.\n"
|
| 86 |
+
f"4. Gib KEINE Hinweise."
|
|
|
|
|
|
|
| 87 |
)
|
| 88 |
|
| 89 |
+
# 3. Prompt zusammenbauen
|
| 90 |
+
prompt = build_prompt(game_master_prompt, history, message)
|
|
|
|
|
|
|
| 91 |
|
| 92 |
+
# 4. Llama.cpp aufrufen
|
| 93 |
+
output = llm(
|
| 94 |
+
prompt,
|
| 95 |
+
max_tokens=int(max_tokens),
|
| 96 |
+
temperature=float(temperature),
|
| 97 |
+
top_p=float(top_p),
|
| 98 |
+
stop=["User:", "System:"],
|
| 99 |
+
echo=False # Wichtig: Den Prompt nicht wiederholen
|
| 100 |
+
)
|
| 101 |
|
| 102 |
+
reply = output["choices"][0]["text"].strip()
|
| 103 |
+
return reply
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
|
|
|
|
|
|
| 105 |
|
| 106 |
+
# --- 3. UI AUFBAU (GRADIO BLOCKS) ---
|
| 107 |
|
|
|
|
|
|
|
| 108 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 109 |
+
gr.Markdown("# 🕵️ Der Ja/Nein Detektiv (Llama GGUF Edition)")
|
|
|
|
|
|
|
| 110 |
gr.Markdown(
|
| 111 |
+
"**Anleitung:** Ich denke an eine seltsame Situation. "
|
| 112 |
+
"Stelle mir Ja/Nein-Fragen, um herauszufinden, was passiert ist!"
|
| 113 |
)
|
| 114 |
|
| 115 |
+
# Das Dropdown Menü für die Rätsel-Auswahl
|
| 116 |
with gr.Row():
|
|
|
|
| 117 |
riddle_select = gr.Dropdown(
|
| 118 |
choices=list(RIDDLES.keys()),
|
| 119 |
+
value="Der tote Mann",
|
| 120 |
+
label="Wähle einen Fall",
|
| 121 |
interactive=True
|
| 122 |
)
|
| 123 |
|
| 124 |
+
# Textfeld, das das Szenario anzeigt
|
| 125 |
scenario_display = gr.Textbox(
|
| 126 |
+
label="Das Szenario (Dein Hinweis)",
|
| 127 |
+
value=RIDDLES["Der tote Mann"]["scenario"],
|
| 128 |
interactive=False
|
| 129 |
)
|
| 130 |
|
| 131 |
+
# Logik: Wenn Dropdown geändert wird, Textbox aktualisieren
|
| 132 |
def update_scenario(choice):
|
| 133 |
return RIDDLES[choice]["scenario"]
|
| 134 |
|
| 135 |
riddle_select.change(fn=update_scenario, inputs=riddle_select, outputs=scenario_display)
|
| 136 |
|
| 137 |
+
# Chat Interface
|
|
|
|
| 138 |
chatbot = gr.ChatInterface(
|
| 139 |
respond,
|
| 140 |
type="messages",
|
| 141 |
additional_inputs=[
|
| 142 |
+
gr.Textbox(value="", visible=False), # Dummy für System Message (wir nutzen den generierten)
|
| 143 |
+
gr.Slider(minimum=1, maximum=512, value=100, step=1, label="Max new tokens"),
|
| 144 |
+
gr.Slider(minimum=0.1, maximum=2.0, value=0.2, step=0.1, label="Temperature (Niedrig für Logik)"),
|
| 145 |
+
gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p"),
|
| 146 |
+
riddle_select # Das ausgewählte Rätsel wird an die respond-Funktion übergeben
|
| 147 |
],
|
| 148 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
if __name__ == "__main__":
|
| 151 |
demo.launch()
|