Alibrown commited on
Commit
92d55f8
·
verified ·
1 Parent(s): 376f1f9

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -0
app.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import json
4
+ from PIL import Image
5
+ import io
6
+ import base64
7
+ import pandas as pd
8
+ import zipfile
9
+ import PyPDF2
10
+ import os # Wird für die zukünftige Umgebungsvariablen-Nutzung bereitgehalten
11
+
12
+ # --- Konfiguration ---
13
+ st.set_page_config(page_title="OpenRouter Free Interface", layout="wide")
14
+ OPENROUTER_API_BASE = "https://openrouter.ai/api/v1"
15
+
16
+ # --- Page Title ---
17
+ st.title("💸 OpenRouter Free-Tier Interface")
18
+ st.markdown("""
19
+ **Willkommen im All-OpenRouter-Free-Interface Deluxe!**
20
+ Chatte mit **kostenlosen (Free-Tier)** Modellen über die OpenRouter API.
21
+ Alle Modelle unterliegen den OpenRouter-Ratenbegrenzungen.
22
+ """)
23
+
24
+ # --- Session State Management ---
25
+ if "messages" not in st.session_state:
26
+ st.session_state.messages = []
27
+ if "uploaded_content" not in st.session_state:
28
+ st.session_state.uploaded_content = None
29
+
30
+
31
+ # --- Datei-Verarbeitung ---
32
+ def encode_image(image):
33
+ buf = io.BytesIO()
34
+ image.save(buf, format="JPEG")
35
+ return base64.b64encode(buf.getvalue()).decode("utf-8")
36
+
37
+
38
+ def process_file(uploaded_file):
39
+ file_type = uploaded_file.name.split('.')[-1].lower()
40
+ text_exts = ('.txt', '.csv', '.py', '.html', '.js', '.css', '.json', '.xml', '.sql', '.xlsx')
41
+
42
+ if file_type in ["jpg", "jpeg", "png"]:
43
+ return {"type": "image", "content": Image.open(uploaded_file).convert('RGB')}
44
+
45
+ if file_type in ["txt"] + [ext.strip('.') for ext in text_exts if ext not in ('.csv', '.xlsx')]:
46
+ return {"type": "text", "content": uploaded_file.read().decode("utf-8", errors="ignore")}
47
+
48
+ if file_type in ["csv", "xlsx"]:
49
+ try:
50
+ df = pd.read_csv(uploaded_file) if file_type == "csv" else pd.read_excel(uploaded_file)
51
+ return {"type": "text", "content": df.to_string()}
52
+ except Exception as e:
53
+ return {"type": "error", "content": f"Fehler beim Lesen der Tabelle: {e}"}
54
+
55
+ if file_type == "pdf":
56
+ try:
57
+ reader = PyPDF2.PdfReader(uploaded_file)
58
+ return {"type": "text", "content": "".join(page.extract_text() or "" for page in reader.pages)}
59
+ except Exception as e:
60
+ return {"type": "error", "content": f"PDF Fehler: {e}"}
61
+
62
+ if file_type == "zip":
63
+ try:
64
+ with zipfile.ZipFile(uploaded_file) as z:
65
+ content = "ZIP Contents:\n"
66
+ for f in z.infolist():
67
+ if not f.is_dir() and f.filename.lower().endswith(text_exts):
68
+ content += f"\n📄 {f.filename}:\n"
69
+ content += z.read(f.filename).decode("utf-8", errors="ignore")
70
+ return {"type": "text", "content": content or "ZIP enthält keine lesbaren Textdateien."}
71
+ except Exception as e:
72
+ return {"type": "error", "content": f"ZIP Fehler: {e}"}
73
+
74
+ return {"type": "error", "content": "Nicht unterstütztes Dateiformat."}
75
+
76
+
77
+ # --- Context-Length Fetch ---
78
+ def fetch_model_contexts(api_key):
79
+ """Lädt alle Modelle + deren context_length."""
80
+ if not api_key:
81
+ return {}
82
+ headers = {"Authorization": f"Bearer {api_key}"}
83
+ try:
84
+ res = requests.get(f"{OPENROUTER_API_BASE}/models", headers=headers, timeout=10)
85
+ contexts = {}
86
+ if res.status_code == 200:
87
+ for m in res.json().get("data", []):
88
+ mid = m.get("id")
89
+ ctx = m.get("context_length", 4096)
90
+ contexts[mid] = ctx
91
+ return contexts
92
+ except Exception as e:
93
+ st.warning(f"⚠️ Fehler beim Laden der Modellinfos: {e}")
94
+ return {}
95
+
96
+
97
+ # --- Sidebar ---
98
+ with st.sidebar:
99
+ st.header("⚙️ API Settings")
100
+ api_key = st.text_input("OpenRouter API Key", type="password")
101
+
102
+ # Free Modelle (Fallback)
103
+ FREE_MODEL_LIST = [
104
+ "cognitivecomputations/dolphin-mistral-24b-venice-edition:free",
105
+ "deepseek/deepseek-chat-v3",
106
+ "google/gemma-2-9b-it",
107
+ "mistralai/mistral-7b-instruct-v0.2",
108
+ "qwen/qwen2-72b-instruct",
109
+ "nousresearch/nous-hermes-2-mixtral-8x7b-dpo",
110
+ ]
111
+
112
+ model = st.selectbox("Wähle ein Modell", FREE_MODEL_LIST, index=0)
113
+
114
+ # Context automatisch anpassen
115
+ model_contexts = fetch_model_contexts(api_key)
116
+ default_ctx = model_contexts.get(model, 4096)
117
+
118
+ temperature = st.slider("Temperature", 0.0, 1.0, 0.7)
119
+ max_tokens = st.slider(f"Max Tokens (max {default_ctx})", 1, min(default_ctx, 32000), min(512, default_ctx))
120
+ st.caption(f"🔢 Model Context Length: {default_ctx}")
121
+
122
+ if st.button("🔄 Chat Reset"):
123
+ st.session_state.messages = []
124
+ st.session_state.uploaded_content = None
125
+ st.success("Chat-Verlauf und Anhang gelöscht.")
126
+
127
+ st.markdown("""
128
+ ---
129
+ 🧠 **Hinweis:** Diese Modelle sind **kostenlos**, aber ggf. durch Rate-Limits beschränkt.
130
+ Dein API-Key wird nur **lokal** verwendet.
131
+ """)
132
+
133
+
134
+ # --- Datei Upload ---
135
+ uploaded_file = st.file_uploader("Upload File (optional)",
136
+ type=["jpg", "jpeg", "png", "txt", "pdf", "zip", "csv", "xlsx", "html", "css", "js", "py"])
137
+
138
+ if uploaded_file and st.session_state.uploaded_content is None:
139
+ st.session_state.uploaded_content = process_file(uploaded_file)
140
+
141
+ if st.session_state.uploaded_content:
142
+ processed = st.session_state.uploaded_content
143
+ st.subheader("📎 Current Attachment:")
144
+ if processed["type"] == "image":
145
+ st.image(processed["content"], caption="Attached Image", width=300)
146
+ elif processed["type"] == "text":
147
+ st.text_area("File Preview", processed["content"], height=150)
148
+ elif processed["type"] == "error":
149
+ st.error(processed["content"])
150
+ if st.button("❌ Remove Attachment"):
151
+ st.session_state.uploaded_content = None
152
+ st.experimental_rerun()
153
+
154
+
155
+ # --- Chat Verlauf anzeigen ---
156
+ for msg in st.session_state.messages:
157
+ with st.chat_message(msg["role"]):
158
+ st.markdown(msg["content"])
159
+
160
+
161
+ # --- API Call ---
162
+ def call_openrouter(model, messages, temp, max_tok, key):
163
+ headers = {
164
+ "Authorization": f"Bearer {key}",
165
+ "Content-Type": "application/json",
166
+ "Referer": "https://aicodecraft.io",
167
+ "X-Title": "OpenRouter-Free-Interface",
168
+ }
169
+ payload = {
170
+ "model": model,
171
+ "messages": messages,
172
+ "temperature": temp,
173
+ "max_tokens": max_tok,
174
+ }
175
+
176
+ res = requests.post(f"{OPENROUTER_API_BASE}/chat/completions", headers=headers, data=json.dumps(payload))
177
+ if res.status_code == 200:
178
+ try:
179
+ return res.json()["choices"][0]["message"]["content"]
180
+ except (KeyError, IndexError):
181
+ raise Exception("Fehlerhafte API-Antwort: Konnte Antworttext nicht extrahieren.")
182
+ else:
183
+ try:
184
+ err = res.json()
185
+ msg = err.get("error", {}).get("message", res.text)
186
+ except:
187
+ msg = res.text
188
+ raise Exception(f"API Error {res.status_code}: {msg}")
189
+
190
+
191
+ # --- Chat Input ---
192
+ if prompt := st.chat_input("Deine Nachricht..."):
193
+ if not api_key:
194
+ st.warning("Bitte trage deinen OpenRouter API Key in der Sidebar ein.")
195
+ st.stop()
196
+
197
+ st.session_state.messages.append({"role": "user", "content": prompt})
198
+ with st.chat_message("user"):
199
+ st.markdown(prompt)
200
+
201
+ messages = [{"role": m["role"], "content": m["content"]} for m in st.session_state.messages]
202
+
203
+ if st.session_state.uploaded_content:
204
+ content = st.session_state.uploaded_content
205
+ if content["type"] == "image":
206
+ base64_img = encode_image(content["content"])
207
+ messages[-1]["content"] = [
208
+ {"type": "text", "text": prompt},
209
+ {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_img}"}}
210
+ ]
211
+ elif content["type"] == "text":
212
+ messages[-1]["content"] += f"\n\n[Attached File Content]\n{content['content']}"
213
+
214
+ with st.chat_message("assistant"):
215
+ with st.spinner(f"Fragend {model}..."):
216
+ try:
217
+ reply = call_openrouter(model, messages, temperature, max_tokens, api_key)
218
+ st.markdown(reply)
219
+ st.session_state.messages.append({"role": "assistant", "content": reply})
220
+ except Exception as e:
221
+ st.error(str(e))
222
+ st.session_state.messages.append({"role": "assistant", "content": f"❌ {str(e)}"})