Spaces:
Sleeping
Sleeping
File size: 17,874 Bytes
7614791 01ebb60 c38897a 01ebb60 c38897a 01ebb60 c38897a 01ebb60 c38897a 01ebb60 c38897a 01ebb60 1af98df 01ebb60 1af98df 01ebb60 1af98df 01ebb60 1af98df 01ebb60 1af98df 01ebb60 1af98df 01ebb60 1af98df 01ebb60 1af98df 01ebb60 1af98df 01ebb60 1af98df 01ebb60 8eb09ea 01ebb60 0c67ab3 01ebb60 0c67ab3 01ebb60 8eb09ea 0c67ab3 8eb09ea 01ebb60 8eb09ea 0c67ab3 01ebb60 8eb09ea 01ebb60 8eb09ea 01ebb60 |
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 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# Copyright Volkan Sah aka AliBrown
# Free to use with your own api-key
import os
import streamlit as st
import tempfile
import requests
import json
from datetime import datetime
# ----------------------------------------------------
# ๐จ HUGGINGFACE SPACES FIX
# ----------------------------------------------------
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 Exception:
pass
# ----------------------------------------------------
# CONFIG
# ----------------------------------------------------
st.set_page_config(
page_title="AI Code Forge",
layout="wide",
page_icon="โ๏ธ"
)
OPENROUTER_API_BASE = "https://openrouter.ai/api/v1"
FREE_MODELS = {
"๐ง DeepSeek Chat": "deepseek/deepseek-chat-v3.1:free",
"๐ป Qwen Coder": "qwen/qwen3-coder:free",
"๐ฎ Gemma 27B": "google/gemma-3-27b-it:free",
"๐ฌ Dolphin Mistral": "cognitivecomputations/dolphin-mistral-24b-venice-edition:free",
"โก Nemotron Nano": "nvidia/nemotron-nano-9b-v2:free",
}
# ----------------------------------------------------
# SESSION STATE
# ----------------------------------------------------
if "messages" not in st.session_state:
st.session_state.messages = {}
if "current_tool" not in st.session_state:
st.session_state.current_tool = "home"
if "last_output" not in st.session_state:
st.session_state.last_output = ""
if "dark_mode" not in st.session_state:
st.session_state.dark_mode = True
# ----------------------------------------------------
# HELPER FUNCTIONS
# ----------------------------------------------------
def fetch_model_contexts(api_key):
if not api_key:
return {}
headers = {"Authorization": f"Bearer {api_key}"}
try:
res = requests.get(f"{OPENROUTER_API_BASE}/models", headers=headers, timeout=10)
contexts = {}
if res.status_code == 200:
for m in res.json().get("data", []):
contexts[m.get("id")] = m.get("context_length", 4096)
return contexts
except Exception:
return {}
def call_openrouter(model, messages, temp, max_tok, key, system_prompt=None):
headers = {
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
"Referer": "https://aicodecraft.io",
"X-Title": "AI-Code-Forge",
}
api_messages = messages.copy()
if system_prompt:
api_messages.insert(0, {"role": "system", "content": system_prompt})
payload = {
"model": model,
"messages": api_messages,
"temperature": temp,
"max_tokens": max_tok,
}
res = requests.post(
f"{OPENROUTER_API_BASE}/chat/completions",
headers=headers,
data=json.dumps(payload),
timeout=60
)
if res.status_code == 200:
try:
return res.json()["choices"][0]["message"]["content"]
except (KeyError, IndexError):
raise Exception("Fehlerhafte API-Antwort")
else:
try:
err = res.json()
msg = err.get("error", {}).get("message", res.text)
except:
msg = res.text
raise Exception(f"API Error {res.status_code}: {msg}")
def extract_code_blocks(text):
"""Extrahiert Code aus Markdown Code-Blรถcken"""
import re
pattern = r"```(\w+)?\n(.*?)```"
matches = re.findall(pattern, text, re.DOTALL)
if matches:
return [(lang or "text", code.strip()) for lang, code in matches]
return [("text", text)]
# ----------------------------------------------------
# TOOL PRESETS
# ----------------------------------------------------
TOOL_PRESETS = {
"code_gen": {
"icon": "๐จ",
"title": "Code Generator",
"color": "#00ff88",
"system_prompt": "You are an Expert Code Generator. Create clean, production-ready code with comments. Respond ONLY with code in Markdown code blocks.",
"templates": {
"Flask REST API": "Create a Flask REST API with:\n- CRUD Endpoints for 'tasks'\n- SQLAlchemy Models\n- Error Handling\n- CORS enabled",
"React Component": "Create a React Functional Component with:\n- Props for title and items (Array)\n- useState for selected item\n- Tailwind CSS Styling\n- Click Handler",
"Python CLI Tool": "Create a Python CLI Tool with:\n- argparse for Arguments\n- Logging\n- Error Handling\n- Main Guard",
"FastAPI Endpoint": "Create FastAPI Endpoints:\n- POST /users - Create User\n- GET /users/{id} - Get User\n- Pydantic Models\n- SQLAlchemy Integration",
}
},
"refactor": {
"icon": "๐",
"title": "Code Refactorer",
"color": "#00aaff",
"system_prompt": "You are a Code Refactoring Expert. Analyze code and improve: readability, performance, best practices. Explain your changes briefly.",
"templates": {
"Clean Code": "Refactor this code according to Clean Code principles:\n- Improve Naming\n- Reduce Complexity\n- Extract Functions\n\n[Insert Code Here]",
"Performance": "Optimize this code for performance:\n- Identify Bottlenecks\n- Improve Algorithms\n- Reduce Memory Usage\n\n[Insert Code Here]",
"Type Safety": "Add Type Hints/TypeScript:\n- All Functions\n- Variables where appropriate\n- Return Types\n\n[Insert Code Here]",
}
},
"debug": {
"icon": "๐",
"title": "Debug Helper",
"color": "#ff4444",
"system_prompt": "You are a Debugging Expert. Analyze code/errors systematically. Explain the problem and provide the fix.",
"templates": {
"Error Analysis": "I have this error:\n[Error Message]\n\nIn this code:\n[Code]\n\nWhat is the problem and how do I fix it?",
"Logic Bug": "This code doesn't do what it's supposed to:\n[Code]\n\nExpected behavior: [describe]\nActual behavior: [describe]\n\nWhere is the logic bug?",
"Performance Issue": "This code is too slow:\n[Code]\n\nInput Size: [describe]\nCurrent Runtime: [describe]\n\nWhere is the problem?",
}
},
"docs": {
"icon": "๐",
"title": "Doc Generator",
"color": "#ffaa00",
"system_prompt": "You are a Technical Writer. Create clear, structured documentation with examples.",
"templates": {
"Function Docs": "Create Docstrings for these functions:\n[Code]\n\nFormat: Google Style for Python / JSDoc for JavaScript",
"README": "Create a README.md for this project:\n- Description\n- Installation\n- Usage Examples\n- API Reference\n\nProject: [describe]",
"API Docs": "Create API Documentation for these Endpoints:\n[Code/Routes]\n\nIncl. Request/Response Examples",
}
},
"explain": {
"icon": "๐ก",
"title": "Code Explainer",
"color": "#aa88ff",
"system_prompt": "You are a Code Educator. Explain code step-by-step, clearly and understandably.",
"templates": {
"ELI5": "Explain this code as if I were 5:\n[Code]",
"Deep Dive": "Explain this code in detail:\n- What does it do?\n- How does it work?\n- Why was it solved this way?\n- Alternatives?\n\n[Code]",
"Architecture": "Explain the architecture of this code:\n- Design Patterns\n- Components\n- Data Flow\n\n[Code]",
}
}
}
# ----------------------------------------------------
# SIDEBAR
# ----------------------------------------------------
# ----------------------------------------------------
# SIDEBAR
# ----------------------------------------------------
with st.sidebar:
st.markdown("# โ๏ธ AI Code Forge")
st.markdown("---")
# API Settings
with st.expander("๐ API Settings", expanded=True):
api_key = st.text_input("OpenRouter API Key", type="password")
model_name = st.selectbox("Model", list(FREE_MODELS.keys()))
model = FREE_MODELS[model_name]
# Kontext-Lรคnge dynamisch von API abrufen
model_contexts = fetch_model_contexts(api_key)
# Falls API nichts liefert, nutzen wir 4096 als Sicherheits-Fallback
default_ctx = model_contexts.get(model, 4096)
temperature = st.slider("Temperature", 0.0, 1.0, 0.7, 0.1)
# Dynamische Slider-Konfiguration
# Wir erlauben das volle Limit, das die API meldet (z.B. 160.000)
# Wir setzen slider_max aber auf mindestens 4096, falls default_ctx kleiner ist
slider_max = max(default_ctx, 4096)
slider_default = min(2048, slider_max)
max_tokens = st.slider(
"Max Tokens",
min_value=256,
max_value=slider_max,
value=slider_default,
step=256,
key=f"slider_{model}_{slider_max}" # Verhindert das Hรคngenbleiben bei Modellwechsel
)
st.caption(f"๐ Model Max Context: {default_ctx:,} tokens")
st.markdown("---")
# ... Rest Navigation ...
# Navigation
st.markdown("### ๐งญ Tools")
if st.button("๐ Home", use_container_width=True):
st.session_state.current_tool = "home"
st.rerun()
for tool_id, tool in TOOL_PRESETS.items():
if st.button(
f"{tool['icon']} {tool['title']}",
use_container_width=True,
type="primary" if st.session_state.current_tool == tool_id else "secondary"
):
st.session_state.current_tool = tool_id
if tool_id not in st.session_state.messages:
st.session_state.messages[tool_id] = []
st.rerun()
st.markdown("---")
# Actions
if st.session_state.current_tool != "home":
if st.button("๐๏ธ Clear Chat", use_container_width=True):
if st.session_state.current_tool in st.session_state.messages:
st.session_state.messages[st.session_state.current_tool] = []
st.success("Chat cleared!")
st.rerun()
st.markdown("---")
st.caption("๐ธ Using Free Models")
st.caption(f"โฐ {datetime.now().strftime('%H:%M')}")
# ----------------------------------------------------
# MAIN CONTENT
# ----------------------------------------------------
# HOME
if st.session_state.current_tool == "home":
st.markdown("# โ๏ธ Welcome to AI Code Forge")
st.markdown("### Your AI-Powered Development Assistant")
st.markdown("---")
# Tool Cards
cols = st.columns(3)
tool_items = list(TOOL_PRESETS.items())
for idx, (tool_id, tool) in enumerate(tool_items):
with cols[idx % 3]:
with st.container():
st.markdown(f"""
<div style="
padding: 20px;
border-radius: 10px;
border-left: 4px solid {tool['color']};
background: rgba(255,255,255,0.05);
margin-bottom: 20px;
">
<h2>{tool['icon']} {tool['title']}</h2>
<p style="opacity: 0.8;">{tool['system_prompt'][:100]}...</p>
</div>
""", unsafe_allow_html=True)
if st.button(f"Open {tool['title']}", key=f"open_{tool_id}", use_container_width=True):
st.session_state.current_tool = tool_id
if tool_id not in st.session_state.messages:
st.session_state.messages[tool_id] = []
st.rerun()
st.markdown("---")
# Quick Stats
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Free Models", len(FREE_MODELS))
with col2:
total_msgs = sum(len(msgs) for msgs in st.session_state.messages.values())
st.metric("Total Messages", total_msgs)
with col3:
st.metric("Tools", len(TOOL_PRESETS))
# TOOL VIEW
else:
tool = TOOL_PRESETS[st.session_state.current_tool]
# Header
st.markdown(f"""
<div style="
padding: 20px;
border-radius: 10px;
border-left: 6px solid {tool['color']};
background: rgba(255,255,255,0.05);
margin-bottom: 30px;
">
<h1>{tool['icon']} {tool['title']}</h1>
<p style="opacity: 0.8; font-size: 1.1em;">{tool['system_prompt']}</p>
</div>
""", unsafe_allow_html=True)
# Templates
with st.expander("๐ Quick Templates", expanded=False):
for template_name, template_text in tool['templates'].items():
if st.button(f"๐ {template_name}", key=f"template_{template_name}"):
st.session_state[f"template_text_{st.session_state.current_tool}"] = template_text
st.rerun()
# Chat Interface
st.markdown("### ๐ฌ Chat")
# Display messages
for msg in st.session_state.messages.get(st.session_state.current_tool, []):
with st.chat_message(msg["role"]):
if msg["role"] == "assistant":
# Check for code blocks
code_blocks = extract_code_blocks(msg["content"])
if len(code_blocks) == 1 and code_blocks[0][0] == "text":
st.markdown(msg["content"])
else:
# Has code blocks
parts = msg["content"].split("```")
for i, part in enumerate(parts):
if i % 2 == 0:
# Text part
if part.strip():
st.markdown(part)
else:
# Code part
lines = part.split("\n", 1)
lang = lines[0].strip() if lines else "text"
code = lines[1] if len(lines) > 1 else part
col1, col2 = st.columns([6, 1])
with col1:
st.code(code, language=lang)
with col2:
if st.button("๐", key=f"copy_{i}_{msg.get('timestamp', 0)}"):
st.toast("Code copied! (simulation)")
else:
st.markdown(msg["content"])
# Input
default_text = st.session_state.get(f"template_text_{st.session_state.current_tool}", "")
if default_text:
del st.session_state[f"template_text_{st.session_state.current_tool}"]
if prompt := st.chat_input("Your message...", key=f"input_{st.session_state.current_tool}"):
user_prompt = prompt
elif default_text:
user_prompt = default_text
else:
user_prompt = None
if user_prompt:
if not api_key:
st.warning("โ ๏ธ Please enter your OpenRouter API Key in the sidebar!")
st.stop()
# Add user message
st.session_state.messages[st.session_state.current_tool].append({
"role": "user",
"content": user_prompt,
"timestamp": datetime.now().timestamp()
})
with st.chat_message("user"):
st.markdown(user_prompt)
# Prepare messages for API
api_messages = [
{"role": m["role"], "content": m["content"]}
for m in st.session_state.messages[st.session_state.current_tool]
]
# Generate response
with st.chat_message("assistant"):
with st.spinner(f"๐ค {model_name} thinking..."):
try:
reply = call_openrouter(
model,
api_messages,
temperature,
max_tokens,
api_key,
system_prompt=tool['system_prompt']
)
# Display with code highlighting
code_blocks = extract_code_blocks(reply)
if len(code_blocks) == 1 and code_blocks[0][0] == "text":
st.markdown(reply)
else:
parts = reply.split("```")
for i, part in enumerate(parts):
if i % 2 == 0:
if part.strip():
st.markdown(part)
else:
lines = part.split("\n", 1)
lang = lines[0].strip() if lines else "text"
code = lines[1] if len(lines) > 1 else part
st.code(code, language=lang)
st.session_state.messages[st.session_state.current_tool].append({
"role": "assistant",
"content": reply,
"timestamp": datetime.now().timestamp()
})
except Exception as e:
error_msg = f"โ Error: {str(e)}"
st.error(error_msg)
st.session_state.messages[st.session_state.current_tool].append({
"role": "assistant",
"content": error_msg,
"timestamp": datetime.now().timestamp()
}) |