amarorn commited on
Commit
162e051
·
verified ·
1 Parent(s): 1a13c0d

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +41 -8
  2. app.py +132 -51
  3. requirements.txt +3 -0
README.md CHANGED
@@ -1,15 +1,48 @@
1
  ---
2
  title: CoderInstruc
3
- emoji: 💬
4
- colorFrom: yellow
5
- colorTo: purple
6
  sdk: gradio
7
- sdk_version: 6.5.1
8
  app_file: app.py
9
  pinned: false
10
- hf_oauth: true
11
- hf_oauth_scopes:
12
- - inference-api
13
  ---
14
 
15
- An example chatbot using [Gradio](https://gradio.app), [`huggingface_hub`](https://huggingface.co/docs/huggingface_hub/v0.22.2/en/index), and the [Hugging Face Inference API](https://huggingface.co/docs/api-inference/index).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: CoderInstruc
3
+ emoji: 🌵
4
+ colorFrom: green
5
+ colorTo: indigo
6
  sdk: gradio
7
+ sdk_version: 6.20.0
8
  app_file: app.py
9
  pinned: false
10
+ license: apache-2.0
11
+ short_description: CoderInstruc coding assistant (Qwen3-Coder + post-train)
 
12
  ---
13
 
14
+ # CoderInstruc
15
+
16
+ Chat Gradio + API para agents. Inferência remota via Hugging Face Inference Router
17
+ (não baixa o modelo 30B neste Space).
18
+
19
+ ## Agents
20
+
21
+ ```bash
22
+ curl https://huggingface.co/spaces/amarorn/CoderInstruc/agents.md
23
+ ```
24
+
25
+ O `agents.md` é **gerado automaticamente** pela HF para todo Gradio Space. Agents (Claude Code, Codex, etc.) leem esse arquivo e chamam a API Gradio.
26
+
27
+ Endpoint útil neste app: **`ask`** (`prompt` → `answer`).
28
+
29
+ ## Secrets (Settings → Variables and secrets)
30
+
31
+ | Name | Valor |
32
+ |------|--------|
33
+ | `HF_TOKEN` | token com permissão Inference |
34
+ | `CODERINSTRUC_HF_INFERENCE_MODEL` | `Qwen/Qwen3-Coder-30B-A3B-Instruct` (depois: seu adapter) |
35
+ | `CODERINSTRUC_HF_INFERENCE_ENDPOINT` | `https://router.huggingface.co/v1` (opcional) |
36
+
37
+ ## Deploy a partir do GitHub
38
+
39
+ Repo código: `CodeModel` → pasta `spaces/coderinstruc-chat/`
40
+ Space alvo: **`amarorn/CoderInstruc`**
41
+
42
+ ```bash
43
+ uv run python scripts/deploy_space.py --space amarorn/CoderInstruc
44
+ ```
45
+
46
+ Ou GitHub Action `deploy-hf-space` (secret `HF_TOKEN`).
47
+
48
+ > Hosting Gradio no CPU free pode exigir [HF PRO](https://huggingface.co/pro). Sem PRO: `uv run coderinstruc space`
app.py CHANGED
@@ -1,69 +1,150 @@
 
 
 
 
 
 
 
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
 
 
 
 
 
 
 
 
 
3
 
 
 
 
 
 
 
 
4
 
5
- def respond(
6
- message,
7
- history: list[dict[str, str]],
8
- system_message,
9
- max_tokens,
10
- temperature,
11
- top_p,
12
- hf_token: gr.OAuthToken,
13
- ):
14
- """
15
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
16
- """
17
- client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")
18
 
19
- messages = [{"role": "system", "content": system_message}]
 
 
 
 
 
 
 
 
20
 
21
- messages.extend(history)
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  messages.append({"role": "user", "content": message})
24
 
25
- response = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- for message in client.chat_completion(
28
- messages,
29
- max_tokens=max_tokens,
30
- stream=True,
31
- temperature=temperature,
32
- top_p=top_p,
33
- ):
34
- choices = message.choices
35
- token = ""
36
- if len(choices) and choices[0].delta.content:
37
- token = choices[0].delta.content
38
 
39
- response += token
40
- yield response
 
41
 
42
 
 
 
 
 
 
 
 
 
 
 
 
43
  """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- chatbot = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
- ],
60
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- with gr.Blocks() as demo:
63
- with gr.Sidebar():
64
- gr.LoginButton()
65
- chatbot.render()
66
 
 
67
 
68
  if __name__ == "__main__":
69
  demo.launch()
 
1
+ """CoderInstruc Gradio Space — UI + agent-friendly API (agents.md /api_name)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import Any
7
+
8
  import gradio as gr
9
+ import httpx
10
+
11
+ DEFAULT_MODEL = "Qwen/Qwen3-Coder-30B-A3B-Instruct"
12
+ DEFAULT_ENDPOINT = "https://router.huggingface.co/v1"
13
+ SYSTEM_PROMPT = (
14
+ "You are CoderInstruc, a coding assistant specialized in Python, TypeScript, Go, and SQL. "
15
+ "Prefer correct, minimal, production-ready code. Use tools/schema style answers when asked "
16
+ "to edit files or run tests. Refuse requests for malware or secret exfiltration."
17
+ )
18
+
19
 
20
+ def _settings() -> dict[str, str]:
21
+ token = os.getenv("HF_TOKEN") or os.getenv("CODERINSTRUC_HF_TOKEN") or ""
22
+ model = os.getenv("CODERINSTRUC_HF_INFERENCE_MODEL") or DEFAULT_MODEL
23
+ endpoint = (
24
+ os.getenv("CODERINSTRUC_HF_INFERENCE_ENDPOINT") or DEFAULT_ENDPOINT
25
+ ).rstrip("/")
26
+ return {"token": token, "model": model, "endpoint": endpoint}
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ def _is_unsafe(text: str) -> bool:
30
+ lowered = text.lower()
31
+ blocked = (
32
+ "ignore previous instructions",
33
+ "bypass safety",
34
+ "write ransomware",
35
+ "create malware to steal",
36
+ )
37
+ return any(b in lowered for b in blocked)
38
 
 
39
 
40
+ def complete(message: str, history: list[dict[str, str]] | None = None) -> str:
41
+ """Core completion used by UI and agent API."""
42
+ cfg = _settings()
43
+ if not message.strip():
44
+ return "Envie uma pergunta de código."
45
+ if _is_unsafe(message):
46
+ return "Request blocked by safety filter."
47
+ if not cfg["token"]:
48
+ return (
49
+ "Configure o secret `HF_TOKEN` no Space "
50
+ "(Settings → Variables and secrets) para habilitar a inferência."
51
+ )
52
+
53
+ messages: list[dict[str, Any]] = [{"role": "system", "content": SYSTEM_PROMPT}]
54
+ for turn in history or []:
55
+ role = turn.get("role")
56
+ content = turn.get("content")
57
+ if role in {"user", "assistant"} and content:
58
+ messages.append({"role": role, "content": content})
59
  messages.append({"role": "user", "content": message})
60
 
61
+ payload = {
62
+ "model": cfg["model"],
63
+ "messages": messages,
64
+ "max_tokens": 2048,
65
+ "temperature": 0.7,
66
+ "top_p": 0.8,
67
+ "stream": False,
68
+ }
69
+ headers = {
70
+ "Authorization": f"Bearer {cfg['token']}",
71
+ "Content-Type": "application/json",
72
+ }
73
+ url = f"{cfg['endpoint']}/chat/completions"
74
+ try:
75
+ with httpx.Client(timeout=180.0) as client:
76
+ resp = client.post(url, json=payload, headers=headers)
77
+ if resp.status_code >= 400:
78
+ return f"Erro HF ({resp.status_code}): {resp.text[:600]}"
79
+ data = resp.json()
80
+ return data["choices"][0]["message"]["content"]
81
+ except Exception as exc: # noqa: BLE001
82
+ return f"Falha ao chamar inference: {exc}"
83
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
+ def ask(prompt: str) -> str:
86
+ """Stateless agent endpoint — exposed as api_name='ask' for /agents.md clients."""
87
+ return complete(prompt, history=None)
88
 
89
 
90
+ def build_ui() -> gr.Blocks:
91
+ cfg = _settings()
92
+ with gr.Blocks(title="CoderInstruc") as demo:
93
+ gr.Markdown(
94
+ f"""
95
+ # CoderInstruc
96
+ Assistente de código (base Qwen3-Coder → post-train CoderInstruc).
97
+
98
+ **Modelo:** `{cfg["model"]}` · Inferência remota (pesos fora deste Space)
99
+
100
+ **Agents:** `curl https://huggingface.co/spaces/amarorn/CoderInstruc/agents.md`
101
  """
102
+ )
103
+ with gr.Tab("Chat"):
104
+ chatbot = gr.Chatbot(height=480)
105
+ msg = gr.Textbox(
106
+ placeholder="Ex.: Write a Python binary search with tests",
107
+ label="Mensagem",
108
+ lines=3,
109
+ )
110
+ with gr.Row():
111
+ send = gr.Button("Enviar", variant="primary")
112
+ clear = gr.Button("Limpar")
113
+
114
+ def respond(user_msg: str, history: list[dict[str, str]] | None):
115
+ reply = complete(user_msg, history)
116
+ history = list(history or [])
117
+ history.append({"role": "user", "content": user_msg})
118
+ history.append({"role": "assistant", "content": reply})
119
+ return history, ""
120
+
121
+ send.click(respond, inputs=[msg, chatbot], outputs=[chatbot, msg])
122
+ msg.submit(respond, inputs=[msg, chatbot], outputs=[chatbot, msg])
123
+ clear.click(lambda: ([], ""), outputs=[chatbot, msg])
124
+
125
+ gr.Examples(
126
+ examples=[
127
+ "Write a Python function that reverses a string",
128
+ "TypeScript debounce utility with generics",
129
+ "SQL: top 5 users by order count",
130
+ ],
131
+ inputs=msg,
132
+ )
133
+
134
+ with gr.Tab("Agent API"):
135
+ gr.Markdown(
136
+ "Endpoint estático para agents (`api_name=ask`). "
137
+ "Também disponível via Gradio API / `agents.md`."
138
+ )
139
+ prompt = gr.Textbox(label="prompt", lines=4)
140
+ answer = gr.Textbox(label="answer", lines=12)
141
+ ask_btn = gr.Button("Ask", variant="primary")
142
+ ask_btn.click(ask, inputs=prompt, outputs=answer, api_name="ask")
143
+
144
+ return demo
145
 
 
 
 
 
146
 
147
+ demo = build_ui()
148
 
149
  if __name__ == "__main__":
150
  demo.launch()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio>=5.23.0,<7
2
+ httpx>=0.28.0
3
+ python-dotenv>=1.0.0