Use dynamic ZeroGPU duration for PDF batches
Browse files- README.md +9 -4
- app.py +28 -1
- dist/assets/{index-BuDpKXhW.js → index-uj7s2XQX.js} +0 -0
- dist/index.html +1 -1
README.md
CHANGED
|
@@ -27,9 +27,11 @@ visual-region crops. Model tokens stream into the interface as they are generate
|
|
| 27 |
PDFs are rasterized locally and processed one page at a time in Base mode. Up to
|
| 28 |
four pages share one streamed GPU request by default, while each page is still
|
| 29 |
inferred independently and sequentially. The browser merges the results, so long
|
| 30 |
-
documents do not have to fit inside one ZeroGPU lease.
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
| 33 |
intentionally does not expose a separate Long mode.
|
| 34 |
|
| 35 |
The five packaged examples cover two financial tables, two Chinese handwriting
|
|
@@ -107,7 +109,10 @@ completed pages.
|
|
| 107 |
- `OVISOCR_PDF_RENDER_SCALE`: PDF rasterization scale, defaults to `2.0`.
|
| 108 |
- `OVISOCR_STREAM_MIN_CHARS`: minimum new characters between stream updates, defaults to `64`.
|
| 109 |
- `OVISOCR_STREAM_MAX_INTERVAL`: maximum seconds between available stream updates, defaults to `0.25`.
|
| 110 |
-
- `
|
|
|
|
|
|
|
|
|
|
| 111 |
- `OVISOCR_ATTN_IMPLEMENTATION`: defaults to `sdpa`.
|
| 112 |
- `OVISOCR_DSW_ID`: generates the Hangzhou DSW proxy root using the active `PORT`.
|
| 113 |
- `OVISOCR_ROOT_PATH`: explicit proxy URL or ASGI path prefix; overrides `OVISOCR_DSW_ID`.
|
|
|
|
| 27 |
PDFs are rasterized locally and processed one page at a time in Base mode. Up to
|
| 28 |
four pages share one streamed GPU request by default, while each page is still
|
| 29 |
inferred independently and sequentially. The browser merges the results, so long
|
| 30 |
+
documents do not have to fit inside one ZeroGPU lease. GPU duration is estimated
|
| 31 |
+
from the number of pages that actually remain in each group, so a short second
|
| 32 |
+
group does not reserve the same ZeroGPU quota as a four-page group. If a Gradio
|
| 33 |
+
stream closes without an explicit backend error, the browser reconnects from the
|
| 34 |
+
first unfinished page while retaining every completed page. This demo
|
| 35 |
intentionally does not expose a separate Long mode.
|
| 36 |
|
| 37 |
The five packaged examples cover two financial tables, two Chinese handwriting
|
|
|
|
| 109 |
- `OVISOCR_PDF_RENDER_SCALE`: PDF rasterization scale, defaults to `2.0`.
|
| 110 |
- `OVISOCR_STREAM_MIN_CHARS`: minimum new characters between stream updates, defaults to `64`.
|
| 111 |
- `OVISOCR_STREAM_MAX_INTERVAL`: maximum seconds between available stream updates, defaults to `0.25`.
|
| 112 |
+
- `OVISOCR_GPU_SECONDS_PER_PAGE`: duration-estimation budget, defaults to `30` seconds per page.
|
| 113 |
+
- `OVISOCR_GPU_DURATION_FLOOR`: minimum per-group ZeroGPU reservation, defaults to `45` seconds.
|
| 114 |
+
- `OVISOCR_GPU_DURATION_CEILING`: maximum estimated per-group reservation, defaults to `120` seconds.
|
| 115 |
+
- `OVISOCR_GPU_DURATION`: optional fixed per-group override; unset by default so dynamic duration is used.
|
| 116 |
- `OVISOCR_ATTN_IMPLEMENTATION`: defaults to `sdpa`.
|
| 117 |
- `OVISOCR_DSW_ID`: generates the Hangzhou DSW proxy root using the active `PORT`.
|
| 118 |
- `OVISOCR_ROOT_PATH`: explicit proxy URL or ASGI path prefix; overrides `OVISOCR_DSW_ID`.
|
app.py
CHANGED
|
@@ -56,6 +56,12 @@ MAX_PDF_PAGES = int(os.getenv("OVISOCR_MAX_PDF_PAGES", "50"))
|
|
| 56 |
PAGES_PER_GPU_REQUEST = max(
|
| 57 |
1, min(5, int(os.getenv("OVISOCR_PAGES_PER_GPU_REQUEST", "4")))
|
| 58 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
PDF_RENDER_SCALE = float(os.getenv("OVISOCR_PDF_RENDER_SCALE", "2.0"))
|
| 60 |
STREAM_MIN_CHARS = int(os.getenv("OVISOCR_STREAM_MIN_CHARS", "64"))
|
| 61 |
STREAM_MAX_INTERVAL = float(os.getenv("OVISOCR_STREAM_MAX_INTERVAL", "0.25"))
|
|
@@ -461,7 +467,25 @@ def _gpu_duration(
|
|
| 461 |
image_path: FileData | dict[str, Any], page_index: int = 0,
|
| 462 |
page_count: int = PAGES_PER_GPU_REQUEST,
|
| 463 |
) -> int:
|
| 464 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 465 |
|
| 466 |
|
| 467 |
_load_model()
|
|
@@ -606,6 +630,9 @@ def healthz() -> JSONResponse:
|
|
| 606 |
"loaded": TEST_MODE or (processor is not None and model is not None),
|
| 607 |
"max_pdf_pages": MAX_PDF_PAGES,
|
| 608 |
"pages_per_gpu_request": PAGES_PER_GPU_REQUEST,
|
|
|
|
|
|
|
|
|
|
| 609 |
"root_path": ROOT_PATH,
|
| 610 |
"public_url": PUBLIC_URL,
|
| 611 |
}
|
|
|
|
| 56 |
PAGES_PER_GPU_REQUEST = max(
|
| 57 |
1, min(5, int(os.getenv("OVISOCR_PAGES_PER_GPU_REQUEST", "4")))
|
| 58 |
)
|
| 59 |
+
GPU_SECONDS_PER_PAGE = max(15, int(os.getenv("OVISOCR_GPU_SECONDS_PER_PAGE", "30")))
|
| 60 |
+
GPU_DURATION_FLOOR = max(15, int(os.getenv("OVISOCR_GPU_DURATION_FLOOR", "45")))
|
| 61 |
+
GPU_DURATION_CEILING = max(
|
| 62 |
+
GPU_DURATION_FLOOR,
|
| 63 |
+
int(os.getenv("OVISOCR_GPU_DURATION_CEILING", "120")),
|
| 64 |
+
)
|
| 65 |
PDF_RENDER_SCALE = float(os.getenv("OVISOCR_PDF_RENDER_SCALE", "2.0"))
|
| 66 |
STREAM_MIN_CHARS = int(os.getenv("OVISOCR_STREAM_MIN_CHARS", "64"))
|
| 67 |
STREAM_MAX_INTERVAL = float(os.getenv("OVISOCR_STREAM_MAX_INTERVAL", "0.25"))
|
|
|
|
| 467 |
image_path: FileData | dict[str, Any], page_index: int = 0,
|
| 468 |
page_count: int = PAGES_PER_GPU_REQUEST,
|
| 469 |
) -> int:
|
| 470 |
+
configured_duration = os.getenv("OVISOCR_GPU_DURATION", "").strip()
|
| 471 |
+
if configured_duration:
|
| 472 |
+
return int(configured_duration)
|
| 473 |
+
|
| 474 |
+
requested_count = max(1, min(PAGES_PER_GPU_REQUEST, int(page_count)))
|
| 475 |
+
try:
|
| 476 |
+
path = _file_path(image_path)
|
| 477 |
+
_, total_pages = document_info(path)
|
| 478 |
+
remaining_pages = max(1, total_pages - int(page_index))
|
| 479 |
+
requested_count = min(requested_count, remaining_pages)
|
| 480 |
+
except Exception:
|
| 481 |
+
# Duration estimation must never prevent a request from reaching the
|
| 482 |
+
# endpoint. The endpoint performs the authoritative file validation.
|
| 483 |
+
pass
|
| 484 |
+
|
| 485 |
+
return max(
|
| 486 |
+
GPU_DURATION_FLOOR,
|
| 487 |
+
min(GPU_DURATION_CEILING, requested_count * GPU_SECONDS_PER_PAGE),
|
| 488 |
+
)
|
| 489 |
|
| 490 |
|
| 491 |
_load_model()
|
|
|
|
| 630 |
"loaded": TEST_MODE or (processor is not None and model is not None),
|
| 631 |
"max_pdf_pages": MAX_PDF_PAGES,
|
| 632 |
"pages_per_gpu_request": PAGES_PER_GPU_REQUEST,
|
| 633 |
+
"gpu_seconds_per_page": GPU_SECONDS_PER_PAGE,
|
| 634 |
+
"gpu_duration_floor": GPU_DURATION_FLOOR,
|
| 635 |
+
"gpu_duration_ceiling": GPU_DURATION_CEILING,
|
| 636 |
"root_path": ROOT_PATH,
|
| 637 |
"public_url": PUBLIC_URL,
|
| 638 |
}
|
dist/assets/{index-BuDpKXhW.js → index-uj7s2XQX.js}
RENAMED
|
The diff for this file is too large to render.
See raw diff
|
|
|
dist/index.html
CHANGED
|
@@ -22,7 +22,7 @@
|
|
| 22 |
};
|
| 23 |
</script>
|
| 24 |
<script defer src="./vendor/mathjax/tex-chtml-full.js"></script>
|
| 25 |
-
<script type="module" crossorigin src="./assets/index-
|
| 26 |
<link rel="stylesheet" crossorigin href="./assets/index-CQtDzYS-.css">
|
| 27 |
</head>
|
| 28 |
<body>
|
|
|
|
| 22 |
};
|
| 23 |
</script>
|
| 24 |
<script defer src="./vendor/mathjax/tex-chtml-full.js"></script>
|
| 25 |
+
<script type="module" crossorigin src="./assets/index-uj7s2XQX.js"></script>
|
| 26 |
<link rel="stylesheet" crossorigin href="./assets/index-CQtDzYS-.css">
|
| 27 |
</head>
|
| 28 |
<body>
|