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)