File size: 21,848 Bytes
ad51766
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
import base64
import json
import re
from pathlib import Path

import gradio as gr

from chat import (
    HY_CHAT_INITIAL_HTML,
    api_chat,
    init_state,
    new_chat,
    send_message,
    submit_tool_result,
)
from i18n import LANG, t
from tools import validate_functions_json
from styles import CSS

_STATIC_DIR = Path(__file__).parent / "static"
_VENDOR_DIR = _STATIC_DIR / "vendor"

_FN_PLACEHOLDER = json.dumps([{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get weather info for a city",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {"type": "string", "description": "City name"}
            },
            "required": ["location"]
        }
    }
}], indent=2, ensure_ascii=False)

_EXTRACT_NAMES_PROMPT = """You are a text information extraction expert with exceptional abilities in information filtering and structured processing. Your core task is to accurately capture key information from the raw text provided by the user and output the result according to the required format, ensuring informational purity and logical consistency.

Initial settings:
1. All extracted information must strictly come from the text provided by the user.
2. If the user's request requires extracting uncertainty-related words such as "possible" or "speculative," you must extract them as requested. Otherwise, you must not output uncertainty-related words such as "possible" or "speculative."
3. You must strictly follow the output format below and must not include any content outside the required format. The output format is: "Extracted content: xxx". Here, "xxx" is a placeholder.

Please help me extract all personal names from the text in order. Note that you do not need to remove duplicates.

Text:
In a warm and harmonious extended family, four elders—father, mother, grandfather, and grandmother—have always quietly watched over the family, supporting the whole household with love and tolerance. Meanwhile, six younger family members—Emily Carter, Olivia Brooks, Ethan Miller, Sophie Taylor, Grace Wilson, and Daniel Harris—are each growing along their own paths in life. The family supports one another and keeps each other company, making ordinary days feel warm and fulfilling. Father is hardworking and steady, carrying the family's responsibilities with strength. Mother is gentle and attentive, taking care of every detail of daily life. Grandfather is experienced and often tells the children stories from the past. Grandmother is kind and loving, warming everyone who returns home with hot meals. Emily Carter is sensible and motivated, setting a good example for her younger siblings. Olivia Brooks is lively and cheerful, always bringing laughter to the home. Ethan Miller is dependable and practical, taking every task seriously without being careless. Sophie Taylor is gentle and considerate, skilled at caring for the people around her. Grace Wilson loves life and is attentive to the beauty around her. Daniel Harris is energetic and full of expectations for the future. These ten family members each have their own traits and strengths, yet they share the same care and affection for one another. Together, they share joy, ease worries, pass through spring, summer, autumn, and winter, and move through the years side by side. Through family love, they gather the strongest support, making home the safest and warmest harbor, and making ordinary life full of happiness and light because of each other's companionship."""

_CHONGQING_TRIP_PROMPT = """You are an experienced local tour guide in Chongqing. Based on the attraction information and the users' situation, you need to generate a travel summary arranged by travel date. You must not output anything else. The travel summary format is as follows:

Date/Location/Ticket
MM-dd/Travel location/Ticket price
MM-dd/Travel location/Ticket price
......

For the Travel location field, choose exactly one from the following six options: Hongya Cave, Chaotianmen, Jiefangbei, Qiansimen, Shapingba, Jiangbeizui.

Opening hours:
Hongya Cave: 9:00–11:00
Chaotianmen: 9:00–11:00
Jiefangbei: 15:00–17:00
Qiansimen: 11:00–13:00
Shapingba: 12:00–14:00
Jiangbeizui: 16:00–18:00

Ticket prices:
Hongya Cave: CNY 20 per person
Chaotianmen: CNY 60 per person
Jiefangbei: CNY 30 per person
Qiansimen: CNY 50 per person
Shapingba: CNY 60 per person
Jiangbeizui: CNY 50 per person

Alex: My friend Ben and I came to Chongqing on March 15 for a trip. For every attraction we visit, we must stay from opening time until closing time. We brought a total of CNY 300. On the first day, we visited two attractions.

Ben: Although we had a great time on the first day, after the day's trip ended, I realized that I had lost CNY 200. So we did not plan to visit any attractions on the second day.

Alex: Losing the money was really stressful for us. However, after 12:00 on the second day, our parents sent us another CNY 200, so we went to visit one attraction that afternoon.

Ben: We were too tired from the first two days, so on the final day we visited only one attraction. None of the attractions we visited were repeated."""

_RATIONAL_VARIETY_PROMPT = r"""Let \(K\) be an algebraically closed field and \(X\) the hypersurface in \(\mathbf{P}_{K}^{3}\) defined by the homogeneous equation     \[     x^{2} w-y^{2} z=0     \]     where \((x, y, z, w)\) are homogeneous coordinates on \(\mathbf{P}_{K}^{3}\). Prove that \(X\) is a rational variety, i.e., its function field is \(K\left(t_{1}, t_{2}\right)\), where \(t_{1}, t_{2}\) are algebraically independent over \(K\)."""

_FISH_PUZZLE_PROMPT = """Three houses stand side by side, numbered 1–3 from left to right. Each house has:

A different color: red, green, blue
A resident of a different nationality: British, French, Japanese
A different pet: dog, cat, fish

Clues:
The British resident lives in the red house.
The French resident owns the dog.
The green house is immediately to the left of the red house.
The Japanese resident lives in House 1.
The cat is kept in the blue house.

Question:
Who owns the fish? 🐟"""

# The 4 showcase prompts are shared across languages — the full text is
# kept verbatim (that's what we send to the model) while the frontend
# truncates the button label for display; see ``createOverlay`` in
# static/app.js for the label-truncation side.
_SHOWCASE_PROMPTS = [
    _EXTRACT_NAMES_PROMPT,
    _CHONGQING_TRIP_PROMPT,
    _RATIONAL_VARIETY_PROMPT,
    _FISH_PUZZLE_PROMPT,
]

_EXAMPLES_BY_LANG = {
    "en": _SHOWCASE_PROMPTS,
    "zh": _SHOWCASE_PROMPTS,
}
EXAMPLES = _EXAMPLES_BY_LANG.get(LANG, _EXAMPLES_BY_LANG["en"])

# ─── Vendored asset inlining ──────────────────────────────────────────
# Every vendored library is INLINED into the ``js`` / ``css`` parameters
# that ``gr.Blocks`` already serves on the page, so the app runs fully
# self-contained without any external CDN or extra static-route plumbing.
#
# Order matters for JS: marked / katex / auto-render / hljs must be
# defined as globals BEFORE static/chat.js runs; static/chat.js must
# define ``window.HY_CHAT`` BEFORE static/app.js's ``bootstrap()``
# polls for ``#hy-chat``. Concatenation order below preserves this.
def _read(p: Path) -> str:
    return p.read_text(encoding="utf-8")


_VENDOR_JS = "\n;\n".join([
    _read(_VENDOR_DIR / "marked" / "marked.min.js"),
    _read(_VENDOR_DIR / "katex" / "katex.min.js"),
    _read(_VENDOR_DIR / "katex" / "auto-render.min.js"),
    _read(_VENDOR_DIR / "highlight" / "highlight.min.js"),
])

_CHAT_JS = _read(_STATIC_DIR / "chat.js")
_APP_JS = _read(_STATIC_DIR / "app.js")

# KaTeX's stylesheet references font files via ``url(fonts/KaTeX_*.woff2)``
# — a path that only resolves if the CSS itself is served from the same
# directory as the fonts. Since we inline the CSS into a <style> tag
# the relative URLs would resolve against the document root and 404. We
# rewrite each font URL into a ``data:`` URI so the CSS is fully
# self-contained, and strip the ``.woff`` / ``.ttf`` fallback entries
# from each ``@font-face`` src list so the browser doesn't fire requests
# we know will 404 (we only ship the ``.woff2`` files).
_KATEX_CSS = _read(_VENDOR_DIR / "katex" / "katex.min.css")
_FONTS_DIR = _VENDOR_DIR / "katex" / "fonts"

# Drop ``,url(fonts/X.woff) format("woff")`` and the ttf equivalent. The
# minified CSS may use single OR double quotes around the format string;
# match both. Run BEFORE inlining woff2 so the regex still sees the
# ``url(fonts/...)`` token shape.
_KATEX_CSS = re.sub(
    r",\s*url\(fonts/[^)]+\.(?:woff|ttf)\)\s*format\(['\"](?:woff|truetype)['\"]\)",
    "",
    _KATEX_CSS,
)

# Inline every woff2 as a data URI.
for _font_path in sorted(_FONTS_DIR.glob("*.woff2")):
    _b64 = base64.b64encode(_font_path.read_bytes()).decode("ascii")
    _data_uri = f"data:font/woff2;base64,{_b64}"
    _KATEX_CSS = _KATEX_CSS.replace(
        f"url(fonts/{_font_path.name})", f"url({_data_uri})"
    )

_HLJS_CSS = _read(_VENDOR_DIR / "highlight" / "github.min.css")
_HLJS_DARK_CSS = _read(_VENDOR_DIR / "highlight" / "github-dark.min.css")

# Gradio passes this string as the body of an init function it calls on
# load. We bundle vendored libs first so they install their globals
# (``window.marked``, ``window.katex``, ``window.renderMathInElement``,
# ``window.hljs``), then chat.js (defines ``window.HY_CHAT`` and starts
# the delta-channel observer), then app.js (the rest of the UX glue).
_JS_INIT = f"""
window.HY_EXAMPLES = {json.dumps(EXAMPLES)};
window.HY_I18N = {json.dumps({
    "examples_heading": t("examples_heading"),
    "model_busy": t("warn.model_busy"),
    "thinking": t("display.thinking"),
    "thinking_done": t("display.thinking_done"),
    "code_copy": t("display.code_copy"),
    "code_copied": t("display.code_copied"),
})};
{_VENDOR_JS}
;
{_CHAT_JS}
;
{_APP_JS}
"""

# Append vendored CSS to whatever ``styles.py`` produced. The KaTeX
# theme has higher specificity for math elements than our other rules,
# so loading order doesn't matter for correctness; we just always
# append it last for consistency.
_FULL_CSS = (
    CSS
    + "\n/* === katex.min.css (inlined, fonts as data URIs) === */\n"
    + _KATEX_CSS
    + "\n/* === highlight.js github theme === */\n"
    + _HLJS_CSS
    + "\n/* === highlight.js github-dark theme (active under .dark) === */\n"
    # Scope the dark theme to ``.dark`` so the light theme above is the
    # default and the dark variant only kicks in when Gradio toggles its
    # top-level ``.dark`` class. ``github-dark.min.css`` references
    # ``.hljs`` (and many ``.hljs-...`` variants) at lots of positions:
    # at the start of a rule, right after ``}``, AND comma-separated
    # like ``.hljs-doctag,.hljs-keyword,...`` where only the first
    # selector is preceded by ``}``. A simple global string replace
    # works because ``.hljs`` only appears inside selectors in this
    # file (never in property values or strings).
    + _HLJS_DARK_CSS.replace(".hljs", ".dark .hljs")
)



with gr.Blocks(title=t("title"), fill_width=True, js=_JS_INIT) as demo:
    state = gr.State(init_state)

    with gr.Sidebar(width=420, open=True):
        gr.HTML(
            f"<h1 style='text-align:center;font-size:2rem;margin:0.2em 0 0.2em'>{t('title')}</h1>"
            f"<p class='title-notice' style='text-align:center;font-size:0.88rem;"
            f"line-height:1.4;color:var(--body-text-color,#444);"
            f"margin:0 0 0.8em;padding:0 0.4em'>{t('title.notice_html')}</p>"
        )
        think_level = gr.Dropdown(
            choices=["no_think", "low", "high"],
            value="high",
            label=t("sidebar.think_level"),
            info=t("sidebar.think_level.info"),
        )
        system_prompt = gr.Textbox(
            value="",
            label=t("sidebar.system_prompt"),
            lines=3,
            placeholder=t("sidebar.system_prompt.placeholder"),
        )
        temperature = gr.Slider(
            minimum=0, maximum=1, step=0.01, value=0.9,
            label=t("sidebar.temperature"),
            info=t("sidebar.temperature.info"),
            interactive=False,
        )
        temperature_use_default = gr.Checkbox(
            value=True,
            label=t("sidebar.temperature.use_default"),
        )
        # Toggle the slider's interactive state to match the checkbox so the
        # disabled visual cue matches the request semantics: when "Use model
        # default" is checked, temperature is omitted from the API request
        # entirely (see ``build_api_kwargs``); when unchecked, the slider
        # value is sent literally — including 0, which means greedy decoding.
        temperature_use_default.change(
            fn=lambda checked: gr.update(interactive=not checked),
            inputs=[temperature_use_default],
            outputs=[temperature],
            api_visibility="private",
            show_progress="hidden",
            queue=False,
        )
        max_tokens = gr.Slider(
            minimum=0, maximum=65536, step=1, value=0,
            label=t("sidebar.max_tokens"),
            info=t("sidebar.max_tokens.info"),
        )
        top_p = gr.Slider(
            minimum=0, maximum=1, step=0.01, value=0,
            label=t("sidebar.top_p"),
            info=t("sidebar.top_p.info"),
        )
        preserved_thinking = gr.Checkbox(
            value=False,
            label=t("sidebar.preserved_thinking"),
            info=t("sidebar.preserved_thinking.info"),
            interactive=False,
        )
        preserved_thinking_use_default = gr.Checkbox(
            value=True,
            label=t("sidebar.preserved_thinking.use_default"),
            info=t("sidebar.preserved_thinking.use_default.info"),
        )
        # Mirror the temperature "Use model default" wiring: when checked,
        # ``preserved_thinking`` is omitted from the API request entirely
        # (see ``build_api_kwargs``) and the toggle is disabled; when
        # unchecked, the toggle becomes interactive and its boolean value is
        # sent literally (defaulting to False / off).
        preserved_thinking_use_default.change(
            fn=lambda checked: gr.update(interactive=not checked),
            inputs=[preserved_thinking_use_default],
            outputs=[preserved_thinking],
            api_visibility="private",
            show_progress="hidden",
            queue=False,
        )
        with gr.Accordion(t("sidebar.functions"), open=False):
            functions_json = gr.Textbox(
                value="",
                label=t("sidebar.functions.label"),
                lines=15,
                max_lines=30,
                placeholder=_FN_PLACEHOLDER,
            )
            validate_fn_btn = gr.Button(t("sidebar.validate_btn"), size="sm")

    # ── Custom chat surface ───────────────────────────────────────────
    # The visible chat lives entirely inside <div id="hy-chat">, owned and
    # mutated exclusively by static/chat.js. Server NEVER updates this
    # component's value — it's set once at component creation and the
    # renderer takes over from there.
    chat_host = gr.HTML(
        value=HY_CHAT_INITIAL_HTML,
        elem_id="hy-chat-host",
    )

    # Hidden delta channel. Server pushes JSON ops payloads here; the
    # client observes mutations on this element and applies each op to
    # the chat container. Visually hidden via _hide.css (see the
    # #hy-chat-delta rule there).
    #
    # Why a separate component (not a side-effect of chat_host updates):
    # any update to chat_host's value would wipe the entire DOM the
    # renderer has built. The delta channel is the safe back-door — its
    # value can change freely on every yield without ever touching the
    # visible chat surface.
    chat_delta = gr.HTML(value="", elem_id="hy-chat-delta")

    with gr.Column(visible=False, elem_id="tool-area") as tool_area:
        tool_call_info = gr.Markdown("")
        tool_result_input = gr.Textbox(
            label=t("tool.result_label"),
            placeholder=t("tool.result_placeholder"),
            lines=1,
        )
        tool_submit_btn = gr.Button(t("tool.submit"), variant="primary", size="sm")

    with gr.Row(elem_classes=["msg-row"]):
        msg_input = gr.Textbox(
            placeholder=t("msg_placeholder"),
            show_label=False,
            lines=1,
            scale=8,
            elem_classes=["msg-input"],
        )
        send_btn = gr.Button(
            "➤", variant="primary",
            scale=0, min_width=42,
            elem_id="send-btn", elem_classes=["send-btn"],
        )
        new_chat_btn = gr.Button(
            "+", variant="secondary",
            scale=0, min_width=42,
            elem_classes=["new-chat-btn"],
        )

    # Hidden busy-marker component. Driven by the streaming generators in
    # chat.py:
    #   * Streaming begins → value = HY_BUSY_HTML.
    #   * Streaming ends   → value = HY_IDLE_HTML.
    #
    # The browser observes this single element and gates the Send button
    # on it. Critically isolated from any chat content — unclosed
    # <style>/<script>/<textarea> in model output cannot hijack it
    # because it's a separate Gradio component.
    busy_marker = gr.HTML("", elem_id="hy-busy-marker")

    # ── Event wiring ──────────────────────────────────────────────────────
    # IMPORTANT: the Send button's busy state is OWNED BY THE BROWSER, not
    # by Gradio. We never call ``gr.update(interactive=False/True)`` on it
    # from any handler. Instead the streaming generators drive busy_marker;
    # static/app.js gates Send on its content (see file).
    #
    # ``concurrency_limit`` is SERVER-WIDE: up to N chat requests across
    # ALL connected users can run in parallel. Per-session ``gr.State``
    # isolates each user's conversation.
    send_outputs = [
        chat_delta, state, msg_input,
        tool_area, tool_call_info, tool_result_input,
        busy_marker,
    ]
    send_inputs = [
        msg_input, state,
        system_prompt, think_level,
        temperature, temperature_use_default,
        max_tokens, top_p,
        preserved_thinking, preserved_thinking_use_default,
        functions_json,
    ]

    chat_concurrency = {"concurrency_id": "chat", "concurrency_limit": 64}

    for trigger in (msg_input.submit, send_btn.click):
        trigger(
            fn=send_message,
            inputs=send_inputs,
            outputs=send_outputs,
            show_progress="hidden",
            # UI-only handler: ships chat content as DOM-mutation deltas
            # through ``chat_delta``, which is unusable from gradio_client.
            # The headless ``/chat`` endpoint registered below is the
            # supported programmatic surface.
            api_visibility="private",
            **chat_concurrency,
        )

    new_chat_btn.click(
        fn=new_chat,
        inputs=[state],
        # ``busy_marker`` is in the output set so ``new_chat`` can release
        # the Send-button lock immediately when it cancels an in-flight
        # stream — without it the cancelled generator never reaches its
        # terminal IDLE yield and the button would stay stuck until the
        # client-side soft watchdog force-clears it.
        outputs=[chat_delta, state, busy_marker],
        show_progress="hidden",
        api_visibility="private",
    )

    validate_fn_btn.click(
        fn=validate_functions_json,
        inputs=[functions_json],
        outputs=[functions_json],
        show_progress="hidden",
        api_visibility="private",
    )

    tool_submit_inputs = [
        tool_result_input, state,
        system_prompt, think_level,
        temperature, temperature_use_default,
        max_tokens, top_p,
        preserved_thinking, preserved_thinking_use_default,
        functions_json,
    ]
    tool_submit_outputs = [
        chat_delta, state,
        tool_area, tool_call_info, tool_result_input,
        busy_marker,
    ]

    for trigger in (tool_submit_btn.click, tool_result_input.submit):
        trigger(
            fn=submit_tool_result,
            inputs=tool_submit_inputs,
            outputs=tool_submit_outputs,
            show_progress="hidden",
            api_visibility="private",  # UI-only; see send_message wiring above.
            **chat_concurrency,
        )

    # ── Programmatic API endpoint ────────────────────────────────────────
    # Registered with ``gr.api`` so it has NO UI footprint — it derives its
    # input/output schema from ``api_chat``'s type hints rather than from
    # Gradio components. Callers reach it via:
    #
    #     from gradio_client import Client
    #     client = Client("ericsqin/hy-demo")
    #     content, reasoning, tool_calls, history = client.predict(
    #         message="Hello!",
    #         api_name="/chat",
    #     )
    gr.api(
        api_chat,
        api_name="chat",
        concurrency_id="chat",
        concurrency_limit=64,
    )


if __name__ == "__main__":
    # Guarded so that ``import app`` (e.g. from tests or docs tooling)
    # does not start a Gradio server as a side effect. HuggingFace Spaces
    # invokes ``python app.py`` so this entry point still runs there.
    demo.queue(default_concurrency_limit=64, max_size=128).launch(
        css=_FULL_CSS,
        ssr_mode=False,
        show_error=True,
    )