File size: 9,387 Bytes
7ecd1b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
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)
    """)