OpenRouter-UI / backup_de_app.py
Alibrown's picture
Rename app.py to backup_de_app.py
6323cc7 verified
import streamlit as st
import requests
import json
from PIL import Image
import io
import base64
import pandas as pd
import zipfile
import PyPDF2
import os # Wird für die zukünftige Umgebungsvariablen-Nutzung bereitgehalten
# --- Konfiguration ---
st.set_page_config(page_title="OpenRouter Free Interface", layout="wide")
OPENROUTER_API_BASE = "https://openrouter.ai/api/v1"
# --- Page Title ---
st.title("💸 OpenRouter Free-Tier Interface")
st.markdown("""
**Willkommen im All-OpenRouter-Free-Interface Deluxe!**
Chatte mit **kostenlosen (Free-Tier)** Modellen über die OpenRouter API.
Alle Modelle unterliegen den OpenRouter-Ratenbegrenzungen.
""")
# --- Session State Management ---
# Verwenden des einheitlichen Schlüssels 'messages'
if "messages" not in st.session_state:
st.session_state.messages = []
if "uploaded_content" not in st.session_state:
st.session_state.uploaded_content = None
# --- Datei-Verarbeitung (Multimodal, basierend auf Gemini-UI) ---
def encode_image(image):
"""Encodiert ein PIL-Image-Objekt in einen Base64-String."""
buf = io.BytesIO()
image.save(buf, format="JPEG")
return base64.b64encode(buf.getvalue()).decode("utf-8")
def process_file(uploaded_file):
"""Verarbeitet die hochgeladene Datei (Text, Bild, PDF, ZIP) und extrahiert den Inhalt."""
file_type = uploaded_file.name.split('.')[-1].lower()
text_exts = ('.txt', '.csv', '.py', '.html', '.js', '.css', '.json', '.xml', '.sql', '.xlsx')
if file_type in ["jpg", "jpeg", "png"]:
return {"type": "image", "content": Image.open(uploaded_file).convert('RGB')}
if file_type in ["txt"] + [ext.strip('.') for ext in text_exts 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:
if file_type == "csv":
df = pd.read_csv(uploaded_file)
else: # xlsx
df = pd.read_excel(uploaded_file)
return {"type": "text", "content": df.to_string()}
except Exception as e:
return {"type": "error", "content": f"Fehler beim Lesen der Tabelle: {e}"}
if file_type == "pdf":
try:
reader = PyPDF2.PdfReader(uploaded_file)
# Extrahieren des Textes und Zusammenführen aller Seiten
return {"type": "text", "content": "".join(page.extract_text() or "" for page in reader.pages)}
except Exception as e:
return {"type": "error", "content": f"PDF Fehler: {e}"}
if file_type == "zip":
try:
with zipfile.ZipFile(uploaded_file) as z:
content = "ZIP Contents:\n"
for f in z.infolist():
if not f.is_dir() and f.filename.lower().endswith(text_exts):
content += f"\n📄 {f.filename}:\n"
# Sicherstellen, dass das Lesen und Dekodieren robust ist
content += z.read(f.filename).decode("utf-8", errors="ignore")
return {"type": "text", "content": content or "ZIP enthält keine lesbaren Textdateien."}
except Exception as e:
return {"type": "error", "content": f"ZIP Fehler: {e}"}
return {"type": "error", "content": "Nicht unterstütztes Dateiformat."}
# --- Sidebar ---
with st.sidebar:
st.header("⚙️ API Settings")
api_key = st.text_input("OpenRouter API Key", type="password")
# --- Manuelle Modelle (Austauschbar mit Auto-Discovery) ---
FREE_MODEL_LIST = [
"cognitivecomputations/dolphin-mistral-24b-venice-edition:free",
"deepseek/deepseek-chat-v3",
"google/gemma-2-9b-it",
"mistralai/mistral-7b-instruct-v0.2",
"qwen/qwen2-72b-instruct",
"nousresearch/nous-hermes-2-mixtral-8x7b-dpo", # Ein Beispiel für ein großes Free-Modell
]
model = st.selectbox("Wähle ein Modell", FREE_MODEL_LIST, index=0)
temperature = st.slider("Temperature", 0.0, 1.0, 0.7)
max_tokens = st.slider("Max Tokens", 1, 4096, 512)
if st.button("🔄 Chat Reset"):
st.session_state.messages = []
st.session_state.uploaded_content = None # Auch den Anhang löschen
st.success("Chat-Verlauf und Anhang gelöscht.")
st.markdown("""
---
🧠 **Hinweis:** Diese Modelle sind **kostenlos**, aber ggf. durch Rate-Limits beschränkt.
Dein API-Key wird nur **lokal** verwendet.
""")
# --- Datei Upload ---
uploaded_file = st.file_uploader("Upload File (optional)",
type=["jpg", "jpeg", "png", "txt", "pdf", "zip", "csv", "xlsx", "html", "css", "js", "py"])
# Logik zur Dateiverarbeitung und Vorschau
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 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(processed["content"])
if st.button("❌ Remove Attachment"):
st.session_state.uploaded_content = None
st.experimental_rerun() # Nötig, um den file_uploader optisch zu resetten
# --- Chat Verlauf anzeigen ---
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
# --- API Request Funktion ---
def call_openrouter(model, messages, temp, max_tok, key):
"""Führt den API-Aufruf an OpenRouter (OpenAI-Schema) durch."""
headers = {
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
# KORRIGIERT: "Referer" statt "HTTP-Referer"
"Referer": "https://aicodecraft.io",
"X-Title": "OpenRouter-Free-Interface",
}
payload = {
"model": model,
"messages": messages,
"temperature": temp,
"max_tokens": max_tok,
}
res = requests.post(f"{OPENROUTER_API_BASE}/chat/completions", headers=headers, data=json.dumps(payload))
if res.status_code == 200:
try:
# KORRIGIERT: Stabile Extraktion der Antwort
return res.json()["choices"][0]["message"]["content"]
except (KeyError, IndexError):
raise Exception("Fehlerhafte API-Antwort: Konnte Antworttext nicht extrahieren.")
else:
try:
err = res.json()
# Versuch, die spezifische Fehlermeldung zu finden
msg = err.get("error", {}).get("message", res.text)
except:
msg = res.text
raise Exception(f"API Error {res.status_code}: {msg}")
# --- Chat Input ---
if prompt := st.chat_input("Deine Nachricht..."):
if not api_key:
st.warning("Bitte trage deinen OpenRouter API Key in der Sidebar ein.")
st.stop()
# Nachricht hinzufügen und anzeigen
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# API Nachrichten vorbereiten (für Chatverlauf)
messages = [{"role": m["role"], "content": m["content"]} for m in st.session_state.messages]
# Datei anhängen, falls vorhanden (Multimodalitäts-Handling)
if st.session_state.uploaded_content:
content = st.session_state.uploaded_content
# OpenRouter/OpenAI Multimodalität: Bilder als 'url' mit Base64
if content["type"] == "image":
base64_img = encode_image(content["content"])
# Aufbau des Multimodal-Contents für die API
messages[-1]["content"] = [
{"type": "text", "text": prompt},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_img}"}}
]
# Text-Dateien einfach dem letzten Prompt anhängen
elif content["type"] == "text":
messages[-1]["content"] += f"\n\n[Attached File Content]\n{content['content']}"
# Antwort generieren
with st.chat_message("assistant"):
with st.spinner(f"Fragend {model}..."):
try:
reply = call_openrouter(model, messages, temperature, max_tokens, api_key)
st.markdown(reply)
st.session_state.messages.append({"role": "assistant", "content": reply})
except Exception as e:
st.error(str(e))
st.session_state.messages.append({"role": "assistant", "content": f"❌ {str(e)}"})