st4ck commited on
Commit
a5d3f72
·
1 Parent(s): 9924fec

Replace Gradio with Flask: no analytics, no network deps, pure HTTP

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -4
  2. app.py +122 -156
  3. requirements.txt +1 -0
Dockerfile CHANGED
@@ -8,11 +8,8 @@ COPY . .
8
  # Pre-compile the binary at image build time
9
  RUN gcc -O2 -o mdc mdc.c -lm
10
 
11
- RUN pip install --no-cache-dir gradio==4.44.0 huggingface_hub==0.22.2
12
 
13
  EXPOSE 7860
14
- ENV GRADIO_SERVER_NAME=0.0.0.0
15
- ENV GRADIO_SERVER_PORT=7860
16
- ENV GRADIO_ANALYTICS_ENABLED=False
17
 
18
  CMD ["python", "app.py"]
 
8
  # Pre-compile the binary at image build time
9
  RUN gcc -O2 -o mdc mdc.c -lm
10
 
11
+ RUN pip install --no-cache-dir flask
12
 
13
  EXPOSE 7860
 
 
 
14
 
15
  CMD ["python", "app.py"]
app.py CHANGED
@@ -1,167 +1,133 @@
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
- # Lazy build
12
- # ---------------------------------------------------------------------------
13
-
14
- _DIR = os.path.dirname(os.path.abspath(__file__))
15
- BINARY = os.path.join(_DIR, "mdc")
16
-
17
-
18
- def _ensure_built():
19
- return None # compiled at Docker build time
20
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
- # ---------------------------------------------------------------------------
23
- # Helpers
24
- # ---------------------------------------------------------------------------
25
 
26
- def _fmt(n):
27
- if n < 1024: return f"{n} B"
28
- if n < 1024 ** 2: return f"{n/1024:.1f} KB"
29
- return f"{n/1024**2:.2f} MB"
30
 
31
 
32
- def _dl(b64, name, label, color="#6366f1"):
33
- sz = len(base64.b64decode(b64))
34
- return (f'<a download="{name}" href="data:application/octet-stream;base64,{b64}" '
35
- f'style="display:inline-block;padding:10px 24px;background:{color};color:white;'
36
- f'border-radius:8px;text-decoration:none;font-weight:bold;font-size:14px;'
37
- f'margin-top:8px;">{label} ({_fmt(sz)})</a>')
38
-
39
-
40
- # ---------------------------------------------------------------------------
41
- # Core functions (inputs/outputs are plain strings — no gr.File)
42
- # ---------------------------------------------------------------------------
43
-
44
- def compress(b64_data, filename):
45
- err = _ensure_built()
46
- if err:
47
- return f"**Build error:** {err}", ""
48
- if not b64_data:
49
- return "Upload a file first.", ""
50
- try:
51
- data = base64.b64decode(b64_data.strip())
52
- except Exception:
53
- return "**Could not decode file.**", ""
54
  with tempfile.TemporaryDirectory() as d:
55
- inp = os.path.join(d, "in")
56
- out = os.path.join(d, "out.mdc")
57
- open(inp, "wb").write(data)
58
  r = subprocess.run([BINARY, "compress", inp, out], capture_output=True, text=True)
59
  if r.returncode != 0:
60
- return f"**Compression failed:** {r.stderr}", ""
61
- compressed = open(out, "rb").read()
62
- in_sz, out_sz = len(data), len(compressed)
63
- bpb = out_sz * 8 / in_sz
64
- name = (filename.strip() + ".mdc") if filename and filename.strip() else "compressed.mdc"
65
- md = (f"| | Size | Ratio |\n|---|---|---|\n"
66
- f"| **Original** | {_fmt(in_sz)} | 100% |\n"
67
- f"| **Midicoth** | {_fmt(out_sz)} | {out_sz/in_sz*100:.1f}% |\n\n"
68
- f"**bpb:** {bpb:.3f} **Space saved:** {100 - out_sz/in_sz*100:.1f}%")
69
- return md, _dl(base64.b64encode(compressed).decode(), name, f"Download {name}")
70
-
71
-
72
- def decompress(b64_data):
73
- err = _ensure_built()
74
- if err:
75
- return f"**Build error:** {err}", ""
76
- if not b64_data:
77
- return "Upload a .mdc file first.", ""
78
- try:
79
- data = base64.b64decode(b64_data.strip())
80
- except Exception:
81
- return "**Could not decode file.**", ""
82
- if len(data) < 12 or data[:4] != b"MDC7":
83
- return "**Not a valid Midicoth file.**", ""
84
  with tempfile.TemporaryDirectory() as d:
85
- inp = os.path.join(d, "in.mdc")
86
- out = os.path.join(d, "out")
87
- open(inp, "wb").write(data)
 
 
 
 
 
88
  r = subprocess.run([BINARY, "decompress", inp, out], capture_output=True, text=True)
89
  if r.returncode != 0:
90
- return f"**Decompression failed:** {r.stderr}", ""
91
- restored = open(out, "rb").read()
92
- md = f"Restored **{_fmt(len(restored))}** — lossless OK"
93
- return md, _dl(base64.b64encode(restored).decode(), "restored.bin", "Download restored", "#22c55e")
94
-
95
-
96
- # ---------------------------------------------------------------------------
97
- # JS: read file as base64, inject into hidden Textbox
98
- # ---------------------------------------------------------------------------
99
-
100
- JS = """
101
- function setupUploads() {
102
- function wire(inputId, nameId, b64Id, fnId) {
103
- var input = document.getElementById(inputId);
104
- if (!input) return;
105
- input.addEventListener('change', function(e) {
106
- var file = e.target.files[0];
107
- if (!file) return;
108
- var nameEl = document.getElementById(nameId);
109
- if (nameEl) nameEl.textContent = file.name + ' (' + (file.size/1024).toFixed(1) + ' KB)';
110
- if (fnId) {
111
- var fn = document.querySelectorAll('#' + fnId + ' textarea');
112
- if (fn.length) { fn[0].value = file.name; fn[0].dispatchEvent(new Event('input',{bubbles:true})); }
113
- }
114
- var reader = new FileReader();
115
- reader.onload = function(ev) {
116
- var b64 = ev.target.result.split(',')[1];
117
- var ta = document.querySelectorAll('#' + b64Id + ' textarea');
118
- if (ta.length) { ta[0].value = b64; ta[0].dispatchEvent(new Event('input',{bubbles:true})); }
119
- };
120
- reader.readAsDataURL(file);
121
- });
122
- }
123
- wire('c-input', 'c-name', 'c-b64', 'c-fn');
124
- wire('d-input', 'd-name', 'd-b64', null);
125
- }
126
- setTimeout(setupUploads, 800);
127
- """
128
-
129
- BTN = ('display:inline-block;padding:10px 24px;background:#6366f1;color:white;'
130
- 'border-radius:8px;cursor:pointer;font-weight:bold;font-size:14px;')
131
-
132
- # ---------------------------------------------------------------------------
133
- # UI
134
- # ---------------------------------------------------------------------------
135
-
136
- print("Building UI...", flush=True)
137
- with gr.Blocks(title="Midicoth — Micro-Diffusion Compression", analytics_enabled=False) as demo:
138
- gr.Markdown("# 🗜️ Midicoth — Micro-Diffusion Compression\n"
139
- "Lossless compression via Binary Tree Tweedie Denoising. "
140
- "No neural network · No GPU · Outperforms xz/zstd/Brotli on text.")
141
-
142
- with gr.Tab("Compress"):
143
- gr.HTML(f'<label style="{BTN}">Choose file'
144
- f'<input id="c-input" type="file" style="display:none;"></label>'
145
- f'<span id="c-name" style="margin-left:10px;color:#aaa;"></span>')
146
- c_b64 = gr.Textbox(visible=False, elem_id="c-b64")
147
- c_fn = gr.Textbox(visible=False, elem_id="c-fn")
148
- c_btn = gr.Button("Compress", variant="primary")
149
- c_res = gr.Markdown()
150
- c_dl = gr.HTML()
151
- c_btn.click(compress, inputs=[c_b64, c_fn], outputs=[c_res, c_dl], api_name=False)
152
-
153
- with gr.Tab("Decompress"):
154
- gr.HTML(f'<label style="{BTN}">Upload .mdc file'
155
- f'<input id="d-input" type="file" accept=".mdc" style="display:none;"></label>'
156
- f'<span id="d-name" style="margin-left:10px;color:#aaa;"></span>')
157
- d_b64 = gr.Textbox(visible=False, elem_id="d-b64")
158
- d_btn = gr.Button("Decompress", variant="primary")
159
- d_res = gr.Markdown()
160
- d_dl = gr.HTML()
161
- d_btn.click(decompress, inputs=[d_b64], outputs=[d_res, d_dl], api_name=False)
162
-
163
- gr.HTML(f"<script>{JS}</script>")
164
-
165
- print("Launching server...", flush=True)
166
- demo.launch(server_name="0.0.0.0", server_port=7860, show_api=False)
167
- print("Server launched.", flush=True)
 
1
  #!/usr/bin/env python3
2
+ """Midicoth Micro-Diffusion Compression HuggingFace Space Demo."""
3
+
4
+ import os, subprocess, tempfile
5
+ from flask import Flask, request, send_file, render_template_string
6
+
7
+ app = Flask(__name__)
8
+ BINARY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mdc")
9
+
10
+ HTML = """<!DOCTYPE html>
11
+ <html lang="en">
12
+ <head>
13
+ <meta charset="UTF-8">
14
+ <meta name="viewport" content="width=device-width,initial-scale=1">
15
+ <title>Midicoth Micro-Diffusion Compression</title>
16
+ <style>
17
+ body{font-family:system-ui,sans-serif;max-width:720px;margin:40px auto;padding:0 20px;background:#0f172a;color:#e2e8f0}
18
+ h1{font-size:1.8em;margin-bottom:4px}
19
+ p.sub{color:#94a3b8;margin-top:0}
20
+ .card{background:#1e293b;border-radius:12px;padding:24px;margin:20px 0}
21
+ h2{font-size:1.1em;margin-top:0;color:#7dd3fc}
22
+ label{display:block;margin-bottom:8px;font-size:.9em;color:#94a3b8}
23
+ input[type=file]{width:100%;padding:10px;background:#0f172a;border:1px solid #334155;border-radius:8px;color:#e2e8f0;box-sizing:border-box}
24
+ button{margin-top:12px;padding:10px 28px;background:#6366f1;color:white;border:none;border-radius:8px;font-size:1em;font-weight:600;cursor:pointer}
25
+ button:hover{background:#4f46e5}
26
+ .error{color:#f87171;margin-top:10px;font-size:.9em}
27
+ .stats{margin-top:10px;font-size:.9em;color:#94a3b8;background:#0f172a;padding:10px;border-radius:6px}
28
+ a.dl{display:inline-block;margin-top:10px;padding:10px 24px;background:#22c55e;color:white;border-radius:8px;text-decoration:none;font-weight:600}
29
+ footer{margin-top:40px;font-size:.8em;color:#475569;text-align:center}
30
+ table{border-collapse:collapse;width:100%;margin-top:10px}
31
+ td,th{padding:6px 12px;border:1px solid #334155;font-size:.9em}
32
+ th{color:#7dd3fc;font-weight:600}
33
+ </style>
34
+ </head>
35
+ <body>
36
+ <h1>🗜️ Midicoth</h1>
37
+ <p class="sub">Micro-Diffusion Compression · Binary Tree Tweedie Denoising · No neural network · No GPU</p>
38
+
39
+ <div class="card">
40
+ <h2>Compress</h2>
41
+ <form method="post" action="/compress" enctype="multipart/form-data">
42
+ <label>Upload any file:</label>
43
+ <input type="file" name="file" required>
44
+ <button type="submit">Compress →</button>
45
+ </form>
46
+ {% if compress_error %}<p class="error">{{ compress_error }}</p>{% endif %}
47
+ </div>
48
+
49
+ <div class="card">
50
+ <h2>Decompress</h2>
51
+ <form method="post" action="/decompress" enctype="multipart/form-data">
52
+ <label>Upload a <code>.mdc</code> file:</label>
53
+ <input type="file" name="file" accept=".mdc" required>
54
+ <button type="submit">Decompress →</button>
55
+ </form>
56
+ {% if decompress_error %}<p class="error">{{ decompress_error }}</p>{% endif %}
57
+ </div>
58
+
59
+ <div class="card" style="font-size:.88em;color:#94a3b8">
60
+ <b style="color:#e2e8f0">How it works:</b> PPM (orders 0–4) → Match Model → Word Model →
61
+ High-Order Context (orders 5–8) → Micro-Diffusion Tweedie Denoiser → Arithmetic Coder.<br><br>
62
+ <table>
63
+ <tr><th>Benchmark</th><th>Midicoth</th><th>xz -9</th><th>Improvement</th></tr>
64
+ <tr><td>alice29.txt (152 KB)</td><td>2.119 bpb</td><td>2.551 bpb</td><td>+16.9%</td></tr>
65
+ <tr><td>enwik8 (100 MB)</td><td>1.753 bpb</td><td>1.989 bpb</td><td>+11.9%</td></tr>
66
+ </table>
67
+ </div>
68
+
69
+ <footer>Apache 2.0 · <a href="https://github.com/robtacconelli/midicoth" style="color:#6366f1">GitHub</a>
70
+ · <a href="https://arxiv.org/abs/2603.08771" style="color:#6366f1">arXiv:2603.08771</a></footer>
71
+ </body></html>"""
72
+
73
+
74
+ def fmt(n):
75
+ if n < 1024: return f"{n} B"
76
+ if n < 1024**2: return f"{n/1024:.1f} KB"
77
+ return f"{n/1024**2:.2f} MB"
78
 
 
 
 
79
 
80
+ @app.route("/")
81
+ def index():
82
+ return render_template_string(HTML)
 
83
 
84
 
85
+ @app.route("/compress", methods=["POST"])
86
+ def compress():
87
+ f = request.files.get("file")
88
+ if not f or not f.filename:
89
+ return render_template_string(HTML, compress_error="No file uploaded.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  with tempfile.TemporaryDirectory() as d:
91
+ inp = os.path.join(d, "input")
92
+ out = os.path.join(d, "output.mdc")
93
+ f.save(inp)
94
  r = subprocess.run([BINARY, "compress", inp, out], capture_output=True, text=True)
95
  if r.returncode != 0:
96
+ return render_template_string(HTML, compress_error=f"Compression failed: {r.stderr}")
97
+ in_sz = os.path.getsize(inp)
98
+ out_sz = os.path.getsize(out)
99
+ bpb = out_sz * 8 / in_sz
100
+ stats = (f"Original: {fmt(in_sz)} Compressed: {fmt(out_sz)} "
101
+ f"({out_sz/in_sz*100:.1f}%, {bpb:.3f} bpb, "
102
+ f"saved {100-out_sz/in_sz*100:.1f}%)")
103
+ return send_file(out, as_attachment=True,
104
+ download_name=f.filename + ".mdc",
105
+ mimetype="application/octet-stream")
106
+
107
+
108
+ @app.route("/decompress", methods=["POST"])
109
+ def decompress():
110
+ f = request.files.get("file")
111
+ if not f or not f.filename:
112
+ return render_template_string(HTML, decompress_error="No file uploaded.")
 
 
 
 
 
 
 
113
  with tempfile.TemporaryDirectory() as d:
114
+ inp = os.path.join(d, "input.mdc")
115
+ out = os.path.join(d, "output")
116
+ f.save(inp)
117
+ # Validate magic
118
+ with open(inp, "rb") as fh:
119
+ magic = fh.read(4)
120
+ if magic != b"MDC7":
121
+ return render_template_string(HTML, decompress_error="Not a valid Midicoth (.mdc) file.")
122
  r = subprocess.run([BINARY, "decompress", inp, out], capture_output=True, text=True)
123
  if r.returncode != 0:
124
+ return render_template_string(HTML, decompress_error=f"Decompression failed: {r.stderr}")
125
+ name = f.filename[:-4] if f.filename.endswith(".mdc") else f.filename + ".restored"
126
+ return send_file(out, as_attachment=True,
127
+ download_name=name,
128
+ mimetype="application/octet-stream")
129
+
130
+
131
+ if __name__ == "__main__":
132
+ print("Starting Midicoth server on 0.0.0.0:7860", flush=True)
133
+ app.run(host="0.0.0.0", port=7860, debug=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -0,0 +1 @@
 
 
1
+ flask