kvyb commited on
Commit
d692e6c
·
verified ·
1 Parent(s): 20af531

Restore free minimal Space UI

Browse files
Files changed (3) hide show
  1. README.md +3 -3
  2. app.py +0 -55
  3. index.html +131 -0
README.md CHANGED
@@ -2,8 +2,8 @@
2
  title: Qwen3.8 27B Humanlike Chat
3
  colorFrom: indigo
4
  colorTo: pink
5
- sdk: gradio
6
- app_file: app.py
7
  fullWidth: true
8
  short_description: Chat with the Q8_0 Humanlike model through its public API.
9
  models:
@@ -12,4 +12,4 @@ models:
12
 
13
  # Qwen3.8 27B Humanlike Chat
14
 
15
- Gradio chat client for the public OpenAI-compatible inference endpoint.
 
2
  title: Qwen3.8 27B Humanlike Chat
3
  colorFrom: indigo
4
  colorTo: pink
5
+ sdk: static
6
+ app_file: index.html
7
  fullWidth: true
8
  short_description: Chat with the Q8_0 Humanlike model through its public API.
9
  models:
 
12
 
13
  # Qwen3.8 27B Humanlike Chat
14
 
15
+ Chat client for the public OpenAI-compatible inference endpoint.
app.py DELETED
@@ -1,55 +0,0 @@
1
- import json
2
- from urllib.error import HTTPError, URLError
3
- from urllib.request import Request, urlopen
4
-
5
- import gradio as gr
6
-
7
-
8
- API_BASE = "https://qwen38-humanlike-api.qwen38-public-api.workers.dev/v1"
9
- MODEL = "LessThanThreeAI/Qwen3.8-27B-Humanlike-Chat-Q8_0-GGUF"
10
-
11
-
12
- def chat(message, history, max_tokens):
13
- request = Request(
14
- f"{API_BASE}/chat/completions",
15
- data=json.dumps(
16
- {
17
- "model": MODEL,
18
- "messages": [*history, {"role": "user", "content": message}],
19
- "max_tokens": int(max_tokens),
20
- "stream": True,
21
- }
22
- ).encode(),
23
- headers={"Content-Type": "application/json"},
24
- method="POST",
25
- )
26
-
27
- answer = ""
28
- try:
29
- with urlopen(request, timeout=900) as response:
30
- for raw_line in response:
31
- line = raw_line.decode().strip()
32
- if not line.startswith("data:"):
33
- continue
34
- payload = line[5:].strip()
35
- if not payload or payload == "[DONE]":
36
- continue
37
- event = json.loads(payload)
38
- content = event.get("choices", [{}])[0].get("delta", {}).get("content", "")
39
- if content:
40
- answer += content
41
- yield answer
42
- except HTTPError as error:
43
- raise gr.Error(error.read().decode() or f"Inference request failed: HTTP {error.code}") from error
44
- except URLError as error:
45
- raise gr.Error(f"Inference endpoint unavailable: {error.reason}") from error
46
-
47
-
48
- with gr.Blocks(fill_height=True) as demo:
49
- gr.Markdown(f"OpenAI-compatible endpoint: `{API_BASE}`")
50
- max_tokens = gr.Slider(1, 20_000, value=1_024, step=1, label="Max output tokens")
51
- gr.ChatInterface(chat, type="messages", additional_inputs=[max_tokens])
52
-
53
-
54
- if __name__ == "__main__":
55
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
index.html ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Qwen3.8 27B Humanlike Chat</title>
7
+ <style>
8
+ * { box-sizing: border-box; }
9
+ body { margin: 0; color: #111827; background: #fff; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
10
+ main { display: grid; grid-template-rows: auto 1fr auto auto; width: min(900px, 100%); height: 100vh; margin: auto; }
11
+ .endpoint { padding: 12px 16px; color: #6b7280; background: #f9fafb; border-bottom: 1px solid #e5e7eb; font-size: 13px; }
12
+ code { color: #374151; overflow-wrap: anywhere; }
13
+ #chat { padding: 24px 16px; overflow-y: auto; }
14
+ .message { max-width: 80%; margin: 0 0 16px; padding: 12px 16px; border-radius: 12px; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
15
+ .assistant { background: #f3f4f6; }
16
+ .user { margin-left: auto; color: white; background: #4f46e5; }
17
+ .error { color: #991b1b; background: #fee2e2; }
18
+ form { display: grid; grid-template-columns: 1fr auto; gap: 10px; padding: 16px; border-top: 1px solid #e5e7eb; }
19
+ textarea, input { padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 8px; font: inherit; }
20
+ textarea { min-height: 84px; resize: vertical; }
21
+ .controls { display: flex; flex-direction: column; gap: 8px; }
22
+ label { color: #6b7280; font-size: 12px; }
23
+ input { display: block; width: 120px; margin-top: 4px; }
24
+ button { padding: 10px 18px; color: white; background: #4f46e5; border: 0; border-radius: 8px; font-weight: 600; cursor: pointer; }
25
+ button:disabled { opacity: 0.5; cursor: wait; }
26
+ textarea:focus, input:focus, button:focus-visible { outline: 2px solid #6366f1; outline-offset: 2px; }
27
+ #status { min-height: 34px; margin: 0; padding: 0 16px 12px; color: #6b7280; font-size: 12px; }
28
+ @media (max-width: 600px) {
29
+ form { grid-template-columns: 1fr; }
30
+ .controls { flex-direction: row; align-items: end; justify-content: space-between; }
31
+ .message { max-width: 92%; }
32
+ }
33
+ </style>
34
+ </head>
35
+ <body>
36
+ <main>
37
+ <div class="endpoint">OpenAI-compatible endpoint: <code>https://qwen38-humanlike-api.qwen38-public-api.workers.dev/v1</code></div>
38
+ <div id="chat" aria-live="polite">
39
+ <div class="message assistant">Ask anything. The first response can take longer while the inference worker starts.</div>
40
+ </div>
41
+ <form id="form">
42
+ <textarea id="prompt" required aria-label="Message" placeholder="Type a message..."></textarea>
43
+ <div class="controls">
44
+ <label>Max output tokens<input id="max-tokens" type="number" min="1" max="20000" value="1024"></label>
45
+ <button id="send" type="submit">Send</button>
46
+ </div>
47
+ </form>
48
+ <p id="status" aria-live="polite"></p>
49
+ </main>
50
+
51
+ <script>
52
+ const API = "https://qwen38-humanlike-api.qwen38-public-api.workers.dev/v1";
53
+ const MODEL = "LessThanThreeAI/Qwen3.8-27B-Humanlike-Chat-Q8_0-GGUF";
54
+ const messages = [];
55
+ const chat = document.querySelector("#chat");
56
+ const form = document.querySelector("#form");
57
+ const prompt = document.querySelector("#prompt");
58
+ const send = document.querySelector("#send");
59
+ const status = document.querySelector("#status");
60
+
61
+ function addMessage(role, text) {
62
+ const element = document.createElement("div");
63
+ element.className = `message ${role}`;
64
+ element.textContent = text;
65
+ chat.append(element);
66
+ chat.scrollTop = chat.scrollHeight;
67
+ return element;
68
+ }
69
+
70
+ function applyEvent(line, output) {
71
+ if (!line.startsWith("data:")) return;
72
+ const payload = line.slice(5).trim();
73
+ if (!payload || payload === "[DONE]") return;
74
+ const event = JSON.parse(payload);
75
+ const content = event.choices?.[0]?.delta?.content || event.choices?.[0]?.text || "";
76
+ if (content) {
77
+ output.textContent += content;
78
+ chat.scrollTop = chat.scrollHeight;
79
+ }
80
+ }
81
+
82
+ form.addEventListener("submit", async (event) => {
83
+ event.preventDefault();
84
+ const text = prompt.value.trim();
85
+ if (!text) return;
86
+ messages.push({ role: "user", content: text });
87
+ addMessage("user", text);
88
+ prompt.value = "";
89
+ send.disabled = true;
90
+ status.textContent = "Waiting for model...";
91
+ const output = addMessage("assistant", "");
92
+
93
+ try {
94
+ const response = await fetch(`${API}/chat/completions`, {
95
+ method: "POST",
96
+ headers: { "content-type": "application/json" },
97
+ body: JSON.stringify({
98
+ model: MODEL,
99
+ messages,
100
+ max_tokens: Number(document.querySelector("#max-tokens").value),
101
+ stream: true,
102
+ }),
103
+ });
104
+ if (!response.ok) throw new Error((await response.text()) || `HTTP ${response.status}`);
105
+
106
+ const reader = response.body.getReader();
107
+ const decoder = new TextDecoder();
108
+ let buffer = "";
109
+ while (true) {
110
+ const { value, done } = await reader.read();
111
+ buffer += decoder.decode(value || new Uint8Array(), { stream: !done });
112
+ const lines = buffer.split("\n");
113
+ buffer = lines.pop() || "";
114
+ for (const line of lines) applyEvent(line.trim(), output);
115
+ if (done) break;
116
+ }
117
+ if (buffer.trim()) applyEvent(buffer.trim(), output);
118
+ messages.push({ role: "assistant", content: output.textContent });
119
+ status.textContent = "Complete";
120
+ } catch (error) {
121
+ output.classList.add("error");
122
+ output.textContent = `Request failed: ${error.message}`;
123
+ status.textContent = "The inference service did not complete the request.";
124
+ } finally {
125
+ send.disabled = false;
126
+ prompt.focus();
127
+ }
128
+ });
129
+ </script>
130
+ </body>
131
+ </html>