Spaces:
Sleeping
Sleeping
File size: 5,991 Bytes
178ff41 162e051 178ff41 162e051 3de8fcc 162e051 178ff41 d65751c 178ff41 3de8fcc 162e051 3de8fcc 162e051 3de8fcc 178ff41 162e051 3de8fcc 162e051 3de8fcc 162e051 178ff41 162e051 3de8fcc 162e051 178ff41 3de8fcc 162e051 3de8fcc 162e051 3de8fcc | 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 | """CoderInstruc Gradio Space — UI + agent API (remote HF inference, no local model)."""
from __future__ import annotations
import os
from typing import Any, Callable, TypeVar
import gradio as gr
import httpx
DEFAULT_MODEL = "Qwen/Qwen3-Coder-30B-A3B-Instruct"
DEFAULT_ENDPOINT = "https://router.huggingface.co/v1"
SYSTEM_PROMPT = (
"You are CoderInstruc, a coding assistant specialized in Python, TypeScript, Go, and SQL. "
"Prefer correct, minimal, production-ready code. Use tools/schema style answers when asked "
"to edit files or run tests. Refuse requests for malware or secret exfiltration."
)
F = TypeVar("F", bound=Callable[..., Any])
def gpu_if_zerogpu(fn: F) -> F:
"""
ZeroGPU Spaces require at least one @spaces.GPU at startup.
We only call remote Inference API — decorate when the real HF `spaces` lib is present.
"""
try:
import spaces # type: ignore
gpu = getattr(spaces, "GPU", None)
if gpu is None:
return fn
return gpu(duration=120)(fn) # type: ignore[return-value]
except Exception: # noqa: BLE001 — local / CPU / wrong PyPI package
return fn
def _settings() -> dict[str, str]:
token = os.getenv("HF_TOKEN") or os.getenv("CODERINSTRUC_HF_TOKEN") or ""
model = os.getenv("CODERINSTRUC_HF_INFERENCE_MODEL") or DEFAULT_MODEL
endpoint = (
os.getenv("CODERINSTRUC_HF_INFERENCE_ENDPOINT") or DEFAULT_ENDPOINT
).rstrip("/")
return {"token": token, "model": model, "endpoint": endpoint}
def _is_unsafe(text: str) -> bool:
lowered = text.lower()
blocked = (
"ignore previous instructions",
"bypass safety",
"write ransomware",
"create malware to steal",
)
return any(b in lowered for b in blocked)
@gpu_if_zerogpu
def complete(message: str, history: list[dict[str, str]] | None = None) -> str:
"""Core completion used by UI and agent API."""
cfg = _settings()
if not message.strip():
return "Envie uma pergunta de código."
if _is_unsafe(message):
return "Request blocked by safety filter."
if not cfg["token"]:
return (
"Configure o secret `HF_TOKEN` no Space "
"(Settings → Variables and secrets) para habilitar a inferência."
)
messages: list[dict[str, Any]] = [{"role": "system", "content": SYSTEM_PROMPT}]
for turn in history or []:
role = turn.get("role")
content = turn.get("content")
if role in {"user", "assistant"} and content:
messages.append({"role": role, "content": content})
messages.append({"role": "user", "content": message})
payload = {
"model": cfg["model"],
"messages": messages,
"max_tokens": 2048,
"temperature": 0.7,
"top_p": 0.8,
"stream": False,
}
headers = {
"Authorization": f"Bearer {cfg['token']}",
"Content-Type": "application/json",
}
url = f"{cfg['endpoint']}/chat/completions"
try:
with httpx.Client(timeout=180.0) as client:
resp = client.post(url, json=payload, headers=headers)
if resp.status_code >= 400:
return f"Erro HF ({resp.status_code}): {resp.text[:600]}"
data = resp.json()
return data["choices"][0]["message"]["content"]
except Exception as exc: # noqa: BLE001
return f"Falha ao chamar inference: {exc}"
def ask(prompt: str) -> str:
"""Stateless agent endpoint — api_name='ask' for /agents.md clients."""
return complete(prompt, history=None)
def build_ui() -> gr.Blocks:
cfg = _settings()
with gr.Blocks(title="CoderInstruc") as demo:
gr.Markdown(
f"""
# CoderInstruc
Assistente de código (base Qwen3-Coder → post-train CoderInstruc).
**Modelo:** `{cfg["model"]}` · Inferência remota (pesos fora deste Space)
**Agents:** `curl https://huggingface.co/spaces/amarorn/CoderInstruc/agents.md`
> Preferência de hardware: **CPU Basic** (não precisa ZeroGPU). Se estiver em ZeroGPU, o app
> usa `@spaces.GPU` só para satisfazer o runtime — a carga real é a Inference API.
"""
)
with gr.Tab("Chat"):
chatbot = gr.Chatbot(height=480)
msg = gr.Textbox(
placeholder="Ex.: Write a Python binary search with tests",
label="Mensagem",
lines=3,
)
with gr.Row():
send = gr.Button("Enviar", variant="primary")
clear = gr.Button("Limpar")
def respond(user_msg: str, history: list[dict[str, str]] | None):
reply = complete(user_msg, history)
history = list(history or [])
history.append({"role": "user", "content": user_msg})
history.append({"role": "assistant", "content": reply})
return history, ""
send.click(respond, inputs=[msg, chatbot], outputs=[chatbot, msg])
msg.submit(respond, inputs=[msg, chatbot], outputs=[chatbot, msg])
clear.click(lambda: ([], ""), outputs=[chatbot, msg])
gr.Examples(
examples=[
"Write a Python function that reverses a string",
"TypeScript debounce utility with generics",
"SQL: top 5 users by order count",
],
inputs=msg,
)
with gr.Tab("Agent API"):
gr.Markdown(
"Endpoint estático para agents (`api_name=ask`). "
"Também disponível via Gradio API / `agents.md`."
)
prompt = gr.Textbox(label="prompt", lines=4)
answer = gr.Textbox(label="answer", lines=12)
ask_btn = gr.Button("Ask", variant="primary")
ask_btn.click(ask, inputs=prompt, outputs=answer, api_name="ask")
return demo
demo = build_ui()
if __name__ == "__main__":
demo.launch()
|