File size: 6,420 Bytes
bd272a0
69b2000
 
bd272a0
9a480ad
1b70e58
 
 
bd272a0
b2c60de
 
 
38c4cab
b9c3558
b2c60de
1b70e58
139b7ee
56f5718
 
 
 
 
139b7ee
4474ae5
 
 
 
 
 
 
 
 
139b7ee
 
4474ae5
 
139b7ee
 
 
 
 
 
 
 
91b1942
b2c60de
 
 
139b7ee
 
 
 
b2c60de
 
 
 
 
1b70e58
b2c60de
 
 
bd272a0
43e9969
bd272a0
 
1b70e58
 
2d4e194
9a480ad
69b2000
 
 
00161b7
69b2000
 
 
 
 
 
2d4e194
69b2000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2d4e194
00161b7
bd272a0
 
 
 
1b70e58
 
9a480ad
1b70e58
 
bd272a0
43e9969
 
9a480ad
bd272a0
1b70e58
 
 
9a480ad
1b70e58
43e9969
9a480ad
 
7ad4232
 
 
 
 
 
 
 
 
 
 
1d1ca16
 
 
b2c60de
 
 
 
 
9a480ad
dcc59b1
b2c60de
 
 
 
 
 
 
bd272a0
 
91b1942
bd272a0
1d1ca16
 
 
 
9a480ad
bd272a0
b2c60de
1d1ca16
7ad4232
b2c60de
 
 
 
 
 
7ad4232
 
 
 
 
 
 
 
 
 
1d1ca16
bd272a0
c38f251
a2f5736
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import os
import shutil
import tempfile
import threading
import gradio as gr
from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))
DEFAULT_MODEL = "gemini-3.1-flash-lite-preview"
MODELS = [
    "gemini-3.1-flash-lite-preview",
    "gemini-3.1-pro-preview",
    "gemini-3.1-flash-preview",
]

# Tools (einmalig)
tools = [
        types.Tool(url_context=types.UrlContext()),
        types.Tool(googleSearch=types.GoogleSearch(
        )),
    ]

SYSTEM_PROMPT = """ Du bist ein intelligenter, effizienter und freundlicher persönlicher Assistent. 
Dein Ziel ist es, meine Produktivität zu maximieren und mir bei meinen täglichen Aufgaben zu helfen. 
Halte dich an diese Richtlinien: Präzision vor 
Füllwörtern: Komm direkt auf den Punkt. Vermeide unnötige Einleitungs- oder Abschlussfloskeln (wie 'Ich hoffe, das hilft Ihnen'). 
Struktur: Verwende für komplexe Antworten Bullet Points, nummerierte Listen oder Fettdruck, um die Lesbarkeit zu erhöhen. 
Rückfragen: Wenn meine Anfrage unklar ist oder wichtige Informationen fehlen, frage mich kurz und gezielt nach dem Kontext, bevor du eine Annahme triffst. 
Objektivität: Wenn ich um eine Meinung bitte, präsentiere mir verschiedene Perspektiven oder Vor- und Nachteile, anstatt dich einseitig festzulegen. 
Anpassungsfähigkeit: Antworte kurz bei kurzen Fragen und ausführlich bei komplexen Themen. Wenn ich um eine Zusammenfassung bitte, halte sie kompakt. Sprache: Antworte immer in der Sprache, in der ich die Frage stelle (standardmäßig Deutsch, sofern nicht anders gewünscht).
"""

def build_generate_content_config(model_name):
    config_kwargs = {"tools": tools,
                    "system_instruction": SYSTEM_PROMPT}

    # Pro-Modelle akzeptieren MINIMAL derzeit nicht.
    if "pro" not in model_name:
        config_kwargs["thinking_config"] = types.ThinkingConfig(
            thinking_level="MINIMAL"
        )

    return types.GenerateContentConfig(**config_kwargs)

def create_chat_state(model_name):
    return {
        "model": model_name,
        "chat": client.chats.create(
            model=model_name,
            config=build_generate_content_config(model_name),
        ),
        "lock": threading.Lock(),
    }


def respond(message, history, state, model_name):
    if state is None:
        state = create_chat_state(model_name)
    elif state.get("model") != model_name:
        state = create_chat_state(model_name)
    
    history = history or []
    text = (message or {}).get("text", "")
    files = (message or {}).get("files", [])
    parts = []

   # 1. Dateien verarbeiten (Upload via API & UI-Vorbereitung)
    for file_path in files:
        # Reinen ASCII-Namen generieren (z.B. 'Lsungen-MP-4 1.pdf')
        base_name = os.path.basename(file_path)
        safe_name = base_name.encode('ascii', 'ignore').decode('ascii')
        
        # Fallback, falls der Name nach der Filterung komplett leer sein sollte
        if not safe_name.strip(". "): 
            safe_name = "upload_file.pdf"
            
        # Temporären, komplett sicheren Pfad generieren
        safe_temp_path = os.path.join(tempfile.gettempdir(), f"safe_{safe_name}")
        
        try:
            # Datei an den sicheren Ort kopieren
            shutil.copy2(file_path, safe_temp_path)
            
            # Upload mit dem sicheren Pfad durchführen
            uploaded_file = client.files.upload(
                file=safe_temp_path, 
                config=types.UploadFileConfig(display_name=safe_name)
            )
            parts.append(uploaded_file)
        finally:
            # Temporäre Kopie aufräumen, um Speicherplatz zu sparen
            if os.path.exists(safe_temp_path):
                os.remove(safe_temp_path)
        
        # Datei in der Gradio UI anzeigen (hier nutzen wir den Originalpfad für die korrekte Anzeige)
        history.append({"role": "user", "content": gr.FileData(path=file_path)})
        
    # 2. Text verarbeiten
    if text:
        parts.append(text)
        history.append({"role": "user", "content": text})

    if not parts:
        yield history, state
        return

    # 3. UI: Assistant Placeholder hinzufügen
    history.append({"role": "assistant", "content": ""})
    yield history, state

    # 4. Gemini: Senden + Streamen
    with state["lock"]:
        out = ""
        for chunk in state["chat"].send_message_stream(parts):
            if getattr(chunk, "text", None):
                out += chunk.text
                history[-1]["content"] = out
                yield history, state

def cleanup_gemini_files():
    deleted_count = 0
    # Holt alle Dateien, die mit deinem API-Key hochgeladen wurden
    for f in client.files.list():
        # Löscht die Datei über ihren eindeutigen Google-Ressourcennamen
        client.files.delete(name=f.name)
        deleted_count += 1
    
    # Zeigt ein kleines Popup in der Gradio UI an
    gr.Info(f"{deleted_count} Datei(en) erfolgreich gelöscht.")

def clear_msg():
    return {"text": "", "files": []}


def reset_chat_for_model_change(_model_name):
    return [], None, {"text": "", "files": []}


with gr.Blocks() as demo:
    gr.Markdown("# Gemini AI Chat by Damian")

    model_selector = gr.Dropdown(
        choices=MODELS,
        value=DEFAULT_MODEL,
        label="Gemini-Modell",
        info="Ein Modellwechsel startet einen neuen Chat.",
    )
    
    # Standard-Initialisierung für Gradio >= 6.0
    chatbot = gr.Chatbot(height=600)
    
    msg = gr.MultimodalTextbox(
        placeholder="Schreib was oder lade Dateien hoch…",
        file_count="multiple",
    )
    state = gr.State(None)
    
    msg.submit(respond, inputs=[msg, chatbot, state, model_selector], outputs=[chatbot, state]) \
       .then(clear_msg, outputs=msg)

    model_selector.change(
        fn=reset_chat_for_model_change,
        inputs=model_selector,
        outputs=[chatbot, state, msg],
    )

    with gr.Row():
        clear_btn = gr.Button("Clear Chat")
        cleanup_btn = gr.Button("🗑️ Server-Dateien bereinigen")
        
    clear_btn.click(
        lambda: ([], None, {"text": "", "files": []}),
        outputs=[chatbot, state, msg]
    )
    
    cleanup_btn.click(fn=cleanup_gemini_files)

if __name__ == "__main__":
    print("Das Programm startet jetzt.")
    demo.launch(server_name="0.0.0.0", server_port=7860)