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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -80
app.py CHANGED
@@ -7,35 +7,33 @@ 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
 
@@ -50,14 +48,14 @@ def process_file(uploaded_file):
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:
@@ -67,16 +65,14 @@ def process_file(uploaded_file):
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}"}
@@ -85,21 +81,45 @@ def fetch_model_contexts(api_key):
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",
@@ -109,32 +129,23 @@ with st.sidebar:
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
 
@@ -142,7 +153,7 @@ 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":
@@ -151,47 +162,15 @@ if st.session_state.uploaded_content:
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})
@@ -200,6 +179,7 @@ if prompt := st.chat_input("Deine Nachricht..."):
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":
@@ -211,11 +191,21 @@ if prompt := st.chat_input("Deine Nachricht..."):
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))
 
7
  import pandas as pd
8
  import zipfile
9
  import PyPDF2
 
10
 
11
+ # --- Page Config ---
12
+ st.set_page_config(page_title="OpenRouter + Gemini AI Chat", layout="wide", initial_sidebar_state="expanded")
13
+
14
  OPENROUTER_API_BASE = "https://openrouter.ai/api/v1"
15
 
16
+ # --- Title ---
17
+ st.title("🤖 OpenRouter + Gemini AI Chat Interface")
18
  st.markdown("""
19
+ **Chat with Free-Tier OpenRouter models and optionally upload files (Text, PDF, ZIP, Images) for context.**
20
+ 💡 Responses are copyable with a single click.
 
21
  """)
22
 
23
+ # --- Session State ---
24
  if "messages" not in st.session_state:
25
  st.session_state.messages = []
26
  if "uploaded_content" not in st.session_state:
27
  st.session_state.uploaded_content = None
28
 
29
+ # --- Utilities ---
 
30
  def encode_image(image):
31
  buf = io.BytesIO()
32
  image.save(buf, format="JPEG")
33
  return base64.b64encode(buf.getvalue()).decode("utf-8")
34
 
 
35
  def process_file(uploaded_file):
36
+ """Process uploaded file (text, image, PDF, ZIP)"""
37
  file_type = uploaded_file.name.split('.')[-1].lower()
38
  text_exts = ('.txt', '.csv', '.py', '.html', '.js', '.css', '.json', '.xml', '.sql', '.xlsx')
39
 
 
48
  df = pd.read_csv(uploaded_file) if file_type == "csv" else pd.read_excel(uploaded_file)
49
  return {"type": "text", "content": df.to_string()}
50
  except Exception as e:
51
+ return {"type": "error", "content": f"Failed reading table: {e}"}
52
 
53
  if file_type == "pdf":
54
  try:
55
  reader = PyPDF2.PdfReader(uploaded_file)
56
  return {"type": "text", "content": "".join(page.extract_text() or "" for page in reader.pages)}
57
  except Exception as e:
58
+ return {"type": "error", "content": f"PDF Error: {e}"}
59
 
60
  if file_type == "zip":
61
  try:
 
65
  if not f.is_dir() and f.filename.lower().endswith(text_exts):
66
  content += f"\n📄 {f.filename}:\n"
67
  content += z.read(f.filename).decode("utf-8", errors="ignore")
68
+ return {"type": "text", "content": content or "ZIP contains no readable text files."}
69
  except Exception as e:
70
+ return {"type": "error", "content": f"ZIP Error: {e}"}
71
 
72
+ return {"type": "error", "content": "Unsupported file type."}
73
 
 
 
74
  def fetch_model_contexts(api_key):
75
+ """Fetch context lengths for models"""
76
  if not api_key:
77
  return {}
78
  headers = {"Authorization": f"Bearer {api_key}"}
 
81
  contexts = {}
82
  if res.status_code == 200:
83
  for m in res.json().get("data", []):
84
+ contexts[m.get("id")] = m.get("context_length", 4096)
 
 
85
  return contexts
86
  except Exception as e:
87
+ st.warning(f"⚠️ Failed to fetch model info: {e}")
88
  return {}
89
 
90
+ def call_openrouter(model, messages, temp, max_tok, key):
91
+ headers = {
92
+ "Authorization": f"Bearer {key}",
93
+ "Content-Type": "application/json",
94
+ "Referer": "https://aicodecraft.io",
95
+ "X-Title": "OpenRouter-Free-Interface",
96
+ }
97
+ payload = {
98
+ "model": model,
99
+ "messages": messages,
100
+ "temperature": temp,
101
+ "max_tokens": max_tok,
102
+ }
103
+
104
+ res = requests.post(f"{OPENROUTER_API_BASE}/chat/completions", headers=headers, data=json.dumps(payload))
105
+ if res.status_code == 200:
106
+ try:
107
+ return res.json()["choices"][0]["message"]["content"]
108
+ except (KeyError, IndexError):
109
+ raise Exception("Invalid API response")
110
+ else:
111
+ try:
112
+ err = res.json()
113
+ msg = err.get("error", {}).get("message", res.text)
114
+ except:
115
+ msg = res.text
116
+ raise Exception(f"API Error {res.status_code}: {msg}")
117
 
118
  # --- Sidebar ---
119
  with st.sidebar:
120
  st.header("⚙️ API Settings")
121
  api_key = st.text_input("OpenRouter API Key", type="password")
122
 
 
123
  FREE_MODEL_LIST = [
124
  "cognitivecomputations/dolphin-mistral-24b-venice-edition:free",
125
  "deepseek/deepseek-chat-v3",
 
129
  "nousresearch/nous-hermes-2-mixtral-8x7b-dpo",
130
  ]
131
 
132
+ model = st.selectbox("Select a model", FREE_MODEL_LIST, index=0)
133
 
134
+ # Context-length slider
135
  model_contexts = fetch_model_contexts(api_key)
136
  default_ctx = model_contexts.get(model, 4096)
137
+ max_tokens = st.slider(f"Max Tokens (max {default_ctx})", 1, min(default_ctx, 32000), min(512, default_ctx))
138
 
139
  temperature = st.slider("Temperature", 0.0, 1.0, 0.7)
 
 
140
 
141
+ if st.button("🔄 Reset Chat"):
142
  st.session_state.messages = []
143
  st.session_state.uploaded_content = None
144
+ st.success("Chat and attachment cleared!")
 
 
 
 
 
 
 
 
 
 
 
145
 
146
+ # --- File Upload ---
147
+ uploaded_file = st.file_uploader("Upload File (optional)",
148
+ type=["jpg","jpeg","png","txt","pdf","zip","csv","xlsx","html","css","js","py"])
149
  if uploaded_file and st.session_state.uploaded_content is None:
150
  st.session_state.uploaded_content = process_file(uploaded_file)
151
 
 
153
  processed = st.session_state.uploaded_content
154
  st.subheader("📎 Current Attachment:")
155
  if processed["type"] == "image":
156
+ st.image(processed["content"], width=300)
157
  elif processed["type"] == "text":
158
  st.text_area("File Preview", processed["content"], height=150)
159
  elif processed["type"] == "error":
 
162
  st.session_state.uploaded_content = None
163
  st.experimental_rerun()
164
 
165
+ # --- Chat History ---
 
166
  for msg in st.session_state.messages:
167
  with st.chat_message(msg["role"]):
168
  st.markdown(msg["content"])
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  # --- Chat Input ---
171
+ if prompt := st.chat_input("Your message..."):
172
  if not api_key:
173
+ st.warning("Please enter your OpenRouter API Key in the sidebar.")
174
  st.stop()
175
 
176
  st.session_state.messages.append({"role": "user", "content": prompt})
 
179
 
180
  messages = [{"role": m["role"], "content": m["content"]} for m in st.session_state.messages]
181
 
182
+ # Attach file if exists
183
  if st.session_state.uploaded_content:
184
  content = st.session_state.uploaded_content
185
  if content["type"] == "image":
 
191
  elif content["type"] == "text":
192
  messages[-1]["content"] += f"\n\n[Attached File Content]\n{content['content']}"
193
 
194
+ # Generate response
195
  with st.chat_message("assistant"):
196
+ with st.spinner(f"Asking {model}..."):
197
  try:
198
  reply = call_openrouter(model, messages, temperature, max_tokens, api_key)
199
+
200
+ # Clipboard-ready response with JS
201
+ st.markdown(f"""
202
+ <div style="position: relative;">
203
+ <button onclick="navigator.clipboard.writeText(`{reply.replace('`','\\`')}`)"
204
+ style="position:absolute; right:0; top:0;">📋 Copy</button>
205
+ <div style="padding-right:50px;">{reply}</div>
206
+ </div>
207
+ """, unsafe_allow_html=True)
208
+
209
  st.session_state.messages.append({"role": "assistant", "content": reply})
210
  except Exception as e:
211
  st.error(str(e))