Spaces:
Sleeping
Sleeping
st4ck commited on
Commit Β·
1346a5c
1
Parent(s): 2940c12
Rewrite app.py with JS-based file upload, Gradio 5.12.0
Browse files- README.md +1 -2
- app.py +256 -93
- requirements.txt +2 -1
README.md
CHANGED
|
@@ -4,8 +4,7 @@ emoji: ποΈ
|
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: "
|
| 8 |
-
python_version: "3.11"
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
| 11 |
---
|
|
|
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: "5.12.0"
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
app.py
CHANGED
|
@@ -1,124 +1,287 @@
|
|
| 1 |
-
|
| 2 |
-
|
|
|
|
| 3 |
import os
|
|
|
|
| 4 |
import tempfile
|
| 5 |
-
import
|
|
|
|
| 6 |
|
| 7 |
# ---------------------------------------------------------------------------
|
| 8 |
# Build the mdc binary once at startup
|
| 9 |
# ---------------------------------------------------------------------------
|
| 10 |
|
| 11 |
-
BINARY = os.path.join(os.path.dirname(__file__), "mdc")
|
|
|
|
| 12 |
|
| 13 |
def _build():
|
| 14 |
-
src = os.path.join(os.path.dirname(__file__), "mdc.c")
|
| 15 |
-
result = subprocess.run(
|
| 16 |
-
|
| 17 |
-
capture_output=True, text=True
|
| 18 |
-
)
|
| 19 |
if result.returncode != 0:
|
| 20 |
-
raise RuntimeError(f"
|
|
|
|
| 21 |
|
| 22 |
if not os.path.isfile(BINARY):
|
| 23 |
_build()
|
| 24 |
|
|
|
|
| 25 |
# ---------------------------------------------------------------------------
|
| 26 |
-
#
|
| 27 |
# ---------------------------------------------------------------------------
|
| 28 |
|
| 29 |
-
def
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
-
def compress(input_file):
|
| 35 |
-
if input_file is None:
|
| 36 |
-
raise gr.Error("Please upload a file.")
|
| 37 |
-
workdir = tempfile.mkdtemp()
|
| 38 |
try:
|
| 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 |
try:
|
| 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 |
with gr.Blocks(title="Midicoth β Micro-Diffusion Compression") as demo:
|
| 93 |
-
gr.
|
| 94 |
-
"""
|
| 95 |
-
# Midicoth β Micro-Diffusion Compression
|
| 96 |
-
Lossless text compressor using **Binary Tree Tweedie Denoising**.
|
| 97 |
-
Outperforms xz, zstd, Brotli, and bzip2 on text data. No neural network, no GPU.
|
| 98 |
-
|
| 99 |
-
> **Note:** Files are processed in memory and deleted after the session.
|
| 100 |
-
> Large files (>10 MB) may be slow on free-tier hardware.
|
| 101 |
-
"""
|
| 102 |
-
)
|
| 103 |
|
|
|
|
| 104 |
with gr.Tab("Compress"):
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
|
|
|
| 114 |
with gr.Tab("Decompress"):
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
|
|
|
| 124 |
demo.launch()
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Midicoth -- Micro-Diffusion Compression -- Hugging Face Space Demo."""
|
| 3 |
+
|
| 4 |
import os
|
| 5 |
+
import base64
|
| 6 |
import tempfile
|
| 7 |
+
import subprocess
|
| 8 |
+
import gradio as gr
|
| 9 |
|
| 10 |
# ---------------------------------------------------------------------------
|
| 11 |
# Build the mdc binary once at startup
|
| 12 |
# ---------------------------------------------------------------------------
|
| 13 |
|
| 14 |
+
BINARY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mdc")
|
| 15 |
+
|
| 16 |
|
| 17 |
def _build():
|
| 18 |
+
src = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mdc.c")
|
| 19 |
+
result = subprocess.run(["gcc", "-O3", "-o", BINARY, src, "-lm"],
|
| 20 |
+
capture_output=True, text=True)
|
|
|
|
|
|
|
| 21 |
if result.returncode != 0:
|
| 22 |
+
raise RuntimeError(f"Compilation failed:\n{result.stderr}")
|
| 23 |
+
|
| 24 |
|
| 25 |
if not os.path.isfile(BINARY):
|
| 26 |
_build()
|
| 27 |
|
| 28 |
+
|
| 29 |
# ---------------------------------------------------------------------------
|
| 30 |
+
# Helpers
|
| 31 |
# ---------------------------------------------------------------------------
|
| 32 |
|
| 33 |
+
def _fmt(n):
|
| 34 |
+
if n < 1024:
|
| 35 |
+
return f"{n} B"
|
| 36 |
+
if n < 1024 ** 2:
|
| 37 |
+
return f"{n / 1024:.1f} KB"
|
| 38 |
+
return f"{n / 1024 ** 2:.2f} MB"
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _download_link(b64_data, filename, label, color="#6366f1"):
|
| 42 |
+
size = len(base64.b64decode(b64_data))
|
| 43 |
+
return (
|
| 44 |
+
f'<a download="{filename}" '
|
| 45 |
+
f'href="data:application/octet-stream;base64,{b64_data}" '
|
| 46 |
+
f'style="display:inline-block;padding:10px 24px;background:{color};color:white;'
|
| 47 |
+
f'border-radius:8px;text-decoration:none;font-weight:bold;font-size:14px;'
|
| 48 |
+
f'margin-top:8px;cursor:pointer;">'
|
| 49 |
+
f'{label} ({_fmt(size)})</a>'
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
# ---------------------------------------------------------------------------
|
| 54 |
+
# Compress
|
| 55 |
+
# ---------------------------------------------------------------------------
|
| 56 |
+
|
| 57 |
+
def compress_b64(b64_data, filename):
|
| 58 |
+
if not b64_data or not b64_data.strip():
|
| 59 |
+
return "Please upload a file using the button above.", ""
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
try:
|
| 62 |
+
data = base64.b64decode(b64_data.strip())
|
| 63 |
+
except Exception:
|
| 64 |
+
return "**Failed to read uploaded file.**", ""
|
| 65 |
+
|
| 66 |
+
if len(data) == 0:
|
| 67 |
+
return "**Empty file.**", ""
|
| 68 |
+
|
| 69 |
+
with tempfile.TemporaryDirectory() as workdir:
|
| 70 |
+
in_path = os.path.join(workdir, "input")
|
| 71 |
+
out_path = os.path.join(workdir, "output.mdc")
|
| 72 |
+
with open(in_path, "wb") as f:
|
| 73 |
+
f.write(data)
|
| 74 |
+
result = subprocess.run([BINARY, "compress", in_path, out_path],
|
| 75 |
+
capture_output=True, text=True)
|
| 76 |
+
if result.returncode != 0:
|
| 77 |
+
return f"**Compression failed:**\n{result.stderr or result.stdout}", ""
|
| 78 |
+
with open(out_path, "rb") as f:
|
| 79 |
+
compressed = f.read()
|
| 80 |
+
|
| 81 |
+
in_size = len(data)
|
| 82 |
+
out_size = len(compressed)
|
| 83 |
+
ratio = out_size / in_size * 100
|
| 84 |
+
bpb = out_size * 8 / in_size
|
| 85 |
+
out_name = (filename.strip() + ".mdc") if filename and filename.strip() else "compressed.mdc"
|
| 86 |
+
b64_out = base64.b64encode(compressed).decode("ascii")
|
| 87 |
+
|
| 88 |
+
md = f"""## Compression Results
|
| 89 |
+
|
| 90 |
+
| | Size | Ratio |
|
| 91 |
+
|---|---|---|
|
| 92 |
+
| **Original** | {_fmt(in_size)} | 100% |
|
| 93 |
+
| **Midicoth** | {_fmt(out_size)} | {ratio:.1f}% |
|
| 94 |
+
|
| 95 |
+
**bpb:** {bpb:.3f} | **Space saved:** {100 - ratio:.1f}%
|
| 96 |
+
"""
|
| 97 |
+
return md, _download_link(b64_out, out_name, f"Download {out_name}")
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
# ---------------------------------------------------------------------------
|
| 101 |
+
# Decompress
|
| 102 |
+
# ---------------------------------------------------------------------------
|
| 103 |
+
|
| 104 |
+
def decompress_b64(b64_data):
|
| 105 |
+
if not b64_data or not b64_data.strip():
|
| 106 |
+
return "Please upload a .mdc file using the button above.", ""
|
| 107 |
+
|
| 108 |
try:
|
| 109 |
+
data = base64.b64decode(b64_data.strip())
|
| 110 |
+
except Exception:
|
| 111 |
+
return "**Failed to read uploaded file.**", ""
|
| 112 |
+
|
| 113 |
+
if len(data) < 12:
|
| 114 |
+
return "**Invalid file.** Too short to be a Midicoth file.", ""
|
| 115 |
+
|
| 116 |
+
if data[:4] != b"MDC7":
|
| 117 |
+
return "**Invalid file.** Not a Midicoth file (wrong magic bytes).", ""
|
| 118 |
+
|
| 119 |
+
with tempfile.TemporaryDirectory() as workdir:
|
| 120 |
+
in_path = os.path.join(workdir, "input.mdc")
|
| 121 |
+
out_path = os.path.join(workdir, "output")
|
| 122 |
+
with open(in_path, "wb") as f:
|
| 123 |
+
f.write(data)
|
| 124 |
+
result = subprocess.run([BINARY, "decompress", in_path, out_path],
|
| 125 |
+
capture_output=True, text=True)
|
| 126 |
+
if result.returncode != 0:
|
| 127 |
+
return f"**Decompression failed:**\n{result.stderr or result.stdout}", ""
|
| 128 |
+
with open(out_path, "rb") as f:
|
| 129 |
+
restored = f.read()
|
| 130 |
+
|
| 131 |
+
in_size = len(data)
|
| 132 |
+
out_size = len(restored)
|
| 133 |
+
b64_out = base64.b64encode(restored).decode("ascii")
|
| 134 |
+
|
| 135 |
+
md = f"""## Decompression Results
|
| 136 |
+
|
| 137 |
+
- Compressed: {_fmt(in_size)}
|
| 138 |
+
- Restored: {_fmt(out_size)}
|
| 139 |
+
- **Lossless reconstruction successful**
|
| 140 |
+
"""
|
| 141 |
+
return md, _download_link(b64_out, "decompressed.bin", "Download decompressed", color="#22c55e")
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
# ---------------------------------------------------------------------------
|
| 145 |
+
# JavaScript for binary file upload (reads as base64, injects into hidden Textbox)
|
| 146 |
+
# ---------------------------------------------------------------------------
|
| 147 |
+
|
| 148 |
+
COMPRESS_UPLOAD_JS = """
|
| 149 |
+
function setupCompressUpload() {
|
| 150 |
+
const input = document.getElementById('compress-file-input');
|
| 151 |
+
if (!input) return;
|
| 152 |
+
input.addEventListener('change', function(e) {
|
| 153 |
+
const file = e.target.files[0];
|
| 154 |
+
if (!file) return;
|
| 155 |
+
const nameSpan = document.getElementById('compress-file-name');
|
| 156 |
+
if (nameSpan) nameSpan.textContent = file.name + ' (' + (file.size / 1024).toFixed(1) + ' KB)';
|
| 157 |
+
const fnBoxes = document.querySelectorAll('#compress-filename textarea');
|
| 158 |
+
if (fnBoxes.length > 0) {
|
| 159 |
+
fnBoxes[0].value = file.name;
|
| 160 |
+
fnBoxes[0].dispatchEvent(new Event('input', {bubbles: true}));
|
| 161 |
+
}
|
| 162 |
+
const reader = new FileReader();
|
| 163 |
+
reader.onload = function(ev) {
|
| 164 |
+
const b64 = ev.target.result.split(',')[1];
|
| 165 |
+
const textareas = document.querySelectorAll('#compress-b64-data textarea');
|
| 166 |
+
if (textareas.length > 0) {
|
| 167 |
+
textareas[0].value = b64;
|
| 168 |
+
textareas[0].dispatchEvent(new Event('input', {bubbles: true}));
|
| 169 |
+
}
|
| 170 |
+
};
|
| 171 |
+
reader.readAsDataURL(file);
|
| 172 |
+
});
|
| 173 |
+
}
|
| 174 |
+
setTimeout(setupCompressUpload, 1000);
|
| 175 |
+
"""
|
| 176 |
+
|
| 177 |
+
DECOMPRESS_UPLOAD_JS = """
|
| 178 |
+
function setupDecompressUpload() {
|
| 179 |
+
const input = document.getElementById('decompress-file-input');
|
| 180 |
+
if (!input) return;
|
| 181 |
+
input.addEventListener('change', function(e) {
|
| 182 |
+
const file = e.target.files[0];
|
| 183 |
+
if (!file) return;
|
| 184 |
+
const nameSpan = document.getElementById('decompress-file-name');
|
| 185 |
+
if (nameSpan) nameSpan.textContent = file.name + ' (' + (file.size / 1024).toFixed(1) + ' KB)';
|
| 186 |
+
const reader = new FileReader();
|
| 187 |
+
reader.onload = function(ev) {
|
| 188 |
+
const b64 = ev.target.result.split(',')[1];
|
| 189 |
+
const textareas = document.querySelectorAll('#decompress-b64-data textarea');
|
| 190 |
+
if (textareas.length > 0) {
|
| 191 |
+
textareas[0].value = b64;
|
| 192 |
+
textareas[0].dispatchEvent(new Event('input', {bubbles: true}));
|
| 193 |
+
}
|
| 194 |
+
};
|
| 195 |
+
reader.readAsDataURL(file);
|
| 196 |
+
});
|
| 197 |
+
}
|
| 198 |
+
setTimeout(setupDecompressUpload, 1200);
|
| 199 |
+
"""
|
| 200 |
|
| 201 |
# ---------------------------------------------------------------------------
|
| 202 |
+
# UI
|
| 203 |
# ---------------------------------------------------------------------------
|
| 204 |
|
| 205 |
+
HEADER_HTML = """
|
| 206 |
+
<div style="text-align:center;margin-bottom:0.5em;">
|
| 207 |
+
<h1 style="font-size:2em;margin-bottom:0.2em;">ποΈ Midicoth</h1>
|
| 208 |
+
<p style="font-size:1.1em;color:#aaa;margin-top:4px;">Micro-Diffusion Compression — Binary Tree Tweedie Denoising</p>
|
| 209 |
+
<p style="font-size:0.9em;">
|
| 210 |
+
<a href="https://github.com/robtacconelli/midicoth">GitHub</a> |
|
| 211 |
+
No neural network • No GPU • ~2,000 lines of C •
|
| 212 |
+
Outperforms xz, zstd, Brotli, bzip2 on text
|
| 213 |
+
</p>
|
| 214 |
+
</div>
|
| 215 |
+
"""
|
| 216 |
+
|
| 217 |
with gr.Blocks(title="Midicoth β Micro-Diffusion Compression") as demo:
|
| 218 |
+
gr.HTML(HEADER_HTML)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
|
| 220 |
+
# ---- Tab 1: Compress ----
|
| 221 |
with gr.Tab("Compress"):
|
| 222 |
+
gr.Markdown("Upload any file to compress with Midicoth.")
|
| 223 |
+
gr.HTML(
|
| 224 |
+
'<div style="margin:8px 0;">'
|
| 225 |
+
'<label style="display:inline-block;padding:10px 24px;background:#6366f1;color:white;'
|
| 226 |
+
'border-radius:8px;cursor:pointer;font-weight:bold;font-size:14px;">'
|
| 227 |
+
'Choose file to compress'
|
| 228 |
+
'<input id="compress-file-input" type="file" style="display:none;">'
|
| 229 |
+
'</label>'
|
| 230 |
+
'<span id="compress-file-name" style="margin-left:10px;color:#aaa;"></span>'
|
| 231 |
+
'</div>'
|
| 232 |
+
)
|
| 233 |
+
compress_b64_data = gr.Textbox(visible=False, elem_id="compress-b64-data")
|
| 234 |
+
compress_filename = gr.Textbox(visible=False, elem_id="compress-filename")
|
| 235 |
+
compress_btn = gr.Button("Compress", variant="primary", size="lg")
|
| 236 |
+
compress_results = gr.Markdown()
|
| 237 |
+
compress_download = gr.HTML()
|
| 238 |
+
|
| 239 |
+
compress_btn.click(
|
| 240 |
+
fn=compress_b64,
|
| 241 |
+
inputs=[compress_b64_data, compress_filename],
|
| 242 |
+
outputs=[compress_results, compress_download],
|
| 243 |
+
)
|
| 244 |
|
| 245 |
+
# ---- Tab 2: Decompress ----
|
| 246 |
with gr.Tab("Decompress"):
|
| 247 |
+
gr.Markdown("Upload a `.mdc` file to decompress.")
|
| 248 |
+
gr.HTML(
|
| 249 |
+
'<div style="margin:8px 0;">'
|
| 250 |
+
'<label style="display:inline-block;padding:10px 24px;background:#6366f1;color:white;'
|
| 251 |
+
'border-radius:8px;cursor:pointer;font-weight:bold;font-size:14px;">'
|
| 252 |
+
'Upload .mdc file'
|
| 253 |
+
'<input id="decompress-file-input" type="file" accept=".mdc" style="display:none;">'
|
| 254 |
+
'</label>'
|
| 255 |
+
'<span id="decompress-file-name" style="margin-left:10px;color:#aaa;"></span>'
|
| 256 |
+
'</div>'
|
| 257 |
+
)
|
| 258 |
+
decompress_b64_data = gr.Textbox(visible=False, elem_id="decompress-b64-data")
|
| 259 |
+
decompress_btn = gr.Button("Decompress", variant="primary", size="lg")
|
| 260 |
+
decompress_results = gr.Markdown()
|
| 261 |
+
decompress_download = gr.HTML()
|
| 262 |
+
|
| 263 |
+
decompress_btn.click(
|
| 264 |
+
fn=decompress_b64,
|
| 265 |
+
inputs=[decompress_b64_data],
|
| 266 |
+
outputs=[decompress_results, decompress_download],
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
gr.Markdown("""
|
| 270 |
+
---
|
| 271 |
+
**How it works:** Midicoth processes input through a five-layer cascade β
|
| 272 |
+
PPM (orders 0β4) β Match Model β Word Model β High-Order Context (orders 5β8) β
|
| 273 |
+
Micro-Diffusion Tweedie Denoiser β feeding a 32-bit arithmetic coder.
|
| 274 |
+
|
| 275 |
+
| Benchmark | Midicoth | xz -9 | Improvement |
|
| 276 |
+
|-----------|----------|--------|-------------|
|
| 277 |
+
| alice29.txt (152 KB) | 2.119 bpb | 2.551 bpb | +16.9% |
|
| 278 |
+
| enwik8 (100 MB) | 1.753 bpb | 1.989 bpb | +11.9% |
|
| 279 |
+
|
| 280 |
+
Apache 2.0 | [Roberto Tacconelli](https://github.com/robtacconelli) | [arXiv:2603.08771](https://arxiv.org/abs/2603.08771)
|
| 281 |
+
""")
|
| 282 |
+
|
| 283 |
+
gr.HTML(f"<script>{COMPRESS_UPLOAD_JS}</script>")
|
| 284 |
+
gr.HTML(f"<script>{DECOMPRESS_UPLOAD_JS}</script>")
|
| 285 |
|
| 286 |
+
demo.queue()
|
| 287 |
demo.launch()
|
requirements.txt
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
-
|
|
|
|
|
|
| 1 |
+
spaces>=0.20.0
|
| 2 |
+
gradio>=4.0.0
|