Alibrown commited on
Commit
01ebb60
·
verified ·
1 Parent(s): ba1c249

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +427 -34
src/streamlit_app.py CHANGED
@@ -1,40 +1,433 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
 
 
 
8
 
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
 
13
- In the meantime, below is an example of what you can do with just a few lines of code:
 
 
 
14
  """
15
 
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
 
 
2
  import streamlit as st
3
+ import tempfile
4
+ import requests
5
+ import json
6
+ from datetime import datetime
7
 
8
+ # ----------------------------------------------------
9
+ # 🚨 HUGGINGFACE SPACES FIX
10
+ # ----------------------------------------------------
11
+ TEMP_STREAMLIT_HOME = os.path.join(tempfile.gettempdir(), "st_config_workaround")
12
+ os.makedirs(TEMP_STREAMLIT_HOME, exist_ok=True)
13
 
14
+ os.environ["STREAMLIT_HOME"] = TEMP_STREAMLIT_HOME
15
+ os.environ["STREAMLIT_GATHER_USAGE_STATS"] = "false"
 
16
 
17
+ CONFIG_PATH = os.path.join(TEMP_STREAMLIT_HOME, "config.toml")
18
+ CONFIG_CONTENT = """
19
+ [browser]
20
+ gatherUsageStats = false
21
  """
22
 
23
+ if not os.path.exists(CONFIG_PATH):
24
+ try:
25
+ with open(CONFIG_PATH, "w") as f:
26
+ f.write(CONFIG_CONTENT)
27
+ except Exception:
28
+ pass
29
+
30
+ # ----------------------------------------------------
31
+ # CONFIG
32
+ # ----------------------------------------------------
33
+ st.set_page_config(
34
+ page_title="AI Code Forge",
35
+ layout="wide",
36
+ page_icon="⚒️"
37
+ )
38
+
39
+ OPENROUTER_API_BASE = "https://openrouter.ai/api/v1"
40
+
41
+ FREE_MODELS = {
42
+ "🧠 DeepSeek Chat": "deepseek/deepseek-chat-v3.1:free",
43
+ "💻 Qwen Coder": "qwen/qwen3-coder:free",
44
+ "🔮 Gemma 27B": "google/gemma-3-27b-it:free",
45
+ "🐬 Dolphin Mistral": "cognitivecomputations/dolphin-mistral-24b-venice-edition:free",
46
+ " Nemotron Nano": "nvidia/nemotron-nano-9b-v2:free",
47
+ }
48
+
49
+ # ----------------------------------------------------
50
+ # SESSION STATE
51
+ # ----------------------------------------------------
52
+ if "messages" not in st.session_state:
53
+ st.session_state.messages = {}
54
+ if "current_tool" not in st.session_state:
55
+ st.session_state.current_tool = "home"
56
+ if "last_output" not in st.session_state:
57
+ st.session_state.last_output = ""
58
+ if "dark_mode" not in st.session_state:
59
+ st.session_state.dark_mode = True
60
+
61
+ # ----------------------------------------------------
62
+ # HELPER FUNCTIONS
63
+ # ----------------------------------------------------
64
+ def fetch_model_contexts(api_key):
65
+ if not api_key:
66
+ return {}
67
+ headers = {"Authorization": f"Bearer {api_key}"}
68
+ try:
69
+ res = requests.get(f"{OPENROUTER_API_BASE}/models", headers=headers, timeout=10)
70
+ contexts = {}
71
+ if res.status_code == 200:
72
+ for m in res.json().get("data", []):
73
+ contexts[m.get("id")] = m.get("context_length", 4096)
74
+ return contexts
75
+ except Exception:
76
+ return {}
77
+
78
+ def call_openrouter(model, messages, temp, max_tok, key, system_prompt=None):
79
+ headers = {
80
+ "Authorization": f"Bearer {key}",
81
+ "Content-Type": "application/json",
82
+ "Referer": "https://aicodecraft.io",
83
+ "X-Title": "AI-Code-Forge",
84
+ }
85
+
86
+ api_messages = messages.copy()
87
+ if system_prompt:
88
+ api_messages.insert(0, {"role": "system", "content": system_prompt})
89
+
90
+ payload = {
91
+ "model": model,
92
+ "messages": api_messages,
93
+ "temperature": temp,
94
+ "max_tokens": max_tok,
95
+ }
96
+
97
+ res = requests.post(
98
+ f"{OPENROUTER_API_BASE}/chat/completions",
99
+ headers=headers,
100
+ data=json.dumps(payload),
101
+ timeout=60
102
+ )
103
+
104
+ if res.status_code == 200:
105
+ try:
106
+ return res.json()["choices"][0]["message"]["content"]
107
+ except (KeyError, IndexError):
108
+ raise Exception("Fehlerhafte API-Antwort")
109
+ else:
110
+ try:
111
+ err = res.json()
112
+ msg = err.get("error", {}).get("message", res.text)
113
+ except:
114
+ msg = res.text
115
+ raise Exception(f"API Error {res.status_code}: {msg}")
116
+
117
+ def extract_code_blocks(text):
118
+ """Extrahiert Code aus Markdown Code-Blöcken"""
119
+ import re
120
+ pattern = r"```(\w+)?\n(.*?)```"
121
+ matches = re.findall(pattern, text, re.DOTALL)
122
+ if matches:
123
+ return [(lang or "text", code.strip()) for lang, code in matches]
124
+ return [("text", text)]
125
+
126
+ # ----------------------------------------------------
127
+ # TOOL PRESETS
128
+ # ----------------------------------------------------
129
+ TOOL_PRESETS = {
130
+ "code_gen": {
131
+ "icon": "🎨",
132
+ "title": "Code Generator",
133
+ "color": "#00ff88",
134
+ "system_prompt": "Du bist ein Expert Code Generator. Erstelle sauberen, produktionsreifen Code mit Kommentaren. Antworte NUR mit Code in Markdown Code-Blöcken.",
135
+ "templates": {
136
+ "Flask REST API": "Erstelle eine Flask REST API mit:\n- CRUD Endpoints für 'tasks'\n- SQLAlchemy Models\n- Error Handling\n- CORS aktiviert",
137
+ "React Component": "Erstelle eine React Functional Component mit:\n- Props für title und items (Array)\n- useState für selected item\n- Tailwind CSS Styling\n- Click Handler",
138
+ "Python CLI Tool": "Erstelle ein Python CLI Tool mit:\n- argparse für Arguments\n- Logging\n- Error Handling\n- Main Guard",
139
+ "FastAPI Endpoint": "Erstelle FastAPI Endpoints:\n- POST /users - Create User\n- GET /users/{id} - Get User\n- Pydantic Models\n- SQLAlchemy Integration",
140
+ }
141
+ },
142
+ "refactor": {
143
+ "icon": "🔄",
144
+ "title": "Code Refactorer",
145
+ "color": "#00aaff",
146
+ "system_prompt": "Du bist ein Code Refactoring Expert. Analysiere Code und verbessere: Lesbarkeit, Performance, Best Practices. Erkläre deine Änderungen kurz.",
147
+ "templates": {
148
+ "Clean Code": "Refactore diesen Code nach Clean Code Prinzipien:\n- Verbessere Naming\n- Reduziere Komplexität\n- Extrahiere Funktionen\n\n[Füge Code hier ein]",
149
+ "Performance": "Optimiere diesen Code für Performance:\n- Identifiziere Bottlenecks\n- Verbessere Algorithmen\n- Reduziere Memory Usage\n\n[Füge Code hier ein]",
150
+ "Type Safety": "Füge Type Hints/TypeScript hinzu:\n- Alle Funktionen\n- Variables wo sinnvoll\n- Return Types\n\n[Füge Code hier ein]",
151
+ }
152
+ },
153
+ "debug": {
154
+ "icon": "🐛",
155
+ "title": "Debug Helper",
156
+ "color": "#ff4444",
157
+ "system_prompt": "Du bist ein Debugging Expert. Analysiere Code/Errors systematisch. Erkläre das Problem und liefere Fix.",
158
+ "templates": {
159
+ "Error Analysis": "Ich habe diesen Error:\n[Error Message]\n\nIn diesem Code:\n[Code]\n\nWas ist das Problem und wie fixe ich es?",
160
+ "Logic Bug": "Dieser Code macht nicht was er soll:\n[Code]\n\nErwartetes Verhalten: [beschreiben]\nAktuelles Verhalten: [beschreiben]\n\nWo ist der Logic Bug?",
161
+ "Performance Issue": "Dieser Code ist zu langsam:\n[Code]\n\nInput Size: [beschreiben]\nAktuelle Runtime: [beschreiben]\n\nWo ist das Problem?",
162
+ }
163
+ },
164
+ "docs": {
165
+ "icon": "📚",
166
+ "title": "Doc Generator",
167
+ "color": "#ffaa00",
168
+ "system_prompt": "Du bist ein Technical Writer. Erstelle klare, strukturierte Dokumentation mit Beispielen.",
169
+ "templates": {
170
+ "Function Docs": "Erstelle Docstrings für diese Funktionen:\n[Code]\n\nFormat: Google Style für Python / JSDoc für JavaScript",
171
+ "README": "Erstelle ein README.md für dieses Projekt:\n- Beschreibung\n- Installation\n- Usage Examples\n- API Reference\n\nProjekt: [beschreiben]",
172
+ "API Docs": "Erstelle API Dokumentation für diese Endpoints:\n[Code/Routes]\n\nInkl. Request/Response Beispiele",
173
+ }
174
+ },
175
+ "explain": {
176
+ "icon": "💡",
177
+ "title": "Code Explainer",
178
+ "color": "#aa88ff",
179
+ "system_prompt": "Du bist ein Code Educator. Erkläre Code Schritt-für-Schritt, klar und verständlich.",
180
+ "templates": {
181
+ "ELI5": "Erkläre diesen Code als wäre ich 5:\n[Code]",
182
+ "Deep Dive": "Erkläre diesen Code im Detail:\n- Was macht er?\n- Wie funktioniert er?\n- Warum wurde es so gelöst?\n- Alternativen?\n\n[Code]",
183
+ "Architecture": "Erkläre die Architektur dieses Codes:\n- Design Patterns\n- Komponenten\n- Datenfluss\n\n[Code]",
184
+ }
185
+ }
186
+ }
187
+
188
+ # ----------------------------------------------------
189
+ # SIDEBAR
190
+ # ----------------------------------------------------
191
+ with st.sidebar:
192
+ st.markdown("# ⚒️ AI Code Forge")
193
+ st.markdown("---")
194
+
195
+ # API Settings
196
+ with st.expander("🔑 API Settings", expanded=True):
197
+ api_key = st.text_input("OpenRouter API Key", type="password")
198
+
199
+ model_name = st.selectbox("Model", list(FREE_MODELS.keys()))
200
+ model = FREE_MODELS[model_name]
201
+
202
+ model_contexts = fetch_model_contexts(api_key)
203
+ default_ctx = model_contexts.get(model, 4096)
204
+
205
+ temperature = st.slider("Temperature", 0.0, 1.0, 0.7, 0.1)
206
+ max_tokens = st.slider(
207
+ "Max Tokens",
208
+ 256,
209
+ min(default_ctx, 16000),
210
+ min(2048, default_ctx),
211
+ step=256
212
+ )
213
+
214
+ st.caption(f"📊 Context: {default_ctx:,} tokens")
215
+
216
+ st.markdown("---")
217
+
218
+ # Navigation
219
+ st.markdown("### 🧭 Tools")
220
+
221
+ if st.button("🏠 Home", use_container_width=True):
222
+ st.session_state.current_tool = "home"
223
+ st.rerun()
224
+
225
+ for tool_id, tool in TOOL_PRESETS.items():
226
+ if st.button(
227
+ f"{tool['icon']} {tool['title']}",
228
+ use_container_width=True,
229
+ type="primary" if st.session_state.current_tool == tool_id else "secondary"
230
+ ):
231
+ st.session_state.current_tool = tool_id
232
+ if tool_id not in st.session_state.messages:
233
+ st.session_state.messages[tool_id] = []
234
+ st.rerun()
235
+
236
+ st.markdown("---")
237
+
238
+ # Actions
239
+ if st.session_state.current_tool != "home":
240
+ if st.button("🗑️ Clear Chat", use_container_width=True):
241
+ if st.session_state.current_tool in st.session_state.messages:
242
+ st.session_state.messages[st.session_state.current_tool] = []
243
+ st.success("Chat cleared!")
244
+ st.rerun()
245
+
246
+ st.markdown("---")
247
+ st.caption("💸 Using Free Models")
248
+ st.caption(f"⏰ {datetime.now().strftime('%H:%M')}")
249
+
250
+ # ----------------------------------------------------
251
+ # MAIN CONTENT
252
+ # ----------------------------------------------------
253
+
254
+ # HOME
255
+ if st.session_state.current_tool == "home":
256
+ st.markdown("# ⚒️ Welcome to AI Code Forge")
257
+ st.markdown("### Your AI-Powered Development Assistant")
258
+
259
+ st.markdown("---")
260
+
261
+ # Tool Cards
262
+ cols = st.columns(3)
263
+ tool_items = list(TOOL_PRESETS.items())
264
+
265
+ for idx, (tool_id, tool) in enumerate(tool_items):
266
+ with cols[idx % 3]:
267
+ with st.container():
268
+ st.markdown(f"""
269
+ <div style="
270
+ padding: 20px;
271
+ border-radius: 10px;
272
+ border-left: 4px solid {tool['color']};
273
+ background: rgba(255,255,255,0.05);
274
+ margin-bottom: 20px;
275
+ ">
276
+ <h2>{tool['icon']} {tool['title']}</h2>
277
+ <p style="opacity: 0.8;">{tool['system_prompt'][:100]}...</p>
278
+ </div>
279
+ """, unsafe_allow_html=True)
280
+
281
+ if st.button(f"Open {tool['title']}", key=f"open_{tool_id}", use_container_width=True):
282
+ st.session_state.current_tool = tool_id
283
+ if tool_id not in st.session_state.messages:
284
+ st.session_state.messages[tool_id] = []
285
+ st.rerun()
286
+
287
+ st.markdown("---")
288
+
289
+ # Quick Stats
290
+ col1, col2, col3 = st.columns(3)
291
+ with col1:
292
+ st.metric("Free Models", len(FREE_MODELS))
293
+ with col2:
294
+ total_msgs = sum(len(msgs) for msgs in st.session_state.messages.values())
295
+ st.metric("Total Messages", total_msgs)
296
+ with col3:
297
+ st.metric("Tools", len(TOOL_PRESETS))
298
+
299
+ # TOOL VIEW
300
+ else:
301
+ tool = TOOL_PRESETS[st.session_state.current_tool]
302
+
303
+ # Header
304
+ st.markdown(f"""
305
+ <div style="
306
+ padding: 20px;
307
+ border-radius: 10px;
308
+ border-left: 6px solid {tool['color']};
309
+ background: rgba(255,255,255,0.05);
310
+ margin-bottom: 30px;
311
+ ">
312
+ <h1>{tool['icon']} {tool['title']}</h1>
313
+ <p style="opacity: 0.8; font-size: 1.1em;">{tool['system_prompt']}</p>
314
+ </div>
315
+ """, unsafe_allow_html=True)
316
+
317
+ # Templates
318
+ with st.expander("📋 Quick Templates", expanded=False):
319
+ for template_name, template_text in tool['templates'].items():
320
+ if st.button(f"📄 {template_name}", key=f"template_{template_name}"):
321
+ st.session_state[f"template_text_{st.session_state.current_tool}"] = template_text
322
+ st.rerun()
323
+
324
+ # Chat Interface
325
+ st.markdown("### 💬 Chat")
326
+
327
+ # Display messages
328
+ for msg in st.session_state.messages.get(st.session_state.current_tool, []):
329
+ with st.chat_message(msg["role"]):
330
+ if msg["role"] == "assistant":
331
+ # Check for code blocks
332
+ code_blocks = extract_code_blocks(msg["content"])
333
+ if len(code_blocks) == 1 and code_blocks[0][0] == "text":
334
+ st.markdown(msg["content"])
335
+ else:
336
+ # Has code blocks
337
+ parts = msg["content"].split("```")
338
+ for i, part in enumerate(parts):
339
+ if i % 2 == 0:
340
+ # Text part
341
+ if part.strip():
342
+ st.markdown(part)
343
+ else:
344
+ # Code part
345
+ lines = part.split("\n", 1)
346
+ lang = lines[0].strip() if lines else "text"
347
+ code = lines[1] if len(lines) > 1 else part
348
+
349
+ col1, col2 = st.columns([6, 1])
350
+ with col1:
351
+ st.code(code, language=lang)
352
+ with col2:
353
+ if st.button("📋", key=f"copy_{i}_{msg.get('timestamp', 0)}"):
354
+ st.toast("Code copied! (simulation)")
355
+ else:
356
+ st.markdown(msg["content"])
357
+
358
+ # Input
359
+ default_text = st.session_state.get(f"template_text_{st.session_state.current_tool}", "")
360
+ if default_text:
361
+ del st.session_state[f"template_text_{st.session_state.current_tool}"]
362
+
363
+ if prompt := st.chat_input("Your message...", key=f"input_{st.session_state.current_tool}"):
364
+ user_prompt = prompt
365
+ elif default_text:
366
+ user_prompt = default_text
367
+ else:
368
+ user_prompt = None
369
+
370
+ if user_prompt:
371
+ if not api_key:
372
+ st.warning("⚠️ Please enter your OpenRouter API Key in the sidebar!")
373
+ st.stop()
374
+
375
+ # Add user message
376
+ st.session_state.messages[st.session_state.current_tool].append({
377
+ "role": "user",
378
+ "content": user_prompt,
379
+ "timestamp": datetime.now().timestamp()
380
+ })
381
+
382
+ with st.chat_message("user"):
383
+ st.markdown(user_prompt)
384
+
385
+ # Prepare messages for API
386
+ api_messages = [
387
+ {"role": m["role"], "content": m["content"]}
388
+ for m in st.session_state.messages[st.session_state.current_tool]
389
+ ]
390
+
391
+ # Generate response
392
+ with st.chat_message("assistant"):
393
+ with st.spinner(f"🤖 {model_name} thinking..."):
394
+ try:
395
+ reply = call_openrouter(
396
+ model,
397
+ api_messages,
398
+ temperature,
399
+ max_tokens,
400
+ api_key,
401
+ system_prompt=tool['system_prompt']
402
+ )
403
+
404
+ # Display with code highlighting
405
+ code_blocks = extract_code_blocks(reply)
406
+ if len(code_blocks) == 1 and code_blocks[0][0] == "text":
407
+ st.markdown(reply)
408
+ else:
409
+ parts = reply.split("```")
410
+ for i, part in enumerate(parts):
411
+ if i % 2 == 0:
412
+ if part.strip():
413
+ st.markdown(part)
414
+ else:
415
+ lines = part.split("\n", 1)
416
+ lang = lines[0].strip() if lines else "text"
417
+ code = lines[1] if len(lines) > 1 else part
418
+ st.code(code, language=lang)
419
+
420
+ st.session_state.messages[st.session_state.current_tool].append({
421
+ "role": "assistant",
422
+ "content": reply,
423
+ "timestamp": datetime.now().timestamp()
424
+ })
425
+
426
+ except Exception as e:
427
+ error_msg = f"❌ Error: {str(e)}"
428
+ st.error(error_msg)
429
+ st.session_state.messages[st.session_state.current_tool].append({
430
+ "role": "assistant",
431
+ "content": error_msg,
432
+ "timestamp": datetime.now().timestamp()
433
+ })