Spaces:
Sleeping
Sleeping
| import os | |
| import streamlit as st | |
| import tempfile | |
| import io | |
| import pandas as pd | |
| import zipfile | |
| import PyPDF2 | |
| # Importe für das Gemini SDK | |
| import google.generativeai as genai | |
| from google.generativeai.errors import APIError | |
| from PIL import Image # Bleibt, um PIL-Objekte zu behandeln | |
| # ---------------------------------------------------- | |
| # 🚨 BEHOBENE KRITISCHE FIXES (Du hast diese bereits!) | |
| # Wird beibehalten, um die Stabilität in restriktiven Umgebungen zu gewährleisten. | |
| # ---------------------------------------------------- | |
| TEMP_STREAMLIT_HOME = os.path.join(tempfile.gettempdir(), "st_config_workaround") | |
| os.makedirs(TEMP_STREAMLIT_HOME, exist_ok=True) | |
| os.environ["STREAMLIT_HOME"] = TEMP_STREAMLIT_HOME | |
| os.environ["STREAMLIT_GATHER_USAGE_STATS"] = "false" | |
| CONFIG_PATH = os.path.join(TEMP_STREAMLIT_HOME, "config.toml") | |
| CONFIG_CONTENT = """ | |
| [browser] | |
| gatherUsageStats = false | |
| """ | |
| if not os.path.exists(CONFIG_PATH): | |
| try: | |
| with open(CONFIG_PATH, "w") as f: | |
| f.write(CONFIG_CONTENT) | |
| except: | |
| pass # Ignoriere, wenn das Schreiben in /tmp fehlschlägt | |
| # ---------------------------------------------------- | |
| # ENDE DER WORKAROUNDS | |
| # ---------------------------------------------------- | |
| # --- Konfiguration der Seite --- | |
| st.set_page_config(page_title="Gemini AI Chat", layout="wide", initial_sidebar_state="expanded") | |
| st.title("🤖 Gemini AI Chat Interface") | |
| st.markdown(""" | |
| **Welcome to the Gemini AI Chat Interface!** | |
| Chat seamlessly with Google's advanced Gemini AI models, supporting multiple input types. | |
| """) | |
| # Session State Management | |
| if "messages" not in st.session_state: | |
| st.session_session.messages = [] | |
| if "uploaded_content" not in st.session_state: | |
| st.session_state.uploaded_content = None | |
| # --- Funktionen zur Dateiverarbeitung --- | |
| # 🛑 encode_image wird entfernt, da das SDK PIL-Objekte direkt verarbeitet. | |
| def process_file(uploaded_file): | |
| """Verarbeitet die hochgeladene Datei und extrahiert den Inhalt.""" | |
| file_type = uploaded_file.name.split('.')[-1].lower() | |
| text_extensions = ('.txt', '.csv', '.py', '.html', '.js', '.css', '.json', '.xml', '.sql', '.xlsx') | |
| if file_type in ["jpg", "jpeg", "png"]: | |
| # WICHTIG: Das PIL-Image-Objekt direkt speichern | |
| return {"type": "image", "content": Image.open(uploaded_file).convert('RGB')} | |
| if file_type in ["txt"] + [ext.strip('.') for ext in text_extensions if ext not in ('.csv', '.xlsx')]: | |
| return {"type": "text", "content": uploaded_file.read().decode("utf-8", errors='ignore')} | |
| if file_type in ["csv", "xlsx"]: | |
| try: | |
| df = pd.read_csv(uploaded_file) if file_type == "csv" else pd.read_excel(uploaded_file) | |
| return {"type": "text", "content": df.to_string()} | |
| except Exception as e: | |
| return {"type": "error", "content": f"Failed to read tabular data: {e}"} | |
| if file_type == "pdf": | |
| try: | |
| reader = PyPDF2.PdfReader(uploaded_file) | |
| return {"type": "text", "content": "".join(page.extract_text() for page in reader.pages if page.extract_text())} | |
| except Exception as e: | |
| return {"type": "error", "content": f"Failed to read PDF: {e}"} | |
| if file_type == "zip": | |
| try: | |
| with zipfile.ZipFile(uploaded_file) as z: | |
| newline = "\n" | |
| content = f"ZIP Contents (Processing text files only):{newline}" | |
| for file_info in z.infolist(): | |
| if not file_info.is_dir() and file_info.filename.lower().endswith(text_extensions): | |
| with z.open(file_info.filename) as file: | |
| file_content = file.read().decode('utf-8', errors='ignore') | |
| content += f"{newline}📄 {file_info.filename}:{newline}{file_content}{newline}" | |
| elif not file_info.is_dir(): | |
| content += f"{newline}⚠️ Binärdatei/Unbekannte Datei ignoriert: {file_info.filename}{newline}" | |
| return {"type": "text", "content": content} | |
| except Exception as e: | |
| return {"type": "error", "content": f"Failed to process ZIP: {e}"} | |
| return {"type": "error", "content": "Unsupported file format"} | |
| # --- Sidebar für Einstellungen --- | |
| with st.sidebar: | |
| st.header("⚙️ API Settings") | |
| # API Key Management | |
| api_key = st.text_input("Google AI API Key", type="password") | |
| # Optimierte Modell-Liste | |
| model_list = [ | |
| "gemini-2.5-flash", | |
| "gemini-2.5-pro", | |
| "gemini-1.5-flash", | |
| "gemini-1.5-pro", | |
| ] | |
| model = st.selectbox("Model", model_list) | |
| st.caption("❗ Alle **2.5er** und **1.5er** Modelle sind **Vision-fähig** (Bilder, Dateien).") | |
| temperature = st.slider("Temperature", 0.0, 1.0, 0.7) | |
| max_tokens = st.slider("Max Tokens", 1, 100000, 1000) | |
| if st.button("🔄 Chat Reset (Full)"): | |
| st.session_state.messages = [] | |
| st.session_state.uploaded_content = None | |
| st.experimental_rerun() | |
| # --- Datei Upload & Vorschau --- | |
| uploaded_file = st.file_uploader("Upload File (Image/Text/PDF/ZIP)", | |
| type=["jpg", "jpeg", "png", "txt", "pdf", "zip", "csv", "xlsx", "html", "css", "js", "py"]) | |
| if uploaded_file and st.session_state.uploaded_content is None: | |
| st.session_state.uploaded_content = process_file(uploaded_file) | |
| if st.session_state.uploaded_content: | |
| processed = st.session_state.uploaded_content | |
| st.subheader("Current File Attachment:") | |
| if processed["type"] == "image": | |
| st.image(processed["content"], caption="Attached Image", width=300) | |
| elif processed["type"] == "text": | |
| st.text_area("File Preview", processed["content"], height=150) | |
| elif processed["type"] == "error": | |
| st.error(f"Error processing file: {processed['content']}") | |
| if st.button("❌ Clear Uploaded File Attachment"): | |
| st.session_state.uploaded_content = None | |
| st.experimental_rerun() | |
| # --- Chat Verlauf anzeigen --- | |
| for message in st.session_state.messages: | |
| # Anzeigen des reinen Textinhalts | |
| with st.chat_message(message["role"]): | |
| st.markdown(message["content"]) | |
| # --- Chat-Eingabe verarbeiten --- | |
| if prompt := st.chat_input("Your message..."): | |
| if not api_key: | |
| st.warning("API Key benötigt!") | |
| st.stop() | |
| # 1. API konfigurieren | |
| genai.configure(api_key=api_key) | |
| model_instance = genai.GenerativeModel(model) | |
| # 2. History und neuen Content für den API-Call vorbereiten | |
| # Konvertiere die Streamlit-History in das Gemini-Format (role: user/model, parts: [{text: ...}, {image: ...}]) | |
| contents = [] | |
| for msg in st.session_state.messages: | |
| role_map = {"user": "user", "assistant": "model"} | |
| contents.append({"role": role_map.get(msg["role"]), "parts": [{"text": msg["content"]}]}) | |
| # 3. Den neuen User-Prompt hinzufügen | |
| current_parts = [{"text": prompt}] | |
| # 4. Dateiinhalt hinzufügen (falls vorhanden) | |
| if st.session_state.uploaded_content: | |
| content_data = st.session_state.uploaded_content | |
| if content_data["type"] == "image": | |
| # Füge das PIL-Objekt direkt als Teil hinzu | |
| current_parts.append(content_data["content"]) | |
| elif content_data["type"] == "text": | |
| # Füge den Text-Inhalt zum Prompt-Text hinzu | |
| current_parts[0]["text"] += f"\n\n[Attached File Content]\n{content_data['content']}" | |
| # Hinzufügen des vollständigen letzten User-Eintrags zum History-Array | |
| contents.append({"role": "user", "parts": current_parts}) | |
| # 5. Nachricht zur Streamlit-Historie hinzufügen und anzeigen | |
| # Wir fügen den reinen Text-Prompt zur Streamlit-History hinzu, um die Darstellung einfach zu halten | |
| st.session_state.messages.append({"role": "user", "content": prompt}) | |
| with st.chat_message("user"): | |
| st.markdown(prompt) | |
| # 6. Antwort generieren | |
| with st.spinner("Gemini is thinking..."): | |
| try: | |
| response = model_instance.generate_content( | |
| contents, # Das vollständige History-Array übergeben | |
| generation_config=genai.types.GenerateContentConfig( | |
| temperature=temperature, | |
| max_output_tokens=max_tokens | |
| ) | |
| ) | |
| response_text = response.text | |
| with st.chat_message("assistant"): | |
| st.markdown(response_text) | |
| st.session_state.messages.append({"role": "assistant", "content": response_text}) | |
| except APIError as e: | |
| st.error(f"Gemini API Error: {str(e)}. Bitte prüfen Sie den API Key und die Modell-Wahl.") | |
| except Exception as e: | |
| st.error(f"General Error: {str(e)}") | |
| # Instructions in the sidebar | |
| with st.sidebar: | |
| st.markdown(""" | |
| --- | |
| ## 📝 Instructions: | |
| 1. Enter your **Google AI API Key** | |
| 2. Select a **Gemini 2.5/1.5** model (all are multimodal) | |
| 3. Adjust parameters (Temperature/Tokens) | |
| 4. Upload a file (optional: **Image, Text, PDF, ZIP, CSV/XLSX**) | |
| 5. Type your message and press Enter | |
| ### About | |
| 🔗 [GitHub Profile](https://github.com/volkansah) | 📂 [Project Repository](https://github.com/volkansah/gemini-ai-chat) | |
| """) |