anurag051194 commited on
Commit
884acd5
·
verified ·
1 Parent(s): 9890229

Launch TwIL-LM3-Pro ZeroGPU demo

Browse files

Adapt the TwIL-LM3 interface for TwIL-LM3-Pro and expose generation controls up to 16,384 new tokens.

Files changed (8) hide show
  1. .gitattributes +1 -0
  2. .gitignore +3 -0
  3. README.md +32 -8
  4. app.py +1022 -0
  5. brand/AppiCon.svg +378 -0
  6. brand/avatar.svg +4 -0
  7. brand/webai-cube-256.webp +3 -0
  8. requirements.txt +2 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ brand/webai-cube-256.webp filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .gradio/
README.md CHANGED
@@ -1,13 +1,37 @@
1
  ---
2
- title: TwIL LM3 Pro
3
- emoji: 🏆
4
- colorFrom: blue
5
- colorTo: green
6
  sdk: gradio
7
- sdk_version: 6.29.1
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: TwIL-LM3-Pro
3
+ emoji: 🧠
4
+ colorFrom: indigo
5
+ colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 6.22.0
 
8
  app_file: app.py
9
+ pinned: true
10
+ license: other
11
+ short_description: Official demo of the TwIL-LM3-Pro logic reasoner
12
+ python_version: "3.12"
13
+ startup_duration_timeout: 30m
14
  ---
15
 
16
+ # TwIL-LM3-Pro — Official Demo
17
+
18
+ Official Hugging Face Space for
19
+ [`webAI-Official/TwIL-LM3-Pro`](https://huggingface.co/webAI-Official/TwIL-LM3-Pro),
20
+ webAI's 3.66B reasoning model for **formal logic**.
21
+
22
+ TwIL-LM3-Pro is built from
23
+ [`ibm-granite/granite-4.2-3b`](https://huggingface.co/ibm-granite/granite-4.2-3b)
24
+ through LoRA SFT, checkpoint fusion, WiSE-FT interpolation, and entropy-weighted
25
+ GRPO. It emits a collapsible `<think>…</think>` trace before the answer.
26
+
27
+ **Temperature = 0** (greedy) matches the published evaluation protocol. Keep
28
+ max new tokens at 2048 or higher so the reasoning block is not truncated. The
29
+ interactive control supports up to **16,384 new tokens** for long reasoning
30
+ traces; larger requests reserve more ZeroGPU time and consume more visitor
31
+ quota.
32
+
33
+ This Space runs on ZeroGPU and streams tokens as they are generated.
34
+
35
+ > **License:** the model is released under the *webAI Non-Commercial License
36
+ > ver. 1.0* — see the [model repository](https://huggingface.co/webAI-Official/TwIL-LM3-Pro)
37
+ > for full terms.
app.py ADDED
@@ -0,0 +1,1022 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ from collections.abc import Iterator
4
+ from pathlib import Path
5
+ from threading import Thread
6
+
7
+ PREVIEW = os.getenv("TWIL_UI_PREVIEW") == "1"
8
+
9
+ if not PREVIEW:
10
+ import spaces
11
+ import torch
12
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
13
+ else:
14
+
15
+ class spaces: # type: ignore[no-redef]
16
+ @staticmethod
17
+ def GPU(duration=None, **kwargs):
18
+ def decorator(fn):
19
+ return fn
20
+
21
+ return decorator
22
+
23
+ import gradio as gr
24
+
25
+ MODEL_ID = "webAI-Official/TwIL-LM3-Pro"
26
+ MAX_INPUT_TOKEN_LENGTH = int(os.getenv("MAX_INPUT_TOKEN_LENGTH", "8192"))
27
+ ASSETS = Path(__file__).resolve().parent / "brand"
28
+ AVATAR = ASSETS / "avatar.svg"
29
+
30
+ THINK_OPEN = chr(60) + "think" + chr(62)
31
+ THINK_CLOSE = chr(60) + chr(47) + "think" + chr(62)
32
+ _THINK_RE = re.compile(
33
+ re.escape(THINK_OPEN) + r".*?(" + re.escape(THINK_CLOSE) + r"|$)",
34
+ re.DOTALL,
35
+ )
36
+
37
+ SYSTEM_PROMPT = (
38
+ "You are TwIL-LM3-Pro, a formal-logic reasoning model created by webAI Intelligence Lab. "
39
+ "You specialise in entailment, first-order logic, semantic parsing, Lean, and proof critique. "
40
+ f"Work through the problem in a {THINK_OPEN} block, then give a concise, precise answer."
41
+ )
42
+
43
+ CHIPS = [
44
+ (
45
+ "Analyze an argument",
46
+ "Analyze this argument: identify the premises and the conclusion, say whether it is valid, and name any fallacy.\n\n",
47
+ ),
48
+ (
49
+ "Check an entailment",
50
+ "Do these premises entail the conclusion? Give a formal proof or a counterexample.\n\nPremise 1: \nPremise 2: \nConclusion: ",
51
+ ),
52
+ (
53
+ "Translate to FOL",
54
+ "Translate into first-order logic: Every student who studies hard passes at least one exam.",
55
+ ),
56
+ ]
57
+
58
+ SHOWCASE_TEMPLATE = (
59
+ "Analyze this argument: extract the premises and the conclusion, translate them "
60
+ "into first-order logic, state whether the argument is valid, and name any fallacy.\n\n"
61
+ 'Argument: "{argument}"'
62
+ )
63
+
64
+ SHOWCASE = [
65
+ {
66
+ "kicker": "Viral tweet",
67
+ "quote": "The streets are wet this morning, and rain always makes the streets wet. So it obviously rained last night.",
68
+ "hint": "Can wet streets prove rain?",
69
+ },
70
+ {
71
+ "kicker": "School board speech",
72
+ "quote": "If we let students retake this one exam, soon they will demand to retake every exam, and before long no grade at this school will mean anything.",
73
+ "hint": "Does one retake doom every grade?",
74
+ },
75
+ {
76
+ "kicker": "Supplement ad",
77
+ "quote": "Our formula is 100% natural, and nature knows best. That means it is completely safe for your body.",
78
+ "hint": "Does natural imply safe?",
79
+ },
80
+ {
81
+ "kicker": "Campaign rally",
82
+ "quote": "Either you support this bill or you don't care about public safety. You oppose the bill, so you don't care about public safety.",
83
+ "hint": "Are those really the only options?",
84
+ },
85
+ {
86
+ "kicker": "Toothpaste commercial",
87
+ "quote": "4 out of 5 dentists we surveyed recommend SparkleDent, and experts know best. You should switch to SparkleDent today.",
88
+ "hint": "What is the survey hiding?",
89
+ },
90
+ {
91
+ "kicker": "HR policy memo",
92
+ "quote": "Every employee who completes the security training receives a certificate. Sam completed the security training. Therefore Sam receives a certificate.",
93
+ "hint": "One of these six actually holds up.",
94
+ },
95
+ ]
96
+
97
+ SHOWCASE_INTRO = """
98
+ <div class="twil-showcase-intro">
99
+ <h2>Can TwIL spot the flaw?</h2>
100
+ <p>Six arguments from the wild. One click and TwIL extracts the premises, translates
101
+ them to first-order logic, and rules on validity. Five are broken — one holds up.</p>
102
+ </div>
103
+ """
104
+
105
+
106
+ def _card_html(case: dict) -> str:
107
+ return (
108
+ '<div class="twil-card-body">'
109
+ f'<div class="twil-card-kicker">{case["kicker"]}</div>'
110
+ f'<div class="twil-card-quote">&ldquo;{case["quote"]}&rdquo;</div>'
111
+ f'<div class="twil-card-hint">{case["hint"]}</div>'
112
+ "</div>"
113
+ )
114
+
115
+
116
+ WAITLIST_URL = "https://www.webai.com/waitlist"
117
+
118
+ HEADER = f"""
119
+ <div class="twil-header">
120
+ <img class="twil-mark" src="/gradio_api/file=brand/AppiCon.svg" alt="webAI" />
121
+ <div class="twil-header-copy">
122
+ <div class="twil-wordmark">webAI</div>
123
+ <div class="twil-product">TwIL-LM3-Pro · Intelligence Lab</div>
124
+ </div>
125
+ <a class="twil-cta twil-cta-header" href="{WAITLIST_URL}" target="_blank" rel="noopener">Get early access</a>
126
+ </div>
127
+ """
128
+
129
+ HEAD = """
130
+ <script>
131
+ document.addEventListener("keydown", (e) => {
132
+ if (e.key !== "Enter" || e.shiftKey || e.isComposing) return;
133
+ const ta = document.querySelector("#twil-input textarea");
134
+ if (!ta || e.target !== ta) return;
135
+ e.preventDefault();
136
+ e.stopPropagation();
137
+ document.querySelector("#send-btn")?.click();
138
+ }, true);
139
+ </script>
140
+ """
141
+
142
+ PLACEHOLDER = f"""
143
+ <div class="twil-empty">
144
+ <img class="twil-cube" src="/gradio_api/file=brand/webai-cube-256.webp" alt="" />
145
+ <h1>What should we work on?</h1>
146
+ <p>TwIL-LM3-Pro is webAI's formal-logic model — entailment, FOL, Lean, rule induction.</p>
147
+ <div class="twil-waitlist">
148
+ <p>TwIL and the full family of webAI models are coming to the webAI app.
149
+ Be the first to try expert intelligence on everyday devices.</p>
150
+ <a class="twil-cta" href="{WAITLIST_URL}" target="_blank" rel="noopener">Join the waitlist</a>
151
+ </div>
152
+ </div>
153
+ """
154
+
155
+ CSS = """
156
+ :root {
157
+ --weba-canvas: #f4f4f4;
158
+ --weba-fg: #161616;
159
+ --weba-muted: #737373;
160
+ --weba-secondary: #e8e8e8;
161
+ --weba-border: rgba(22, 22, 22, 0.1);
162
+ --weba-composer: #ffffff;
163
+ --weba-primary: #232323;
164
+ --weba-primary-fg: #fafafa;
165
+ }
166
+ .dark {
167
+ --weba-canvas: #232323;
168
+ --weba-fg: #fafafa;
169
+ --weba-muted: #a8a8a8;
170
+ --weba-secondary: #323232;
171
+ --weba-border: rgba(255, 255, 255, 0.1);
172
+ --weba-composer: #161616;
173
+ --weba-primary: #e8e8e8;
174
+ --weba-primary-fg: #232323;
175
+ }
176
+
177
+ * { box-shadow: none !important; text-shadow: none !important; }
178
+ html, body, #root, .gradio-container, .gradio-container > .main,
179
+ .gradio-container .contain, .fillable,
180
+ .gradio-container .column, .gradio-container .row, .contain, .wrapper {
181
+ background: var(--weba-canvas) !important;
182
+ box-shadow: none !important;
183
+ filter: none !important;
184
+ }
185
+ /* Never paint over content while a job is running: the status tracker
186
+ overlay must stay transparent or the chat looks blank mid-generation. */
187
+ .gradio-container [data-testid="status-tracker"],
188
+ .gradio-container .wrap.default {
189
+ background: transparent !important;
190
+ }
191
+ html, body, .gradio-container, .gradio-container > .main, .fillable,
192
+ .contain, .app {
193
+ height: 100% !important;
194
+ min-height: 100vh !important;
195
+ max-height: 100vh !important;
196
+ max-width: none !important;
197
+ margin: 0 !important;
198
+ overflow: hidden !important;
199
+ }
200
+ .gradio-container, .main.fillable, .contain {
201
+ display: flex !important;
202
+ flex-direction: column !important;
203
+ padding: 0 !important;
204
+ }
205
+ .gradio-container {
206
+ font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif !important;
207
+ color: var(--weba-fg) !important;
208
+ }
209
+ footer, .footer, .built-with, .settings, .settings-bar, .show-api,
210
+ .show-api-divider, .divider {
211
+ display: none !important;
212
+ }
213
+
214
+ #app-shell {
215
+ flex: 1 1 auto !important;
216
+ height: 100% !important;
217
+ max-height: 100% !important;
218
+ min-height: 0 !important;
219
+ display: flex !important;
220
+ flex-direction: column !important;
221
+ padding: 16px 24px 12px !important;
222
+ box-sizing: border-box !important;
223
+ overflow: hidden !important;
224
+ background: var(--weba-canvas) !important;
225
+ }
226
+ #app-shell > .gap, #app-shell > div {
227
+ background: transparent !important;
228
+ flex-grow: 0 !important;
229
+ height: auto !important;
230
+ min-height: 0 !important;
231
+ }
232
+ #app-shell > #twil-chat,
233
+ #app-shell > .block:has(#twil-chat),
234
+ #app-shell > div:has(#twil-chat) {
235
+ flex: 1 1 auto !important;
236
+ min-height: 0 !important;
237
+ height: auto !important;
238
+ }
239
+
240
+ .twil-header {
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 12px;
244
+ padding: 4px 0 8px;
245
+ flex-shrink: 0;
246
+ }
247
+ .twil-mark { width: 28px; height: 28px; border-radius: 6px; }
248
+ .twil-wordmark { font-weight: 600; font-size: 15px; letter-spacing: -0.02em; line-height: 1.2; color: var(--weba-fg); }
249
+ .twil-product { font-size: 12px; color: var(--weba-muted); line-height: 1.3; }
250
+
251
+ .twil-cta {
252
+ display: inline-flex;
253
+ align-items: center;
254
+ height: 34px;
255
+ padding: 0 18px;
256
+ border-radius: 999px;
257
+ background: var(--weba-primary);
258
+ color: var(--weba-primary-fg) !important;
259
+ font-size: 13px;
260
+ font-weight: 500;
261
+ text-decoration: none !important;
262
+ border: none;
263
+ white-space: nowrap;
264
+ transition: opacity 0.15s ease;
265
+ }
266
+ .twil-cta:hover { opacity: 0.85; }
267
+ .twil-cta-header {
268
+ /* Keep clear of the floating HF Space pill in the top-right corner */
269
+ margin-left: 20px;
270
+ height: 30px;
271
+ padding: 0 14px;
272
+ font-size: 12px;
273
+ }
274
+ .twil-waitlist {
275
+ margin-top: 28px;
276
+ display: flex;
277
+ flex-direction: column;
278
+ align-items: center;
279
+ gap: 12px;
280
+ }
281
+ .twil-waitlist p {
282
+ margin: 0;
283
+ max-width: 30rem;
284
+ font-size: 13px;
285
+ line-height: 1.5;
286
+ color: var(--weba-muted);
287
+ }
288
+
289
+ .twil-empty {
290
+ height: 100%;
291
+ min-height: 280px;
292
+ display: flex;
293
+ flex-direction: column;
294
+ align-items: center;
295
+ justify-content: center;
296
+ text-align: center;
297
+ padding: 12px;
298
+ }
299
+ .twil-cube { width: 96px; height: 96px; margin-bottom: 24px; }
300
+ .twil-empty h1 {
301
+ font-size: 28px;
302
+ font-weight: 500;
303
+ letter-spacing: -0.03em;
304
+ margin: 0 0 8px;
305
+ color: var(--weba-fg);
306
+ }
307
+ .twil-empty p {
308
+ margin: 0;
309
+ max-width: 28rem;
310
+ font-size: 15px;
311
+ line-height: 1.5;
312
+ color: var(--weba-muted);
313
+ }
314
+
315
+ #twil-chat, #twil-chat > .wrapper, #twil-chat .bubble-wrap,
316
+ #twil-chat .message-wrap, #twil-chat .placeholder-content {
317
+ background: transparent !important;
318
+ border: none !important;
319
+ box-shadow: none !important;
320
+ flex: 1 1 auto !important;
321
+ min-height: 0 !important;
322
+ }
323
+ #twil-chat {
324
+ max-width: 768px !important;
325
+ width: 100% !important;
326
+ margin: 0 auto !important;
327
+ flex: 1 1 auto !important;
328
+ }
329
+ #twil-chat .message.user, #twil-chat .user, #twil-chat .bubble.user {
330
+ background: var(--weba-secondary) !important;
331
+ color: var(--weba-fg) !important;
332
+ border: none !important;
333
+ box-shadow: none !important;
334
+ border-radius: 12px !important;
335
+ }
336
+ #twil-chat .message.bot, #twil-chat .bot, #twil-chat .bubble.bot {
337
+ background: transparent !important;
338
+ border: none !important;
339
+ box-shadow: none !important;
340
+ }
341
+ #twil-chat [aria-label="Delete"], #twil-chat [aria-label="Copy"],
342
+ #twil-chat [aria-label="Share"], #twil-chat .message-buttons {
343
+ display: none !important;
344
+ }
345
+ #twil-chat details, #twil-chat .thought, #twil-chat .md.thought {
346
+ background: transparent !important;
347
+ border: none !important;
348
+ box-shadow: none !important;
349
+ color: var(--weba-muted) !important;
350
+ }
351
+
352
+ #composer-row {
353
+ max-width: 768px !important;
354
+ width: 100% !important;
355
+ margin: 8px auto 0 !important;
356
+ display: flex !important;
357
+ flex-direction: row !important;
358
+ flex-wrap: nowrap !important;
359
+ align-items: center !important;
360
+ gap: 10px !important;
361
+ flex: 0 0 auto !important;
362
+ min-height: 56px !important;
363
+ background: transparent !important;
364
+ }
365
+ #composer-row > .block:has(#twil-input),
366
+ #twil-input {
367
+ flex: 1 1 0% !important;
368
+ min-width: 0 !important;
369
+ width: auto !important;
370
+ height: 52px !important;
371
+ min-height: 52px !important;
372
+ max-height: 52px !important;
373
+ background: var(--weba-composer) !important;
374
+ border: 1px solid var(--weba-border) !important;
375
+ border-radius: 999px !important;
376
+ box-shadow: none !important;
377
+ overflow: hidden !important;
378
+ }
379
+ #twil-input label,
380
+ #twil-input .input-container {
381
+ display: flex !important;
382
+ align-items: stretch !important;
383
+ width: 100% !important;
384
+ height: 100% !important;
385
+ min-height: 52px !important;
386
+ margin: 0 !important;
387
+ padding: 0 !important;
388
+ border: none !important;
389
+ background: transparent !important;
390
+ box-sizing: border-box !important;
391
+ }
392
+ #twil-input textarea,
393
+ #twil-input input,
394
+ #twil-input .scroll-hide,
395
+ #twil-input [data-testid="textbox"] {
396
+ display: block !important;
397
+ background: transparent !important;
398
+ color: var(--weba-fg) !important;
399
+ border: none !important;
400
+ box-shadow: none !important;
401
+ width: 100% !important;
402
+ height: 52px !important;
403
+ min-height: 52px !important;
404
+ max-height: 52px !important;
405
+ padding: 16px 18px !important;
406
+ font-size: 16px !important;
407
+ line-height: 20px !important;
408
+ overflow-y: auto !important;
409
+ overflow-x: hidden !important;
410
+ resize: none !important;
411
+ white-space: pre-wrap !important;
412
+ field-sizing: fixed !important;
413
+ pointer-events: auto !important;
414
+ }
415
+ #twil-input button, #twil-input .show-count, #twil-input .icon-button,
416
+ #twil-input .sr-only, #twil-input span[data-testid="block-info"] {
417
+ display: none !important;
418
+ }
419
+ #composer-row > .form,
420
+ #composer-row > .block:has(#send-btn) {
421
+ flex: 0 0 40px !important;
422
+ width: 40px !important;
423
+ min-width: 40px !important;
424
+ max-width: 40px !important;
425
+ background: transparent !important;
426
+ border: none !important;
427
+ padding: 0 !important;
428
+ box-shadow: none !important;
429
+ }
430
+ #send-btn {
431
+ flex: 0 0 40px !important;
432
+ width: 40px !important;
433
+ min-width: 40px !important;
434
+ max-width: 40px !important;
435
+ height: 40px !important;
436
+ border-radius: 999px !important;
437
+ background: var(--weba-secondary) !important;
438
+ color: var(--weba-fg) !important;
439
+ border: none !important;
440
+ box-shadow: none !important;
441
+ align-self: center !important;
442
+ padding: 0 !important;
443
+ }
444
+ #chips-row {
445
+ max-width: 768px !important;
446
+ width: 100% !important;
447
+ margin: 0 auto !important;
448
+ display: flex !important;
449
+ flex-wrap: wrap !important;
450
+ justify-content: center !important;
451
+ gap: 8px !important;
452
+ flex-shrink: 0 !important;
453
+ background: transparent !important;
454
+ padding: 4px 0 8px !important;
455
+ }
456
+ #chips-row button {
457
+ height: 32px !important;
458
+ padding: 0 14px !important;
459
+ border-radius: 999px !important;
460
+ border: 1px solid var(--weba-border) !important;
461
+ background: transparent !important;
462
+ color: var(--weba-muted) !important;
463
+ box-shadow: none !important;
464
+ font-size: 13px !important;
465
+ font-weight: 400 !important;
466
+ }
467
+ #chips-row button:hover {
468
+ background: var(--weba-secondary) !important;
469
+ color: var(--weba-fg) !important;
470
+ }
471
+
472
+ @keyframes twil-pulse {
473
+ 0%, 100% { opacity: 0.4; }
474
+ 50% { opacity: 1; }
475
+ }
476
+ #twil-chat .pending, #twil-chat [data-status="pending"] {
477
+ animation: twil-pulse 1.1s ease-in-out infinite;
478
+ }
479
+
480
+ #params-box,
481
+ #params-box.block,
482
+ #params-box .wrap,
483
+ #params-box .styler,
484
+ #params-box .gap,
485
+ #accordion-content {
486
+ max-width: 768px !important;
487
+ margin: 4px auto 0 !important;
488
+ background: transparent !important;
489
+ border: none !important;
490
+ box-shadow: none !important;
491
+ flex: 0 0 auto !important;
492
+ flex-grow: 0 !important;
493
+ height: auto !important;
494
+ min-height: 0 !important;
495
+ max-height: none !important;
496
+ overflow: visible !important;
497
+ }
498
+ #params-box .label-wrap, #params-box .icon {
499
+ color: var(--weba-muted) !important;
500
+ font-size: 12px !important;
501
+ box-shadow: none !important;
502
+ background: transparent !important;
503
+ border: none !important;
504
+ }
505
+
506
+ .twil-disclaimer {
507
+ margin: 8px 0 0;
508
+ text-align: center;
509
+ font-size: 11px;
510
+ color: var(--weba-muted);
511
+ flex-shrink: 0;
512
+ }
513
+
514
+ /* Tabs shell: keep the chat layout filling the viewport */
515
+ #twil-tabs {
516
+ flex: 1 1 auto !important;
517
+ min-height: 0 !important;
518
+ display: flex !important;
519
+ flex-direction: column !important;
520
+ background: transparent !important;
521
+ border: none !important;
522
+ box-shadow: none !important;
523
+ }
524
+ #twil-tabs > .tab-wrapper,
525
+ #twil-tabs .tab-container,
526
+ #twil-tabs .tab-nav {
527
+ flex: 0 0 auto !important;
528
+ background: transparent !important;
529
+ border: none !important;
530
+ box-shadow: none !important;
531
+ justify-content: center !important;
532
+ }
533
+ #twil-tabs .tab-container button,
534
+ #twil-tabs .tab-nav button {
535
+ background: transparent !important;
536
+ border: none !important;
537
+ border-bottom: 2px solid transparent !important;
538
+ border-radius: 0 !important;
539
+ box-shadow: none !important;
540
+ color: var(--weba-muted) !important;
541
+ font-size: 13px !important;
542
+ padding: 6px 14px !important;
543
+ }
544
+ #twil-tabs .tab-container button.selected,
545
+ #twil-tabs .tab-nav button.selected {
546
+ color: var(--weba-fg) !important;
547
+ border-bottom-color: var(--weba-fg) !important;
548
+ }
549
+ #twil-tabs .tabitem {
550
+ flex: 1 1 auto !important;
551
+ min-height: 0 !important;
552
+ background: transparent !important;
553
+ border: none !important;
554
+ box-shadow: none !important;
555
+ padding: 0 !important;
556
+ }
557
+ #tab-chat > .gap, #tab-chat > .column,
558
+ #tab-chat, #tab-chat .column:has(> div > #twil-chat) {
559
+ height: 100% !important;
560
+ min-height: 0 !important;
561
+ }
562
+ #tab-chat > .gap, #tab-chat > .column {
563
+ display: flex !important;
564
+ flex-direction: column !important;
565
+ }
566
+ #tab-chat .block:has(#twil-chat),
567
+ #tab-chat div:has(> #twil-chat) {
568
+ flex: 1 1 auto !important;
569
+ min-height: 0 !important;
570
+ height: auto !important;
571
+ }
572
+ #tab-showcase {
573
+ overflow-y: auto !important;
574
+ }
575
+
576
+ /* Showcase cards */
577
+ .twil-showcase-intro {
578
+ max-width: 768px;
579
+ margin: 8px auto 4px;
580
+ text-align: center;
581
+ }
582
+ .twil-showcase-intro h2 {
583
+ font-size: 22px;
584
+ font-weight: 500;
585
+ letter-spacing: -0.02em;
586
+ margin: 0 0 6px;
587
+ color: var(--weba-fg);
588
+ }
589
+ .twil-showcase-intro p {
590
+ margin: 0 0 8px;
591
+ font-size: 14px;
592
+ line-height: 1.5;
593
+ color: var(--weba-muted);
594
+ }
595
+ .twil-card-row {
596
+ max-width: 900px !important;
597
+ width: 100% !important;
598
+ margin: 6px auto !important;
599
+ display: flex !important;
600
+ gap: 12px !important;
601
+ align-items: stretch !important;
602
+ background: transparent !important;
603
+ }
604
+ .twil-card {
605
+ background: var(--weba-composer) !important;
606
+ border: 1px solid var(--weba-border) !important;
607
+ border-radius: 16px !important;
608
+ box-shadow: none !important;
609
+ padding: 16px !important;
610
+ gap: 10px !important;
611
+ display: flex !important;
612
+ flex-direction: column !important;
613
+ justify-content: space-between !important;
614
+ min-height: 170px !important;
615
+ }
616
+ .twil-card > * { background: transparent !important; }
617
+ .twil-card-kicker {
618
+ font-size: 11px;
619
+ font-weight: 600;
620
+ letter-spacing: 0.06em;
621
+ text-transform: uppercase;
622
+ color: var(--weba-muted);
623
+ margin-bottom: 8px;
624
+ }
625
+ .twil-card-quote {
626
+ font-size: 14px;
627
+ line-height: 1.45;
628
+ color: var(--weba-fg);
629
+ margin-bottom: 8px;
630
+ }
631
+ .twil-card-hint {
632
+ font-size: 12px;
633
+ color: var(--weba-muted);
634
+ font-style: italic;
635
+ }
636
+ .twil-card-btn {
637
+ height: 32px !important;
638
+ border-radius: 999px !important;
639
+ border: 1px solid var(--weba-border) !important;
640
+ background: var(--weba-secondary) !important;
641
+ color: var(--weba-fg) !important;
642
+ box-shadow: none !important;
643
+ font-size: 13px !important;
644
+ font-weight: 500 !important;
645
+ align-self: flex-start !important;
646
+ padding: 0 16px !important;
647
+ width: auto !important;
648
+ min-width: 0 !important;
649
+ flex: 0 0 auto !important;
650
+ }
651
+ .twil-card-btn:hover {
652
+ background: var(--weba-fg) !important;
653
+ color: var(--weba-canvas) !important;
654
+ }
655
+ """
656
+
657
+ THEME = gr.themes.Base(
658
+ primary_hue="zinc",
659
+ secondary_hue="zinc",
660
+ neutral_hue="zinc",
661
+ font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"],
662
+ radius_size=gr.themes.sizes.radius_lg,
663
+ ).set(
664
+ body_background_fill="#f4f4f4",
665
+ body_background_fill_dark="#232323",
666
+ body_text_color="#161616",
667
+ body_text_color_dark="#fafafa",
668
+ background_fill_primary="transparent",
669
+ background_fill_primary_dark="transparent",
670
+ background_fill_secondary="#e8e8e8",
671
+ background_fill_secondary_dark="#323232",
672
+ border_color_primary="rgba(22,22,22,0.1)",
673
+ border_color_primary_dark="rgba(255,255,255,0.1)",
674
+ block_background_fill="transparent",
675
+ block_background_fill_dark="transparent",
676
+ block_border_width="0px",
677
+ block_shadow="none",
678
+ block_shadow_dark="none",
679
+ shadow_drop="none",
680
+ shadow_drop_lg="none",
681
+ input_background_fill="#ffffff",
682
+ input_background_fill_dark="#161616",
683
+ input_border_color="rgba(22,22,22,0.1)",
684
+ input_border_color_dark="rgba(255,255,255,0.1)",
685
+ input_shadow="none",
686
+ button_secondary_background_fill="#e8e8e8",
687
+ button_secondary_background_fill_dark="#323232",
688
+ button_secondary_text_color="#161616",
689
+ button_secondary_text_color_dark="#fafafa",
690
+ button_secondary_shadow="none",
691
+ )
692
+
693
+ tokenizer = None
694
+ model = None
695
+ EOS_TOKEN_ID = None
696
+
697
+ if not PREVIEW:
698
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
699
+ model = AutoModelForCausalLM.from_pretrained(
700
+ MODEL_ID,
701
+ dtype=torch.bfloat16,
702
+ attn_implementation="sdpa",
703
+ ).to("cuda")
704
+ model.eval()
705
+ model.generation_config.use_cache = True
706
+ EOS_TOKEN_ID = tokenizer.eos_token_id
707
+
708
+
709
+ def _strip_thinking(text: str) -> str:
710
+ if not isinstance(text, str):
711
+ return ""
712
+ return _THINK_RE.sub("", text).strip()
713
+
714
+
715
+ def _parse_think(raw: str) -> tuple[str, str, bool]:
716
+ """Return (think, answer, think_open) from a possibly streaming reply."""
717
+ open_idx = raw.find(THINK_OPEN)
718
+ if open_idx == -1:
719
+ # A partially streamed opening tag is not an answer yet.
720
+ if raw.strip() and THINK_OPEN.startswith(raw.strip()):
721
+ return "", "", False
722
+ return "", raw, False
723
+ rest = raw[open_idx + len(THINK_OPEN) :]
724
+ close_idx = rest.find(THINK_CLOSE)
725
+ before = raw[:open_idx]
726
+ if close_idx == -1:
727
+ # Hide a partially streamed closing tag at the end of the think block.
728
+ for i in range(len(THINK_CLOSE) - 1, 0, -1):
729
+ if rest.endswith(THINK_CLOSE[:i]):
730
+ rest = rest[:-i]
731
+ break
732
+ return rest.strip(), before.strip(), True
733
+ think = rest[:close_idx].strip()
734
+ answer = (before + rest[close_idx + len(THINK_CLOSE) :]).strip()
735
+ return think, answer, False
736
+
737
+
738
+ def _gpu_seconds(history, max_new_tokens=2048, *args, **kwargs):
739
+ # Scale the ZeroGPU reservation with the requested output budget. The
740
+ # platform caps a single reservation at five minutes.
741
+ tokens = int(max_new_tokens or 2048)
742
+ return min(300, max(30, 20 + tokens // 50))
743
+
744
+
745
+ def _history_for_model(history: list) -> list[dict]:
746
+ conversation = [{"role": "system", "content": SYSTEM_PROMPT}]
747
+ for msg in history or []:
748
+ role = msg.get("role", "user")
749
+ meta = msg.get("metadata") or {}
750
+ # Only skip thought/tool bubbles; Gradio attaches an empty metadata
751
+ # dict to ordinary messages, so a bare truthiness check drops them all.
752
+ is_thought = isinstance(meta, dict) and bool(meta.get("title"))
753
+ if role == "system" or is_thought:
754
+ continue
755
+ content = msg.get("content", "")
756
+ if isinstance(content, list):
757
+ # Gradio 6 sends message content as a list of blocks.
758
+ parts = []
759
+ for block in content:
760
+ if isinstance(block, str):
761
+ parts.append(block)
762
+ elif isinstance(block, dict):
763
+ text = block.get("text") or block.get("content") or ""
764
+ if isinstance(text, str) and text:
765
+ parts.append(text)
766
+ content = "\n".join(parts)
767
+ if role == "assistant":
768
+ content = _strip_thinking(content)
769
+ if content:
770
+ conversation.append({"role": role, "content": content})
771
+ return conversation
772
+
773
+
774
+ def _stream_tokens(conversation: list[dict], max_new_tokens: int, temperature: float, top_p: float, enable_thinking: bool) -> Iterator[str]:
775
+ if PREVIEW:
776
+ import time
777
+
778
+ demo = (
779
+ f"{THINK_OPEN}\n"
780
+ "Check the premises, then the conclusion. Premise 1 gives Rain -> Wet. "
781
+ "Premise 2 observes Wet. Inferring Rain from Wet affirms the consequent, "
782
+ "which is invalid. The argument is a classic Barbara syllogism otherwise.\n"
783
+ f"{THINK_CLOSE}\n\n"
784
+ "Answer: entailment."
785
+ )
786
+ acc = ""
787
+ for ch in demo:
788
+ acc += ch
789
+ time.sleep(0.02)
790
+ yield acc
791
+ return
792
+
793
+ encoded = tokenizer.apply_chat_template(
794
+ conversation,
795
+ add_generation_prompt=True,
796
+ return_tensors="pt",
797
+ return_dict=True,
798
+ enable_thinking=enable_thinking,
799
+ )
800
+ input_ids = encoded["input_ids"]
801
+ attention_mask = encoded["attention_mask"]
802
+ if input_ids.shape[1] > MAX_INPUT_TOKEN_LENGTH:
803
+ input_ids = input_ids[:, -MAX_INPUT_TOKEN_LENGTH:]
804
+ attention_mask = attention_mask[:, -MAX_INPUT_TOKEN_LENGTH:]
805
+ gr.Warning(f"Trimmed the conversation to the last {MAX_INPUT_TOKEN_LENGTH} tokens.")
806
+
807
+ input_ids = input_ids.to(model.device)
808
+ attention_mask = attention_mask.to(model.device)
809
+ streamer = TextIteratorStreamer(
810
+ tokenizer, timeout=30.0, skip_prompt=True, skip_special_tokens=True
811
+ )
812
+ generate_kwargs = dict(
813
+ input_ids=input_ids,
814
+ attention_mask=attention_mask,
815
+ streamer=streamer,
816
+ max_new_tokens=int(max_new_tokens),
817
+ num_beams=1,
818
+ use_cache=True,
819
+ eos_token_id=EOS_TOKEN_ID,
820
+ pad_token_id=tokenizer.pad_token_id or EOS_TOKEN_ID,
821
+ )
822
+ if temperature == 0:
823
+ generate_kwargs["do_sample"] = False
824
+ else:
825
+ generate_kwargs["do_sample"] = True
826
+ generate_kwargs["temperature"] = float(temperature)
827
+ generate_kwargs["top_p"] = float(top_p)
828
+
829
+ Thread(target=model.generate, kwargs=generate_kwargs, daemon=True).start()
830
+ acc = ""
831
+ for text in streamer:
832
+ acc += text
833
+ yield acc
834
+
835
+
836
+ def queue_message(message, history):
837
+ """Paint the user turn immediately so the GPU wait is not a blank screen."""
838
+ message = (message or "").strip()
839
+ if not message:
840
+ return "", history, gr.skip()
841
+ history = [dict(m) if isinstance(m, dict) else m for m in (history or [])]
842
+ history.append({"role": "user", "content": message})
843
+ history.append(
844
+ {
845
+ "role": "assistant",
846
+ "content": "",
847
+ "metadata": {"title": "Logicizing…", "status": "pending"},
848
+ }
849
+ )
850
+ return "", history, gr.update(visible=False)
851
+
852
+
853
+ @spaces.GPU(duration=_gpu_seconds)
854
+ def generate_reply(history, max_new_tokens, temperature, top_p, enable_thinking):
855
+ history = [dict(m) if isinstance(m, dict) else m for m in (history or [])]
856
+ if not any(m.get("role") == "user" for m in history):
857
+ yield history
858
+ return
859
+ conversation = _history_for_model(history)
860
+
861
+ # Stream only into a bubble at the END of the history. Reaching for any
862
+ # older assistant message would overwrite a previous turn in place.
863
+ think_msg = None
864
+ last = history[-1] if history else None
865
+ if (
866
+ last is not None
867
+ and last.get("role") == "assistant"
868
+ and isinstance(last.get("metadata"), dict)
869
+ and last["metadata"].get("status") == "pending"
870
+ ):
871
+ think_msg = last
872
+ think_msg["content"] = ""
873
+ elif last is not None and last.get("role") == "user":
874
+ # Gradio strips the empty pending bubble in preprocessing; recreate it.
875
+ think_msg = {"role": "assistant", "content": ""}
876
+ history.append(think_msg)
877
+ else:
878
+ # No new user turn (e.g. an empty submit); don't regenerate old answers.
879
+ yield history
880
+ return
881
+ think_msg["metadata"] = {"title": "Logicizing…", "status": "pending"}
882
+ yield history
883
+
884
+ answer_msg = {"role": "assistant", "content": ""}
885
+
886
+ for raw in _stream_tokens(
887
+ conversation, max_new_tokens, temperature, top_p, enable_thinking
888
+ ):
889
+ think, answer, think_open = _parse_think(raw)
890
+ if think or think_open:
891
+ think_msg["content"] = think
892
+ think_msg["metadata"] = {
893
+ "title": "Logicizing…" if think_open else "Logic",
894
+ "status": "pending" if think_open else "done",
895
+ }
896
+ if answer:
897
+ answer_msg["content"] = answer
898
+ if not any(m is answer_msg for m in history):
899
+ history.append(answer_msg)
900
+ elif not think_open and not think and raw.strip():
901
+ answer_msg["content"] = raw.strip()
902
+ if not any(m is answer_msg for m in history):
903
+ history.append(answer_msg)
904
+ elif any(m is answer_msg for m in history):
905
+ # A partial tag was mistaken for an answer earlier; retract it.
906
+ history.remove(answer_msg)
907
+ answer_msg["content"] = ""
908
+ yield history
909
+
910
+ if think_msg["content"]:
911
+ think_msg["metadata"] = {"title": "Logic", "status": "done"}
912
+ yield history
913
+ elif think_msg in history and not think_msg["content"]:
914
+ history.remove(think_msg)
915
+ yield history
916
+
917
+
918
+ with gr.Blocks(fill_height=True, fill_width=True, elem_id="app-root") as demo:
919
+ with gr.Column(elem_id="app-shell"):
920
+ gr.HTML(HEADER)
921
+ with gr.Tabs(elem_id="twil-tabs") as tabs:
922
+ with gr.Tab("Chat", id="chat", elem_id="tab-chat"):
923
+ chatbot = gr.Chatbot(
924
+ value=[],
925
+ placeholder=PLACEHOLDER,
926
+ label="",
927
+ show_label=False,
928
+ layout="bubble",
929
+ avatar_images=(None, str(AVATAR) if AVATAR.exists() else None),
930
+ allow_tags=["think"],
931
+ reasoning_tags=[(THINK_OPEN, THINK_CLOSE)],
932
+ buttons=None,
933
+ editable=False,
934
+ line_breaks=False,
935
+ elem_id="twil-chat",
936
+ scale=1,
937
+ height="100%",
938
+ )
939
+ with gr.Row(elem_id="chips-row") as chips_row:
940
+ chip_btns = [
941
+ gr.Button(label, variant="secondary", size="sm", elem_classes=["twil-chip"])
942
+ for label, _ in CHIPS
943
+ ]
944
+ with gr.Row(elem_id="composer-row"):
945
+ prompt = gr.Textbox(
946
+ placeholder="Ask webAI anything…",
947
+ show_label=False,
948
+ container=False,
949
+ lines=2,
950
+ max_lines=2,
951
+ elem_id="twil-input",
952
+ scale=8,
953
+ )
954
+ send = gr.Button("↑", elem_id="send-btn", scale=0)
955
+ with gr.Accordion("Parameters", open=False, elem_id="params-box"):
956
+ max_new_tokens = gr.Slider(256, 16384, value=2048, step=256, label="Max new tokens")
957
+ temperature = gr.Slider(0, 1.5, value=0, step=0.05, label="Temperature (0 = greedy)")
958
+ top_p = gr.Slider(0.1, 1.0, value=0.95, step=0.05, label="Top-p")
959
+ enable_thinking = gr.Checkbox(value=True, label="Enable thinking")
960
+ gr.HTML('<p class="twil-disclaimer">This is AI and it can make mistakes</p>')
961
+ with gr.Tab("Showcase", id="showcase", elem_id="tab-showcase"):
962
+ gr.HTML(SHOWCASE_INTRO)
963
+ card_btns = []
964
+ for start in range(0, len(SHOWCASE), 3):
965
+ with gr.Row(elem_classes=["twil-card-row"]):
966
+ for case in SHOWCASE[start : start + 3]:
967
+ with gr.Column(elem_classes=["twil-card"], scale=1, min_width=220):
968
+ gr.HTML(_card_html(case))
969
+ card_btns.append(
970
+ gr.Button("Analyze it", size="sm", elem_classes=["twil-card-btn"])
971
+ )
972
+
973
+ # Lock the composer while a turn is running: overlapping events make the
974
+ # finishing stream overwrite the chat with its own stale history.
975
+ lockable = [prompt, send, *card_btns]
976
+
977
+ def _lock():
978
+ return [gr.update(interactive=False)] * len(lockable)
979
+
980
+ def _unlock():
981
+ return [gr.update(interactive=True)] * len(lockable)
982
+
983
+ def _wire(event, prep_fn=None, prep_outputs=None):
984
+ chain = event(_lock, None, lockable, show_progress="hidden")
985
+ if prep_fn is not None:
986
+ chain = chain.then(prep_fn, outputs=prep_outputs, show_progress="hidden")
987
+ chain = chain.then(
988
+ queue_message,
989
+ [prompt, chatbot],
990
+ [prompt, chatbot, chips_row],
991
+ show_progress="hidden",
992
+ ).then(
993
+ generate_reply,
994
+ [chatbot, max_new_tokens, temperature, top_p, enable_thinking],
995
+ chatbot,
996
+ concurrency_limit=1,
997
+ show_progress="hidden",
998
+ )
999
+ chain.then(_unlock, None, lockable, show_progress="hidden")
1000
+
1001
+ _wire(send.click)
1002
+ _wire(prompt.submit)
1003
+ for btn, (_, template) in zip(chip_btns, CHIPS):
1004
+ btn.click(lambda t=template: t, outputs=prompt)
1005
+
1006
+ for btn, case in zip(card_btns, SHOWCASE):
1007
+ case_prompt = SHOWCASE_TEMPLATE.format(argument=case["quote"])
1008
+ _wire(
1009
+ btn.click,
1010
+ prep_fn=lambda p=case_prompt: (p, gr.Tabs(selected="chat")),
1011
+ prep_outputs=[prompt, tabs],
1012
+ )
1013
+
1014
+ if __name__ == "__main__":
1015
+ demo.launch(
1016
+ theme=THEME,
1017
+ css=CSS,
1018
+ head=HEAD,
1019
+ allowed_paths=[str(ASSETS), str(ASSETS.parent)],
1020
+ ssr_mode=False,
1021
+ show_error=True,
1022
+ )
brand/AppiCon.svg ADDED
brand/avatar.svg ADDED
brand/webai-cube-256.webp ADDED

Git LFS Details

  • SHA256: d4cc93f2f9f45d06eabc845734c3ffd182eb4e58f2169b6605cf02e06e3a57e4
  • Pointer size: 131 Bytes
  • Size of remote file: 850 kB
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ transformers>=5.0.0
2
+ accelerate