Polish upload preview and deployment dependencies
Browse files- DEPLOYMENT_LOG.md +14 -0
- RUNBOOK.md +1 -1
- app.py +104 -25
- requirements.txt +1 -1
DEPLOYMENT_LOG.md
CHANGED
|
@@ -26,6 +26,20 @@ CPU fallback after a Transformers failure is now opt-in with:
|
|
| 26 |
AUTO_FALLBACK_TO_LLAMACPP=1
|
| 27 |
```
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
## 2026-06-10 — Switch from Docker Space to Gradio ZeroGPU
|
| 30 |
|
| 31 |
Decision: use **Gradio ZeroGPU** as the active Hugging Face Space architecture.
|
|
|
|
| 26 |
AUTO_FALLBACK_TO_LLAMACPP=1
|
| 27 |
```
|
| 28 |
|
| 29 |
+
Follow-up fix: MiniCPM-V 4.6 declares `model_type: minicpmv4_6` and its official model card
|
| 30 |
+
requires `transformers[torch]>=5.7.0`. The Space was still pinned to `transformers==4.57.3`, which
|
| 31 |
+
caused the ZeroGPU worker to fail with:
|
| 32 |
+
|
| 33 |
+
```text
|
| 34 |
+
Transformers does not recognize this architecture
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
The runtime now pins:
|
| 38 |
+
|
| 39 |
+
```text
|
| 40 |
+
transformers[torch]==5.7.0
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
## 2026-06-10 — Switch from Docker Space to Gradio ZeroGPU
|
| 44 |
|
| 45 |
Decision: use **Gradio ZeroGPU** as the active Hugging Face Space architecture.
|
RUNBOOK.md
CHANGED
|
@@ -53,7 +53,7 @@ Install dependencies from `requirements.txt`, including:
|
|
| 53 |
```text
|
| 54 |
spaces
|
| 55 |
torch
|
| 56 |
-
transformers
|
| 57 |
llama-cpp-python
|
| 58 |
```
|
| 59 |
|
|
|
|
| 53 |
```text
|
| 54 |
spaces
|
| 55 |
torch
|
| 56 |
+
transformers[torch]==5.7.0
|
| 57 |
llama-cpp-python
|
| 58 |
```
|
| 59 |
|
app.py
CHANGED
|
@@ -135,6 +135,29 @@ def _display_status_label(status: str) -> str:
|
|
| 135 |
return normalized.title() if normalized else "Unknown"
|
| 136 |
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
def workflow_arrow_html(kind: str) -> str:
|
| 139 |
kind = kind if kind in {"upload", "report"} else "upload"
|
| 140 |
if kind == "upload":
|
|
@@ -190,19 +213,16 @@ def upload_state(uploaded_file: str | None) -> tuple[Any, Any]:
|
|
| 190 |
workflow_phase_html("ready"),
|
| 191 |
)
|
| 192 |
|
| 193 |
-
filename = os.path.basename(uploaded_file)
|
| 194 |
preview_data_url = _uploaded_file_preview_data_url(uploaded_file)
|
| 195 |
return (
|
| 196 |
gr.update(visible=False),
|
| 197 |
gr.update(value="", visible=False),
|
| 198 |
-
gr.update(visible=True, value=selected_document_html(
|
| 199 |
workflow_phase_html("processing"),
|
| 200 |
)
|
| 201 |
|
| 202 |
|
| 203 |
-
def selected_document_html(
|
| 204 |
-
if not filename:
|
| 205 |
-
filename = "Document ready"
|
| 206 |
preview_markup = (
|
| 207 |
f'<img class="bte-upload-preview-image" src="{escape(preview_data_url)}" alt="Uploaded document preview">'
|
| 208 |
if preview_data_url
|
|
@@ -219,7 +239,6 @@ def selected_document_html(filename: str | None = None, preview_data_url: str |
|
|
| 219 |
<div class="bte-selected-preview" aria-hidden="true">
|
| 220 |
{preview_markup}
|
| 221 |
<div class="bte-selected-preview-overlay"></div>
|
| 222 |
-
<div class="bte-preview-watermark">{escape(filename)}</div>
|
| 223 |
</div>
|
| 224 |
</section>
|
| 225 |
"""
|
|
@@ -1021,7 +1040,7 @@ gradio-app,
|
|
| 1021 |
margin: 0 auto 18px !important;
|
| 1022 |
padding: 30px 28px 28px;
|
| 1023 |
display: grid;
|
| 1024 |
-
grid-template-columns: minmax(0, 1fr) minmax(
|
| 1025 |
gap: 32px;
|
| 1026 |
align-items: center;
|
| 1027 |
border: 1px solid rgba(255, 255, 255, 0.42);
|
|
@@ -1053,6 +1072,69 @@ gradio-app,
|
|
| 1053 |
min-width: 0;
|
| 1054 |
}
|
| 1055 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1056 |
.bte-title .bte-kicker,
|
| 1057 |
.bte-title .bte-kicker *,
|
| 1058 |
.bte-title h1,
|
|
@@ -1125,7 +1207,7 @@ gradio-app,
|
|
| 1125 |
border: 1px solid var(--bte-line) !important;
|
| 1126 |
border-radius: var(--bte-radius) !important;
|
| 1127 |
padding: 18px !important;
|
| 1128 |
-
background: var(--bte-
|
| 1129 |
box-shadow: var(--bte-shadow) !important;
|
| 1130 |
overflow: hidden !important;
|
| 1131 |
}
|
|
@@ -1137,7 +1219,7 @@ gradio-app,
|
|
| 1137 |
border: 1px solid var(--bte-line) !important;
|
| 1138 |
border-radius: var(--bte-radius) !important;
|
| 1139 |
padding: 18px !important;
|
| 1140 |
-
background: var(--bte-
|
| 1141 |
box-shadow: var(--bte-shadow) !important;
|
| 1142 |
overflow: hidden !important;
|
| 1143 |
}
|
|
@@ -1769,7 +1851,7 @@ gradio-app,
|
|
| 1769 |
border: 1px solid #d8e2ee;
|
| 1770 |
border-radius: 18px;
|
| 1771 |
padding: 22px;
|
| 1772 |
-
background: var(--bte-
|
| 1773 |
}
|
| 1774 |
|
| 1775 |
.bte-selected-preview {
|
|
@@ -1777,7 +1859,7 @@ gradio-app,
|
|
| 1777 |
min-height: 260px;
|
| 1778 |
border-radius: 22px;
|
| 1779 |
border: 1px solid rgba(216, 226, 238, 0.9);
|
| 1780 |
-
background:
|
| 1781 |
overflow: hidden;
|
| 1782 |
}
|
| 1783 |
|
|
@@ -1863,20 +1945,6 @@ gradio-app,
|
|
| 1863 |
filter: blur(1.2px);
|
| 1864 |
}
|
| 1865 |
|
| 1866 |
-
.bte-preview-watermark {
|
| 1867 |
-
position: absolute;
|
| 1868 |
-
left: 34px;
|
| 1869 |
-
right: 34px;
|
| 1870 |
-
bottom: 28px;
|
| 1871 |
-
color: rgba(17, 24, 39, 0.2);
|
| 1872 |
-
font-size: 18px;
|
| 1873 |
-
font-weight: 700;
|
| 1874 |
-
letter-spacing: 0;
|
| 1875 |
-
white-space: nowrap;
|
| 1876 |
-
overflow: hidden;
|
| 1877 |
-
text-overflow: ellipsis;
|
| 1878 |
-
}
|
| 1879 |
-
|
| 1880 |
.bte-selected-preview-overlay {
|
| 1881 |
position: absolute;
|
| 1882 |
inset: 0;
|
|
@@ -2968,6 +3036,15 @@ button.bte-action *,
|
|
| 2968 |
gap: 18px;
|
| 2969 |
}
|
| 2970 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2971 |
.bte-title h1,
|
| 2972 |
.bte-report h2 {
|
| 2973 |
font-size: 32px !important;
|
|
@@ -3177,6 +3254,8 @@ with gr.Blocks(title="Blood Test Explainer") as demo:
|
|
| 3177 |
</div>
|
| 3178 |
"""
|
| 3179 |
)
|
|
|
|
|
|
|
| 3180 |
|
| 3181 |
workflow_phase = gr.HTML(
|
| 3182 |
workflow_phase_html("ready"),
|
|
|
|
| 135 |
return normalized.title() if normalized else "Unknown"
|
| 136 |
|
| 137 |
|
| 138 |
+
def hero_attribution_html() -> str:
|
| 139 |
+
items = [
|
| 140 |
+
("Codex", "Build with Codex", "CDX", "aria-label=\"Codex logo\""),
|
| 141 |
+
("OpenBMB", "Enabled with OpenBMB", "OB", "aria-label=\"OpenBMB logo\""),
|
| 142 |
+
("Modal", "Finetuned with Modal", "M", "aria-label=\"Modal logo\""),
|
| 143 |
+
("ACG", "Created by researchers at ACG", "ACG", "aria-label=\"ACG logo\""),
|
| 144 |
+
]
|
| 145 |
+
badges = "\n".join(
|
| 146 |
+
f"""
|
| 147 |
+
<li class=\"bte-hero-badge bte-hero-badge--{escape(slug.lower())}\">
|
| 148 |
+
<span class=\"bte-hero-badge-mark\" {attrs}>{escape(mark)}</span>
|
| 149 |
+
<span class=\"bte-hero-badge-text\">{escape(label)}</span>
|
| 150 |
+
</li>
|
| 151 |
+
"""
|
| 152 |
+
for slug, label, mark, attrs in items
|
| 153 |
+
)
|
| 154 |
+
return f"""
|
| 155 |
+
<ul class=\"bte-hero-attribution\" aria-label=\"Project attributions\">
|
| 156 |
+
{badges}
|
| 157 |
+
</ul>
|
| 158 |
+
"""
|
| 159 |
+
|
| 160 |
+
|
| 161 |
def workflow_arrow_html(kind: str) -> str:
|
| 162 |
kind = kind if kind in {"upload", "report"} else "upload"
|
| 163 |
if kind == "upload":
|
|
|
|
| 213 |
workflow_phase_html("ready"),
|
| 214 |
)
|
| 215 |
|
|
|
|
| 216 |
preview_data_url = _uploaded_file_preview_data_url(uploaded_file)
|
| 217 |
return (
|
| 218 |
gr.update(visible=False),
|
| 219 |
gr.update(value="", visible=False),
|
| 220 |
+
gr.update(visible=True, value=selected_document_html(preview_data_url=preview_data_url)),
|
| 221 |
workflow_phase_html("processing"),
|
| 222 |
)
|
| 223 |
|
| 224 |
|
| 225 |
+
def selected_document_html(preview_data_url: str | None = None) -> str:
|
|
|
|
|
|
|
| 226 |
preview_markup = (
|
| 227 |
f'<img class="bte-upload-preview-image" src="{escape(preview_data_url)}" alt="Uploaded document preview">'
|
| 228 |
if preview_data_url
|
|
|
|
| 239 |
<div class="bte-selected-preview" aria-hidden="true">
|
| 240 |
{preview_markup}
|
| 241 |
<div class="bte-selected-preview-overlay"></div>
|
|
|
|
| 242 |
</div>
|
| 243 |
</section>
|
| 244 |
"""
|
|
|
|
| 1040 |
margin: 0 auto 18px !important;
|
| 1041 |
padding: 30px 28px 28px;
|
| 1042 |
display: grid;
|
| 1043 |
+
grid-template-columns: minmax(0, 1fr) minmax(280px, 360px);
|
| 1044 |
gap: 32px;
|
| 1045 |
align-items: center;
|
| 1046 |
border: 1px solid rgba(255, 255, 255, 0.42);
|
|
|
|
| 1072 |
min-width: 0;
|
| 1073 |
}
|
| 1074 |
|
| 1075 |
+
.bte-title-attribution-wrap {
|
| 1076 |
+
min-width: 0;
|
| 1077 |
+
justify-self: end;
|
| 1078 |
+
}
|
| 1079 |
+
|
| 1080 |
+
.bte-hero-attribution {
|
| 1081 |
+
list-style: none;
|
| 1082 |
+
margin: 0;
|
| 1083 |
+
padding: 0;
|
| 1084 |
+
display: grid;
|
| 1085 |
+
gap: 10px;
|
| 1086 |
+
}
|
| 1087 |
+
|
| 1088 |
+
.bte-hero-badge {
|
| 1089 |
+
display: grid;
|
| 1090 |
+
grid-template-columns: 34px minmax(0, 1fr);
|
| 1091 |
+
gap: 12px;
|
| 1092 |
+
align-items: center;
|
| 1093 |
+
padding: 10px 12px;
|
| 1094 |
+
border-radius: 14px;
|
| 1095 |
+
background: rgba(255, 255, 255, 0.12);
|
| 1096 |
+
border: 1px solid rgba(255, 255, 255, 0.18);
|
| 1097 |
+
backdrop-filter: blur(6px);
|
| 1098 |
+
}
|
| 1099 |
+
|
| 1100 |
+
.bte-hero-badge-mark {
|
| 1101 |
+
width: 34px;
|
| 1102 |
+
height: 34px;
|
| 1103 |
+
border-radius: 11px;
|
| 1104 |
+
display: grid;
|
| 1105 |
+
place-items: center;
|
| 1106 |
+
color: #fff;
|
| 1107 |
+
font-size: 11px;
|
| 1108 |
+
font-weight: 800;
|
| 1109 |
+
letter-spacing: 0;
|
| 1110 |
+
background: linear-gradient(135deg, rgba(255, 255, 255, 0.26), rgba(255, 255, 255, 0.08));
|
| 1111 |
+
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
|
| 1112 |
+
}
|
| 1113 |
+
|
| 1114 |
+
.bte-hero-badge-text {
|
| 1115 |
+
color: #ffffff !important;
|
| 1116 |
+
-webkit-text-fill-color: #ffffff !important;
|
| 1117 |
+
font-size: 13px;
|
| 1118 |
+
line-height: 1.2;
|
| 1119 |
+
font-weight: 700;
|
| 1120 |
+
}
|
| 1121 |
+
|
| 1122 |
+
.bte-hero-badge--codex .bte-hero-badge-mark {
|
| 1123 |
+
background: linear-gradient(135deg, rgba(37, 99, 235, 0.95), rgba(18, 128, 92, 0.92));
|
| 1124 |
+
}
|
| 1125 |
+
|
| 1126 |
+
.bte-hero-badge--openbmb .bte-hero-badge-mark {
|
| 1127 |
+
background: linear-gradient(135deg, rgba(18, 128, 92, 0.95), rgba(37, 99, 235, 0.92));
|
| 1128 |
+
}
|
| 1129 |
+
|
| 1130 |
+
.bte-hero-badge--modal .bte-hero-badge-mark {
|
| 1131 |
+
background: linear-gradient(135deg, rgba(191, 52, 52, 0.95), rgba(37, 99, 235, 0.9));
|
| 1132 |
+
}
|
| 1133 |
+
|
| 1134 |
+
.bte-hero-badge--acg .bte-hero-badge-mark {
|
| 1135 |
+
background: linear-gradient(135deg, rgba(90, 99, 214, 0.95), rgba(18, 128, 92, 0.9));
|
| 1136 |
+
}
|
| 1137 |
+
|
| 1138 |
.bte-title .bte-kicker,
|
| 1139 |
.bte-title .bte-kicker *,
|
| 1140 |
.bte-title h1,
|
|
|
|
| 1207 |
border: 1px solid var(--bte-line) !important;
|
| 1208 |
border-radius: var(--bte-radius) !important;
|
| 1209 |
padding: 18px !important;
|
| 1210 |
+
background: var(--bte-page) !important;
|
| 1211 |
box-shadow: var(--bte-shadow) !important;
|
| 1212 |
overflow: hidden !important;
|
| 1213 |
}
|
|
|
|
| 1219 |
border: 1px solid var(--bte-line) !important;
|
| 1220 |
border-radius: var(--bte-radius) !important;
|
| 1221 |
padding: 18px !important;
|
| 1222 |
+
background: var(--bte-page) !important;
|
| 1223 |
box-shadow: var(--bte-shadow) !important;
|
| 1224 |
overflow: hidden !important;
|
| 1225 |
}
|
|
|
|
| 1851 |
border: 1px solid #d8e2ee;
|
| 1852 |
border-radius: 18px;
|
| 1853 |
padding: 22px;
|
| 1854 |
+
background: var(--bte-page);
|
| 1855 |
}
|
| 1856 |
|
| 1857 |
.bte-selected-preview {
|
|
|
|
| 1859 |
min-height: 260px;
|
| 1860 |
border-radius: 22px;
|
| 1861 |
border: 1px solid rgba(216, 226, 238, 0.9);
|
| 1862 |
+
background: var(--bte-page);
|
| 1863 |
overflow: hidden;
|
| 1864 |
}
|
| 1865 |
|
|
|
|
| 1945 |
filter: blur(1.2px);
|
| 1946 |
}
|
| 1947 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1948 |
.bte-selected-preview-overlay {
|
| 1949 |
position: absolute;
|
| 1950 |
inset: 0;
|
|
|
|
| 3036 |
gap: 18px;
|
| 3037 |
}
|
| 3038 |
|
| 3039 |
+
.bte-title-attribution-wrap {
|
| 3040 |
+
justify-self: start;
|
| 3041 |
+
width: 100%;
|
| 3042 |
+
}
|
| 3043 |
+
|
| 3044 |
+
.bte-hero-attribution {
|
| 3045 |
+
grid-template-columns: 1fr;
|
| 3046 |
+
}
|
| 3047 |
+
|
| 3048 |
.bte-title h1,
|
| 3049 |
.bte-report h2 {
|
| 3050 |
font-size: 32px !important;
|
|
|
|
| 3254 |
</div>
|
| 3255 |
"""
|
| 3256 |
)
|
| 3257 |
+
with gr.Column(scale=0, min_width=300, elem_classes=["bte-title-attribution-wrap"]):
|
| 3258 |
+
gr.HTML(hero_attribution_html())
|
| 3259 |
|
| 3260 |
workflow_phase = gr.HTML(
|
| 3261 |
workflow_phase_html("ready"),
|
requirements.txt
CHANGED
|
@@ -7,7 +7,7 @@ json-repair==0.60.1
|
|
| 7 |
# ZeroGPU/CUDA path for EXTRACTOR_BACKEND=auto: the app uses the official OpenBMB
|
| 8 |
# Transformers pipeline when ACCELERATOR is ZeroGPU, ZERO_GPU=TRUE, or CUDA is visible.
|
| 9 |
torch==2.9.1 ; sys_platform == "linux" and platform_machine == "x86_64"
|
| 10 |
-
transformers==
|
| 11 |
accelerate==1.12.0
|
| 12 |
bitsandbytes==0.48.2 ; sys_platform == "linux" and platform_machine == "x86_64"
|
| 13 |
torchvision==0.24.1 ; sys_platform == "linux" and platform_machine == "x86_64"
|
|
|
|
| 7 |
# ZeroGPU/CUDA path for EXTRACTOR_BACKEND=auto: the app uses the official OpenBMB
|
| 8 |
# Transformers pipeline when ACCELERATOR is ZeroGPU, ZERO_GPU=TRUE, or CUDA is visible.
|
| 9 |
torch==2.9.1 ; sys_platform == "linux" and platform_machine == "x86_64"
|
| 10 |
+
transformers[torch]==5.7.0
|
| 11 |
accelerate==1.12.0
|
| 12 |
bitsandbytes==0.48.2 ; sys_platform == "linux" and platform_machine == "x86_64"
|
| 13 |
torchvision==0.24.1 ; sys_platform == "linux" and platform_machine == "x86_64"
|