Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -54,7 +54,8 @@ def convert(text: str, num_candidates: int = 3, structured_fallback: bool = True
|
|
| 54 |
|
| 55 |
PASITA samples the document several times, every candidate is post-processed and
|
| 56 |
scored for number fidelity, content coverage and GFM validity, and the best one is
|
| 57 |
-
returned. Inputs detected as HTML or delimited data are converted deterministically
|
|
|
|
| 58 |
|
| 59 |
Args:
|
| 60 |
text: The document to convert, for example OCR output, pasted HTML, meeting notes or a report.
|
|
@@ -93,9 +94,14 @@ def convert(text: str, num_candidates: int = 3, structured_fallback: bool = True
|
|
| 93 |
metrics = result["metrics"]
|
| 94 |
numbers = f"{metrics['numbers_matched']}/{metrics['numbers_in']} NUMBERS"
|
| 95 |
overlap = f"{metrics['content_overlap']:.0%} CONTENT"
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
taken = result["seconds"] if result["seconds"] else time.perf_counter() - started
|
| 98 |
-
detail = f"{
|
| 99 |
return result["markdown"], result["markdown"], _status("DONE", "ready", detail)
|
| 100 |
|
| 101 |
|
|
@@ -103,6 +109,21 @@ def reset() -> tuple[str, str, str, str]:
|
|
| 103 |
return EMPTY_MD, "", _status("READY"), _counter("")
|
| 104 |
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
HEADER = f"""
|
| 107 |
<header class="p-head">
|
| 108 |
<div class="p-brand">
|
|
@@ -228,7 +249,7 @@ body, .gradio-container {
|
|
| 228 |
-webkit-font-smoothing: antialiased;
|
| 229 |
}
|
| 230 |
.gradio-container { max-width: 1240px !important; margin: 0 auto; padding: 0 20px 26px; }
|
| 231 |
-
footer { display: none !important; }
|
| 232 |
:focus-visible { outline: 2px solid var(--cool) !important; outline-offset: 2px; }
|
| 233 |
|
| 234 |
.p-head {
|
|
@@ -284,29 +305,33 @@ footer { display: none !important; }
|
|
| 284 |
#p-clear { flex: 1; background: transparent !important; color: var(--muted) !important; border: 1px solid var(--hair) !important; }
|
| 285 |
#p-clear:hover { color: var(--ink) !important; border-color: rgba(255,255,255,0.3) !important; }
|
| 286 |
|
| 287 |
-
#p-examples
|
| 288 |
-
#p-examples
|
|
|
|
| 289 |
background: var(--panel) !important; border: 1px solid var(--hair) !important; border-radius: 999px !important;
|
| 290 |
-
color: var(--muted) !important; font-size: 12px !important; min-height:
|
| 291 |
text-align: left; transition: background .2s ease, color .2s ease, border-color .2s ease;
|
| 292 |
}
|
| 293 |
-
#p-examples
|
| 294 |
-
#p-examples label { font-family: var(--font-mono) !important; font-size: 0.66rem !important; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted) !important; }
|
| 295 |
|
| 296 |
-
#p-tabs .tab-
|
| 297 |
display: inline-flex; gap: 4px; background: var(--panel); border: 1px solid var(--hair);
|
| 298 |
border-radius: 100px; padding: 3px; margin-bottom: 12px;
|
| 299 |
}
|
| 300 |
-
#p-tabs .tab-
|
| 301 |
appearance: none; border: 0 !important; background: transparent !important; border-radius: 100px !important;
|
| 302 |
padding: 7px 14px !important; font-family: var(--font-mono) !important; font-size: 10.5px !important;
|
| 303 |
letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted) !important;
|
| 304 |
}
|
| 305 |
-
#p-tabs .tab-
|
|
|
|
| 306 |
background: rgba(255,255,255,0.08) !important; color: var(--warm) !important;
|
| 307 |
}
|
|
|
|
|
|
|
| 308 |
|
| 309 |
#p-rendered { min-height: 360px; font-size: 0.95rem; line-height: 1.72; color: var(--ink); }
|
|
|
|
| 310 |
#p-rendered h1, #p-rendered h2, #p-rendered h3, #p-rendered h4 { color: #fff; font-weight: 600; line-height: 1.25; margin: 1.25em 0 0.55em; }
|
| 311 |
#p-rendered h1 { font-size: 1.5em; }
|
| 312 |
#p-rendered h2 { font-size: 1.28em; }
|
|
@@ -330,9 +355,14 @@ footer { display: none !important; }
|
|
| 330 |
background: var(--obsidian) !important; border: 1px solid var(--hair-soft) !important;
|
| 331 |
border-radius: 12px !important; font-family: var(--font-mono) !important; min-height: 360px;
|
| 332 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
|
| 334 |
#p-settings { background: transparent !important; border: 1px solid var(--hair-soft) !important; border-radius: 12px !important; margin-top: 4px; }
|
| 335 |
-
#p-settings
|
|
|
|
| 336 |
#p-settings .p-label { margin-bottom: 4px; }
|
| 337 |
|
| 338 |
.p-foot {
|
|
@@ -376,9 +406,9 @@ with gr.Blocks(title="PASITA: plain text to Markdown") as demo:
|
|
| 376 |
text_in = gr.Textbox(
|
| 377 |
show_label=False,
|
| 378 |
container=False,
|
| 379 |
-
placeholder="Paste OCR output, pasted HTML, meeting notes, a report or an article...",
|
| 380 |
lines=18,
|
| 381 |
-
max_lines=
|
| 382 |
elem_id="p-text",
|
| 383 |
)
|
| 384 |
with gr.Accordion("SETTINGS", open=False, elem_id="p-settings"):
|
|
@@ -397,13 +427,17 @@ with gr.Blocks(title="PASITA: plain text to Markdown") as demo:
|
|
| 397 |
clear = gr.Button("CLEAR", elem_id="p-clear")
|
| 398 |
with gr.Column(scale=1, min_width=320, elem_classes=["p-panel"]):
|
| 399 |
with gr.Tabs(elem_id="p-tabs"):
|
| 400 |
-
with gr.Tab("RENDERED"):
|
| 401 |
-
md_out = gr.Markdown(EMPTY_MD, buttons=["copy"], elem_id="p-rendered")
|
| 402 |
-
with gr.Tab("SOURCE"):
|
| 403 |
-
source = gr.Code(
|
|
|
|
|
|
|
|
|
|
| 404 |
|
| 405 |
gr.Examples(
|
| 406 |
examples=EXAMPLES,
|
|
|
|
| 407 |
inputs=[text_in],
|
| 408 |
outputs=[md_out, source, status],
|
| 409 |
fn=convert,
|
|
@@ -415,10 +449,24 @@ with gr.Blocks(title="PASITA: plain text to Markdown") as demo:
|
|
| 415 |
)
|
| 416 |
gr.HTML(FOOTER)
|
| 417 |
|
| 418 |
-
run.click(
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
|
| 423 |
demo.queue()
|
| 424 |
demo.launch(theme=theme, css=CSS, mcp_server=True)
|
|
|
|
| 54 |
|
| 55 |
PASITA samples the document several times, every candidate is post-processed and
|
| 56 |
scored for number fidelity, content coverage and GFM validity, and the best one is
|
| 57 |
+
returned. Inputs detected as HTML or delimited data are converted deterministically,
|
| 58 |
+
and a run that loses source numbers falls back to a faithful passthrough of the input.
|
| 59 |
|
| 60 |
Args:
|
| 61 |
text: The document to convert, for example OCR output, pasted HTML, meeting notes or a report.
|
|
|
|
| 94 |
metrics = result["metrics"]
|
| 95 |
numbers = f"{metrics['numbers_matched']}/{metrics['numbers_in']} NUMBERS"
|
| 96 |
overlap = f"{metrics['content_overlap']:.0%} CONTENT"
|
| 97 |
+
routes = {
|
| 98 |
+
"model": "PASITA + RERANK",
|
| 99 |
+
"structured": "HTML/TABLE CONVERTER",
|
| 100 |
+
"passthrough": "SOURCE PASSTHROUGH",
|
| 101 |
+
}
|
| 102 |
+
source_label = routes.get(result.get("route"), "PASITA + RERANK")
|
| 103 |
taken = result["seconds"] if result["seconds"] else time.perf_counter() - started
|
| 104 |
+
detail = f"{source_label} / {numbers} / {overlap} / {taken:.1f}S"
|
| 105 |
return result["markdown"], result["markdown"], _status("DONE", "ready", detail)
|
| 106 |
|
| 107 |
|
|
|
|
| 109 |
return EMPTY_MD, "", _status("READY"), _counter("")
|
| 110 |
|
| 111 |
|
| 112 |
+
BUSY_JS = """(text, num_candidates, structured_fallback, md, source, status) => {
|
| 113 |
+
const n = Number(num_candidates) || 1;
|
| 114 |
+
const label = n > 1 ? 'PASITA ' + n + ' SAMPLES / RERANK' : 'PASITA SAMPLE / RERANK';
|
| 115 |
+
return [md, source,
|
| 116 |
+
'<div class="p-status busy"><span class="p-dot"></span><span class="p-text">CONVERTING</span><span class="p-detail">' + label + '</span></div>'];
|
| 117 |
+
}"""
|
| 118 |
+
|
| 119 |
+
COUNTER_JS = """(text) => {
|
| 120 |
+
const t = (text || '').trim();
|
| 121 |
+
const words = t ? t.split(/\\s+/).length : 0;
|
| 122 |
+
const chars = (text || '').length;
|
| 123 |
+
return '<span class="p-label">PLAIN TEXT</span><span class="p-count">' + chars.toLocaleString() + ' CHARS / ' + words.toLocaleString() + ' WORDS</span>';
|
| 124 |
+
}"""
|
| 125 |
+
|
| 126 |
+
|
| 127 |
HEADER = f"""
|
| 128 |
<header class="p-head">
|
| 129 |
<div class="p-brand">
|
|
|
|
| 249 |
-webkit-font-smoothing: antialiased;
|
| 250 |
}
|
| 251 |
.gradio-container { max-width: 1240px !important; margin: 0 auto; padding: 0 20px 26px; }
|
| 252 |
+
footer[aria-label="Gradio footer navigation"] { display: none !important; }
|
| 253 |
:focus-visible { outline: 2px solid var(--cool) !important; outline-offset: 2px; }
|
| 254 |
|
| 255 |
.p-head {
|
|
|
|
| 305 |
#p-clear { flex: 1; background: transparent !important; color: var(--muted) !important; border: 1px solid var(--hair) !important; }
|
| 306 |
#p-clear:hover { color: var(--ink) !important; border-color: rgba(255,255,255,0.3) !important; }
|
| 307 |
|
| 308 |
+
#p-examples .label { font-family: var(--font-mono) !important; font-size: 0.66rem !important; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted) !important; }
|
| 309 |
+
#p-examples .label svg { display: none; }
|
| 310 |
+
#p-examples .gallery-item {
|
| 311 |
background: var(--panel) !important; border: 1px solid var(--hair) !important; border-radius: 999px !important;
|
| 312 |
+
color: var(--muted) !important; font-size: 12px !important; min-height: 44px; padding: 8px 14px !important;
|
| 313 |
text-align: left; transition: background .2s ease, color .2s ease, border-color .2s ease;
|
| 314 |
}
|
| 315 |
+
#p-examples .gallery-item:hover { border-color: rgba(255,255,255,0.3) !important; color: var(--ink) !important; background: var(--panel-hover) !important; }
|
|
|
|
| 316 |
|
| 317 |
+
#p-tabs .tab-container[role="tablist"] {
|
| 318 |
display: inline-flex; gap: 4px; background: var(--panel); border: 1px solid var(--hair);
|
| 319 |
border-radius: 100px; padding: 3px; margin-bottom: 12px;
|
| 320 |
}
|
| 321 |
+
#p-tabs .tab-container[role="tablist"] button {
|
| 322 |
appearance: none; border: 0 !important; background: transparent !important; border-radius: 100px !important;
|
| 323 |
padding: 7px 14px !important; font-family: var(--font-mono) !important; font-size: 10.5px !important;
|
| 324 |
letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted) !important;
|
| 325 |
}
|
| 326 |
+
#p-tabs .tab-container[role="tablist"] button.selected,
|
| 327 |
+
#p-tabs .tab-container[role="tablist"] button[aria-selected="true"] {
|
| 328 |
background: rgba(255,255,255,0.08) !important; color: var(--warm) !important;
|
| 329 |
}
|
| 330 |
+
#p-tabs .tab-container::after,
|
| 331 |
+
#p-tabs .tab-container button.selected::after { content: none !important; }
|
| 332 |
|
| 333 |
#p-rendered { min-height: 360px; font-size: 0.95rem; line-height: 1.72; color: var(--ink); }
|
| 334 |
+
#p-rendered .prose { font-size: 0.95rem; line-height: 1.72; }
|
| 335 |
#p-rendered h1, #p-rendered h2, #p-rendered h3, #p-rendered h4 { color: #fff; font-weight: 600; line-height: 1.25; margin: 1.25em 0 0.55em; }
|
| 336 |
#p-rendered h1 { font-size: 1.5em; }
|
| 337 |
#p-rendered h2 { font-size: 1.28em; }
|
|
|
|
| 355 |
background: var(--obsidian) !important; border: 1px solid var(--hair-soft) !important;
|
| 356 |
border-radius: 12px !important; font-family: var(--font-mono) !important; min-height: 360px;
|
| 357 |
}
|
| 358 |
+
#p-source label[data-testid="block-label"] { display: none; }
|
| 359 |
+
#p-source .cm-editor { background: transparent !important; }
|
| 360 |
+
#p-source .cm-content, #p-source .cm-scroller { color: var(--ink) !important; caret-color: var(--warm) !important; }
|
| 361 |
+
#p-source .cm-gutters { background: transparent !important; color: var(--muted-2) !important; border-right: 1px solid var(--hair-soft) !important; }
|
| 362 |
|
| 363 |
#p-settings { background: transparent !important; border: 1px solid var(--hair-soft) !important; border-radius: 12px !important; margin-top: 4px; }
|
| 364 |
+
#p-settings > .label-wrap { font-family: var(--font-mono) !important; font-size: 0.68rem !important; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted) !important; }
|
| 365 |
+
#p-settings > .label-wrap .icon { color: var(--muted-2) !important; }
|
| 366 |
#p-settings .p-label { margin-bottom: 4px; }
|
| 367 |
|
| 368 |
.p-foot {
|
|
|
|
| 406 |
text_in = gr.Textbox(
|
| 407 |
show_label=False,
|
| 408 |
container=False,
|
| 409 |
+
placeholder="Paste OCR output, pasted HTML, meeting notes, a report or an article... (SHIFT + ENTER to convert)",
|
| 410 |
lines=18,
|
| 411 |
+
max_lines=32,
|
| 412 |
elem_id="p-text",
|
| 413 |
)
|
| 414 |
with gr.Accordion("SETTINGS", open=False, elem_id="p-settings"):
|
|
|
|
| 427 |
clear = gr.Button("CLEAR", elem_id="p-clear")
|
| 428 |
with gr.Column(scale=1, min_width=320, elem_classes=["p-panel"]):
|
| 429 |
with gr.Tabs(elem_id="p-tabs"):
|
| 430 |
+
with gr.Tab("RENDERED", elem_id="p-tab-rendered"):
|
| 431 |
+
md_out = gr.Markdown(EMPTY_MD, buttons=["copy"], height=520, elem_id="p-rendered")
|
| 432 |
+
with gr.Tab("SOURCE", elem_id="p-tab-source"):
|
| 433 |
+
source = gr.Code(
|
| 434 |
+
value="", language="markdown", buttons=["copy", "download"],
|
| 435 |
+
wrap_lines=True, lines=24, max_lines=24, elem_id="p-source",
|
| 436 |
+
)
|
| 437 |
|
| 438 |
gr.Examples(
|
| 439 |
examples=EXAMPLES,
|
| 440 |
+
example_labels=["ES REPORT", "EN MEETING", "HTML", "CSV"],
|
| 441 |
inputs=[text_in],
|
| 442 |
outputs=[md_out, source, status],
|
| 443 |
fn=convert,
|
|
|
|
| 449 |
)
|
| 450 |
gr.HTML(FOOTER)
|
| 451 |
|
| 452 |
+
run.click(
|
| 453 |
+
convert,
|
| 454 |
+
inputs=[text_in, candidates, structured],
|
| 455 |
+
outputs=[md_out, source, status],
|
| 456 |
+
api_name="convert",
|
| 457 |
+
js=BUSY_JS,
|
| 458 |
+
show_progress="minimal",
|
| 459 |
+
)
|
| 460 |
+
text_in.submit(
|
| 461 |
+
convert,
|
| 462 |
+
inputs=[text_in, candidates, structured],
|
| 463 |
+
outputs=[md_out, source, status],
|
| 464 |
+
api_visibility="undocumented",
|
| 465 |
+
js=BUSY_JS,
|
| 466 |
+
show_progress="minimal",
|
| 467 |
+
)
|
| 468 |
+
text_in.change(None, inputs=[text_in], outputs=[label_in], js=COUNTER_JS, api_visibility="undocumented")
|
| 469 |
+
clear.click(reset, inputs=None, outputs=[md_out, source, status, label_in], api_visibility="undocumented")
|
| 470 |
|
| 471 |
demo.queue()
|
| 472 |
demo.launch(theme=theme, css=CSS, mcp_server=True)
|