konvursions commited on
Commit
77cff24
·
verified ·
1 Parent(s): 865fcef

Add Qwen3.8-27B Uncensored MLX demo Space

Browse files
Files changed (5) hide show
  1. README.md +41 -7
  2. __pycache__/app.cpython-314.pyc +0 -0
  3. app.py +217 -0
  4. examples/scene.jpg +0 -0
  5. requirements.txt +1 -0
README.md CHANGED
@@ -1,13 +1,47 @@
1
  ---
2
- title: Qwen38 27b Uncensored Mlx Demo
3
- emoji: 👀
4
- colorFrom: red
5
- colorTo: red
6
  sdk: gradio
7
  sdk_version: 6.25.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Qwen3.8-27B Uncensored MLX
3
+ emoji: 🍎
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 6.25.0
 
8
  app_file: app.py
9
+ python_version: "3.12"
10
+ short_description: Chat demo + MLX guide for Qwen3.8-27B
11
+ hf_oauth: true
12
+ hf_oauth_scopes:
13
+ - inference-api
14
+ models:
15
+ - orcarouter/Qwen3.8-27B-Uncensored-MLX
16
+ - Qwen/Qwen3.8-27B
17
  ---
18
 
19
+ # Qwen3.8-27B Uncensored MLX
20
+
21
+ Demo Space for [`orcarouter/Qwen3.8-27B-Uncensored-MLX`](https://huggingface.co/orcarouter/Qwen3.8-27B-Uncensored-MLX) — an abliterated Qwen3.8-27B quantized for Apple Silicon (2 / 4 / 6 / 8-bit).
22
+
23
+ **Live chat cannot load MLX weights on Space hardware.** This app talks to hosted **Qwen/Qwen3.8-27B** (same 27B vision-language architecture, 262K context, thinking + tools). To run the uncensored MLX build, use a Mac and `mlx-vlm` as in the model card.
24
+
25
+ ## Local MLX (Apple Silicon)
26
+
27
+ ```bash
28
+ pip install -U mlx-vlm # mlx-vlm >= 0.6.13, mlx >= 0.32
29
+
30
+ hf download orcarouter/Qwen3.8-27B-Uncensored-MLX --include "4-bit/*" \
31
+ --local-dir ./Qwen3.8-27B-Uncensored-MLX
32
+
33
+ python -m mlx_vlm generate \
34
+ --model ./Qwen3.8-27B-Uncensored-MLX/4-bit \
35
+ --prompt "Explain quantum entanglement in one sentence." --max-tokens 256
36
+ ```
37
+
38
+ 4-bit is the default (~15 GB, 24 GB unified memory). Prefer 6-bit or 8-bit when you have the RAM. Do not use 2-bit for real work.
39
+
40
+ ## Disclaimer
41
+
42
+ The MLX repo has had refusal behavior substantially removed. It is intended for research, interpretability, and red-teaming — not unmoderated production. You are responsible for how you use it and for what it generates. Apache 2.0, inherited from `Qwen/Qwen3.8-27B`.
43
+
44
+ ## Similar Spaces
45
+
46
+ - [akhaliq/Qwen3.8-27B](https://huggingface.co/spaces/akhaliq/Qwen3.8-27B) — official-style ZeroGPU workflow
47
+ - [Yinhaoc/qwen3.8-27b-demo](https://huggingface.co/spaces/Yinhaoc/qwen3.8-27b-demo) — Featherless chat + vision
__pycache__/app.cpython-314.pyc ADDED
Binary file (12.3 kB). View file
 
app.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import mimetypes
3
+ import os
4
+ from pathlib import Path
5
+
6
+ import gradio as gr
7
+ import spaces
8
+ from gradio import ChatMessage
9
+ from huggingface_hub import InferenceClient
10
+ from openai import OpenAI
11
+
12
+ MLX_REPO = "orcarouter/Qwen3.8-27B-Uncensored-MLX"
13
+ HOSTED_MODEL = "Qwen/Qwen3.8-27B"
14
+ ORCA_MODEL = "qwen/qwen3.8-27b-free"
15
+ ORCA_BASE = "https://api.orcarouter.ai/v1"
16
+ HF_PROVIDER = "featherless-ai"
17
+
18
+ ORCA_KEY = os.environ.get("ORCAROUTER_API_KEY") or os.environ.get("ORCA_API_KEY") or ""
19
+ HF_KEY = os.environ.get("HF_TOKEN") or ""
20
+
21
+ CSS = """
22
+ #col-container { max-width: 1100px; margin: 0 auto; }
23
+ .dark .gradio-container { color: var(--body-text-color); }
24
+ """
25
+
26
+
27
+ @spaces.GPU(duration=1)
28
+ def _noop():
29
+ """ZeroGPU requires at least one decorated function; never called."""
30
+ pass
31
+
32
+
33
+ def _file_to_data_url(path: str) -> str:
34
+ mime = mimetypes.guess_type(path)[0] or "image/jpeg"
35
+ data = base64.b64encode(Path(path).read_bytes()).decode()
36
+ return f"data:{mime};base64,{data}"
37
+
38
+
39
+ def _to_parts(content) -> list[dict]:
40
+ """Convert a Gradio multimodal message to OpenAI content parts."""
41
+ parts = []
42
+ if isinstance(content, str):
43
+ if content.strip():
44
+ parts.append({"type": "text", "text": content})
45
+ return parts or [{"type": "text", "text": "..."}]
46
+ text = (content or {}).get("text", "") or ""
47
+ if text.strip():
48
+ parts.append({"type": "text", "text": text})
49
+ for item in (content or {}).get("files", []) or []:
50
+ path = item.get("path", item) if isinstance(item, dict) else item
51
+ parts.append({"type": "image_url", "image_url": {"url": _file_to_data_url(str(path))}})
52
+ return parts or [{"type": "text", "text": "..."}]
53
+
54
+
55
+ def _history_to_messages(history: list[dict]) -> list[dict]:
56
+ messages = []
57
+ for turn in history or []:
58
+ content = turn.get("content")
59
+ role = turn.get("role", "user")
60
+ if turn.get("metadata"):
61
+ continue
62
+ if isinstance(content, str):
63
+ messages.append({"role": role, "content": content})
64
+ else:
65
+ messages.append({"role": role, "content": _to_parts(content)})
66
+ return messages
67
+
68
+
69
+ def _extra_body(enable_thinking: bool, reasoning_effort: str) -> dict:
70
+ return {
71
+ "chat_template_kwargs": {"enable_thinking": bool(enable_thinking)},
72
+ "include_reasoning": bool(enable_thinking),
73
+ "reasoning_effort": reasoning_effort,
74
+ "temperature": 1.0 if enable_thinking else 0.7,
75
+ "top_p": 0.95 if enable_thinking else 0.8,
76
+ "top_k": 20,
77
+ }
78
+
79
+
80
+ def _stream_orca(messages: list[dict], extra_body: dict, max_tokens: int):
81
+ client = OpenAI(base_url=ORCA_BASE, api_key=ORCA_KEY)
82
+ return client.chat.completions.create(
83
+ model=ORCA_MODEL,
84
+ messages=messages,
85
+ max_tokens=max_tokens,
86
+ stream=True,
87
+ extra_body=extra_body,
88
+ ), "OrcaRouter"
89
+
90
+
91
+ def _stream_hf(messages: list[dict], extra_body: dict, max_tokens: int, token: str):
92
+ client = InferenceClient(api_key=token, provider=HF_PROVIDER)
93
+ return client.chat_completion(
94
+ model=HOSTED_MODEL,
95
+ messages=messages,
96
+ max_tokens=max_tokens,
97
+ stream=True,
98
+ extra_body=extra_body,
99
+ ), "Featherless"
100
+
101
+
102
+ def _delta_text(delta) -> tuple[str, str]:
103
+ reasoning = getattr(delta, "reasoning", None) or getattr(delta, "reasoning_content", None) or ""
104
+ content = getattr(delta, "content", None) or ""
105
+ if not reasoning and isinstance(delta, dict):
106
+ reasoning = delta.get("reasoning") or delta.get("reasoning_content") or ""
107
+ content = delta.get("content") or ""
108
+ return reasoning or "", content or ""
109
+
110
+
111
+ def chat(
112
+ message: dict,
113
+ history: list[dict],
114
+ enable_thinking: bool = True,
115
+ reasoning_effort: str = "medium",
116
+ max_tokens: int = 2048,
117
+ oauth_token: gr.OAuthToken | None = None,
118
+ ):
119
+ """Chat with hosted Qwen3.8-27B (text + image). Documents the MLX uncensored build.
120
+
121
+ Args:
122
+ message: Gradio multimodal payload with text and optional image files.
123
+ history: prior turns as OpenAI-style chat messages.
124
+ enable_thinking: let the model reason before answering.
125
+ reasoning_effort: xhigh, medium, or low.
126
+ max_tokens: cap for thinking + answer tokens.
127
+ oauth_token: Hugging Face login token used if no OrcaRouter key is set.
128
+ """
129
+ hf_token = (oauth_token.token if oauth_token is not None else None) or HF_KEY
130
+ if not ORCA_KEY and not hf_token:
131
+ yield ChatMessage(
132
+ content=(
133
+ "Sign in with Hugging Face (sidebar) to run this demo, "
134
+ "or add an `ORCAROUTER_API_KEY` Space secret."
135
+ )
136
+ )
137
+ return
138
+
139
+ messages = _history_to_messages(history) + [{"role": "user", "content": _to_parts(message)}]
140
+ extra = _extra_body(enable_thinking, reasoning_effort)
141
+
142
+ try:
143
+ if ORCA_KEY:
144
+ stream, _backend = _stream_orca(messages, extra, int(max_tokens))
145
+ else:
146
+ stream, _backend = _stream_hf(messages, extra, int(max_tokens), hf_token)
147
+ except Exception as exc:
148
+ yield ChatMessage(content=f"Could not start generation: {exc}")
149
+ return
150
+
151
+ thinking = ChatMessage(role="assistant", content="", metadata={"title": "Thinking", "status": "pending"})
152
+ answer = ChatMessage(role="assistant", content="")
153
+ if enable_thinking:
154
+ yield [thinking, answer]
155
+
156
+ try:
157
+ for chunk in stream:
158
+ if not getattr(chunk, "choices", None):
159
+ continue
160
+ delta = chunk.choices[0].delta
161
+ reason_bit, content_bit = _delta_text(delta)
162
+ if reason_bit:
163
+ thinking.content += reason_bit
164
+ if content_bit:
165
+ answer.content += content_bit
166
+ if enable_thinking:
167
+ yield [thinking, answer]
168
+ else:
169
+ yield answer
170
+ except Exception as exc:
171
+ answer.content = (answer.content or "") + f"\n\nGeneration stopped: {exc}"
172
+ yield [thinking, answer] if enable_thinking else answer
173
+ return
174
+
175
+ if enable_thinking:
176
+ thinking.metadata["status"] = "done"
177
+ if not (answer.content or "").strip() and (thinking.content or "").strip():
178
+ answer.content = thinking.content
179
+ yield [thinking, answer]
180
+ else:
181
+ yield answer
182
+
183
+
184
+ EXAMPLES = [
185
+ [{"text": "Explain quantum entanglement in one sentence.", "files": []}, True, "medium", 1024],
186
+ [{"text": "Describe this image: colors, objects, and any text.", "files": ["examples/scene.jpg"]}, True, "low", 1024],
187
+ [{"text": "Write a Python function that merges two sorted lists.", "files": []}, False, "medium", 2048],
188
+ ]
189
+
190
+ demo = gr.ChatInterface(
191
+ chat,
192
+ multimodal=True,
193
+ type="messages",
194
+ title="Qwen3.8-27B Uncensored MLX",
195
+ description=(
196
+ "Demo for "
197
+ f"[`{MLX_REPO}`](https://huggingface.co/{MLX_REPO}) — "
198
+ "abliterated Qwen3.8-27B in 2/4/6/8-bit **MLX** for Apple Silicon. "
199
+ "Live chat uses hosted "
200
+ f"[`{HOSTED_MODEL}`](https://huggingface.co/{HOSTED_MODEL}) "
201
+ "(Spaces cannot run MLX). "
202
+ "Run the uncensored weights locally with `mlx-vlm` — see the "
203
+ "[model card](https://huggingface.co/orcarouter/Qwen3.8-27B-Uncensored-MLX). "
204
+ "Abliterated weights have refusals removed; research use only."
205
+ ),
206
+ examples=EXAMPLES,
207
+ additional_inputs=[
208
+ gr.Checkbox(label="Thinking", value=True),
209
+ gr.Dropdown(choices=["xhigh", "medium", "low"], value="medium", label="Reasoning effort"),
210
+ gr.Slider(512, 8192, value=2048, step=256, label="Max tokens"),
211
+ ],
212
+ theme=gr.themes.Citrus(),
213
+ css=CSS,
214
+ )
215
+
216
+ if __name__ == "__main__":
217
+ demo.launch(mcp_server=True)
examples/scene.jpg ADDED
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ openai