import gradio as gr
import requests, json, os, time
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import uvicorn
OLLAMA = "http://localhost:11434"
MODEL = "FableForge-AI/shellwhisperer"
fastapi_app = FastAPI(docs_url=None, redoc_url=None)
@fastapi_app.post("/api/chat")
async def api_chat(request: Request):
body = await request.json()
try:
r = requests.post(f"{OLLAMA}/api/chat", json=body, timeout=120)
return JSONResponse(content=r.json())
except Exception as e:
return JSONResponse(status_code=500, content={"error": str(e)})
# ── helpers ──
def model_info():
"""Get model details and status."""
info = {
"ready": False,
"model": MODEL,
"size": "?",
"speed": "?",
"error": "",
}
try:
r = requests.get(f"{OLLAMA}/api/tags", timeout=5)
if r.status_code == 200:
models = r.json().get("models", [])
for m in models:
if MODEL in m.get("name", ""):
info["ready"] = True
size_gb = m.get("size", 0) / 1e9
info["size"] = f"{size_gb:.1f} GB"
break
if not info["ready"]:
info["error"] = "Model not pulled yet"
else:
info["error"] = f"Ollama returned {r.status_code}"
except requests.ConnectionError:
info["error"] = "Ollama not running"
except Exception as e:
info["error"] = str(e)
return info
def generate(prompt, temp):
if not prompt.strip():
yield "⚠️ Enter a prompt first."
return
info = model_info()
if not info["ready"]:
yield f"⏳ Model loading... ({info['error']})"
return
try:
r = requests.post(f"{OLLAMA}/api/chat", json={
"model": MODEL,
"messages": [
{"role": "system", "content": "You are ShellWhisperer-1.5B, a shell and CLI specialist. Output working code only, no explanations."},
{"role": "user", "content": prompt}
],
"stream": True,
"options": {"temperature": temp, "num_ctx": 16384}
}, stream=True, timeout=120)
full = []
for line in r.iter_lines():
if not line:
continue
try:
d = json.loads(line)
chunk = d.get("message", {}).get("content", "")
if chunk:
full.append(chunk)
yield "".join(full)
except json.JSONDecodeError:
pass
if not full:
yield "⚠️ Empty response from model. Try again."
except requests.Timeout:
yield "⏰ Request timed out after 120s. Try a shorter prompt."
except Exception as e:
yield f"❌ Error: {e}"
# ── UI ──
with gr.Blocks(
title="ShellWhisperer-1.5B · API",
theme=gr.themes.Soft(),
fill_height=True,
css="""footer { display: none !important; }
.status-ok { color: #22c55e; font-weight: 600; }
.status-loading { color: #f59e0b; font-weight: 600; }
.status-err { color: #ef4444; font-weight: 600; }
.api-box { background: #1f2937; color: #e5e7eb; padding: 1em; border-radius: 8px; font-family: monospace; font-size: 0.9em; overflow-x: auto; }
""",
) as demo:
gr.Markdown("""# 🐚 ShellWhisperer-1.5B · API Demo
**CLI & Shell Code Specialist** · 1 GB · 29 tok/s · 16K context · Apache 2.0
Built by **FableForge AI** — part of the [Mythos model ecosystem](https://github.com/KingLabsA/mythos).
""")
# ── status dashboard ──
with gr.Row():
status_badge = gr.Markdown("⏳ Checking...")
model_size = gr.Markdown("")
model_speed = gr.Markdown("")
with gr.Tabs():
with gr.TabItem("🧪 Try it"):
with gr.Row():
with gr.Column(scale=3):
inp = gr.Textbox(
label="Prompt",
placeholder="Write a bash script to...",
lines=4,
)
with gr.Row():
temp = gr.Slider(0.0, 1.0, value=0.3, step=0.05, label="Temperature")
btn = gr.Button("🚀 Generate", variant="primary", scale=1, size="lg")
out = gr.Textbox(label="Output", lines=16)
gr.Markdown("### 💡 Try these")
gr.Examples(
examples=[
["Write a bash script to monitor a directory for new files and log them"],
["Write a Python script to batch resize images to 800px wide"],
["Write a Docker Compose file for a web app with PostgreSQL"],
["Write a git pre-commit hook that runs tests"],
],
inputs=inp,
label="",
)
with gr.TabItem("📡 API"):
gr.Markdown("""### REST API
This space exposes a standard Ollama-compatible chat endpoint.
```
POST /api/chat
Content-Type: application/json
```
**cURL:**
```bash
curl -X POST https://karma-devops-shellwhisperer-demo.hf.space/api/chat \\
-H "Content-Type: application/json" \\
-d '{
"model": "FableForge-AI/shellwhisperer",
"messages": [{"role": "user", "content": "Write a bash script"}],
"stream": false,
"options": {"temperature": 0.3, "num_ctx": 16384}
}'
```
**Python:**
```python
import requests
r = requests.post("https://karma-devops-shellwhisperer-demo.hf.space/api/chat", json={
"model": "FableForge-AI/shellwhisperer",
"messages": [{"role": "user", "content": "Write a bash script"}],
"stream": False,
"options": {"temperature": 0.3, "num_ctx": 16384}
})
print(r.json()["message"]["content"])
```
""")
with gr.TabItem("ℹ️ About"):
gr.Markdown("""### Model
| Property | Value |
|---|---|
| Name | ShellWhisperer-1.5B |
| Author | FableForge AI ([KingLabsA](https://github.com/KingLabsA)) |
| Size | 1 GB |
| Speed | ~29 tok/s (T4 GPU) |
| Context | 16,384 tokens |
| License | Apache 2.0 |
| Base | Qwen2.5-Coder-1.5B-Instruct |
### Links
- [GitHub: KingLabsA/mythos](https://github.com/KingLabsA/mythos)
- [HuggingFace: King3Djbl](https://huggingface.co/King3Djbl)
- [HuggingFace: fableforge-ai](https://huggingface.co/fableforge-ai)
- [Ollama: FableForge-AI](https://ollama.com/FableForge-AI)
""")
# ── events ──
def refresh_status():
info = model_info()
if info["ready"]:
badge = f'✅ Model: Ready'
size = f'📦 {info["size"]}'
speed = '⚡ ~29 tok/s'
else:
badge = f'⏳ Model: {info["error"]}'
size = ""
speed = ""
return badge, size, speed
demo.load(fn=refresh_status, outputs=[status_badge, model_size, model_speed])
gr.Timer(30).tick(fn=refresh_status, outputs=[status_badge, model_size, model_speed])
btn.click(
fn=generate,
inputs=[inp, temp],
outputs=out,
)
app = gr.mount_gradio_app(fastapi_app, demo, path="/")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)