File size: 5,729 Bytes
1322a45
d44df79
1322a45
d44df79
d4704a2
d44df79
 
 
 
 
 
 
 
 
 
d4704a2
d44df79
 
 
d4704a2
d44df79
 
 
d4704a2
d44df79
 
 
d4704a2
d44df79
 
 
d4704a2
 
1322a45
d44df79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1322a45
 
 
d44df79
1322a45
 
 
d44df79
1322a45
d44df79
d4704a2
 
d44df79
 
d4704a2
d44df79
 
 
 
 
 
 
 
d4704a2
1322a45
d44df79
 
1322a45
d44df79
 
 
 
 
 
 
 
 
1322a45
d44df79
 
1322a45
 
d44df79
1322a45
d4704a2
d44df79
d4704a2
d44df79
 
d4704a2
 
d44df79
d4704a2
 
 
d44df79
 
d4704a2
 
 
d44df79
d4704a2
d44df79
 
d4704a2
 
1322a45
d44df79
d4704a2
 
 
 
 
d44df79
d4704a2
 
 
 
d44df79
 
 
 
 
d4704a2
 
1322a45
 
d4704a2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import gradio as gr
from llama_cpp import Llama

# --- 1. SETUP & DATEN ---

# Das lokale Modell laden (dein angegebener Pfad)
llm = Llama.from_pretrained(
    repo_id="simonper/Llama-3.2-1B-bnb-4bit_finetome-100k_gguf_4bit",
    filename="Llama-3.2-1B.Q4_K_M.gguf",
    n_ctx=4096,      # Context window
    n_threads=8,     # CPU threads (anpassbar)
    n_gpu_layers=0,  # 0 für CPU, höher setzen wenn du GPU hast
)

# Die Datenbank der Rätsel (Szenario = Öffentlich, Lösung = Versteckt)
RIDDLES = {
    "Der tote Mann": {
        "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?",
        "solution": "Der Mann ist aus einem Flugzeug gesprungen. Das Paket war sein Fallschirm, der sich nicht geöffnet hat."
    },
    "Der Barman": {
        "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?",
        "solution": "Der Mann hatte Schluckauf. Der Schreck durch die Waffe hat ihn geheilt, weshalb er dankbar war."
    },
    "Die Hütte": {
        "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?",
        "solution": "Die Hütte ist die Kabine eines abgestürzten Flugzeugs. Der Mann starb beim Absturzfeuer."
    },
    "Der Koffer": {
        "scenario": "Eine Frau öffnet ihren Koffer und findet einen toten Mann darin. Sie wird nicht verhaftet und hat keine Angst. Warum?",
        "solution": "Der 'Koffer' ist eigentlich ein Sarg. Die Frau ist auf einer Beerdigung."
    }
}

# --- 2. HELPER FUNCTIONS ---

def build_prompt(system_message: str, history: list[dict], user_message: str) -> str:
    """
    Konstruiert den Prompt für das Llama Modell.
    Da wir Llama-3 nutzen, ist ein Format wichtig, das System-Instruktionen klar trennt.
    """
    lines = []

    # System Prompt Injection
    if system_message:
        lines.append(f"System: {system_message}\n")

    # History aufbauen
    for turn in history:
        role = turn["role"]
        content = turn["content"]
        if role == "user":
            lines.append(f"User: {content}")
        elif role == "assistant":
            lines.append(f"Assistant: {content}")

    # Aktuelle Nachricht
    lines.append(f"User: {user_message}")
    lines.append("Assistant:")

    return "\n".join(lines)


def respond(
    message,
    history: list[dict[str, str]],
    system_message_dummy, # Wir ignorieren den Input vom Slider und bauen unseren eigenen
    max_tokens,
    temperature,
    top_p,
    selected_riddle, # Das kommt vom Dropdown
):
    # 1. Das aktuelle Rätsel laden
    current_game = RIDDLES[selected_riddle]
    
    # 2. Den "Game Master" System Prompt bauen (Hier passiert das Context Learning)
    # Wir injizieren die Lösung direkt in den Kontext des Modells
    game_master_prompt = (
        f"Du bist der Spielleiter eines Laterale-Denk-Rätsels (Black Stories). "
        f"AKTUELLES SZENARIO: '{current_game['scenario']}' "
        f"VERSTECKTE LÖSUNG (Der User kennt diese NICHT!): '{current_game['solution']}' "
        f"\nREGELN FÜR DICH:\n"
        f"1. Analysiere die Frage des Users logisch in Bezug auf die versteckte Lösung.\n"
        f"2. Antworte AUSSCHLIESSLICH mit: 'Ja', 'Nein', oder 'Irrelevant'.\n"
        f"3. Wenn der User die Lösung errät, sage: 'KORREKT! Du hast es gelöst: [Erklärung]'.\n"
        f"4. Gib KEINE Hinweise."
    )

    # 3. Prompt zusammenbauen
    prompt = build_prompt(game_master_prompt, history, message)

    # 4. Llama.cpp aufrufen
    output = llm(
        prompt,
        max_tokens=int(max_tokens),
        temperature=float(temperature),
        top_p=float(top_p),
        stop=["User:", "System:"],
        echo=False # Wichtig: Den Prompt nicht wiederholen
    )

    reply = output["choices"][0]["text"].strip()
    return reply


# --- 3. UI AUFBAU (GRADIO BLOCKS) ---

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 🕵️ Der Ja/Nein Detektiv (Llama GGUF Edition)")
    gr.Markdown(
        "**Anleitung:** Ich denke an eine seltsame Situation. "
        "Stelle mir Ja/Nein-Fragen, um herauszufinden, was passiert ist!"
    )

    # Das Dropdown Menü für die Rätsel-Auswahl
    with gr.Row():
        riddle_select = gr.Dropdown(
            choices=list(RIDDLES.keys()), 
            value="Der tote Mann", 
            label="Wähle einen Fall", 
            interactive=True
        )
    
    # Textfeld, das das Szenario anzeigt
    scenario_display = gr.Textbox(
        label="Das Szenario (Dein Hinweis)", 
        value=RIDDLES["Der tote Mann"]["scenario"],
        interactive=False 
    )

    # Logik: Wenn Dropdown geändert wird, Textbox aktualisieren
    def update_scenario(choice):
        return RIDDLES[choice]["scenario"]
    
    riddle_select.change(fn=update_scenario, inputs=riddle_select, outputs=scenario_display)

    # Chat Interface
    chatbot = gr.ChatInterface(
        respond,
        type="messages",
        additional_inputs=[
            gr.Textbox(value="", visible=False), # Dummy für System Message (wir nutzen den generierten)
            gr.Slider(minimum=1, maximum=512, value=100, step=1, label="Max new tokens"),
            gr.Slider(minimum=0.1, maximum=2.0, value=0.2, step=0.1, label="Temperature (Niedrig für Logik)"),
            gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p"),
            riddle_select # Das ausgewählte Rätsel wird an die respond-Funktion übergeben
        ],
    )

if __name__ == "__main__":
    demo.launch()