runpod+ocr: fill a warm floor in waves, and put the OCR pipeline on the pod image (v0.3.69)
THE WARM FLOOR. A declared floor of 8 took ~40 minutes to exist. Measured on the
Digesta orchestrator, one pod per scaler pass at ~5 minutes each:
15:15:27 warm floor 8, 2 pod(s) healthy — renting one
15:21:12 warm floor 8, 3 pod(s) healthy — renting one
15:32:41 warm floor 8, 4 pod(s) healthy — renting one
Two causes. First, `maintain()` rented exactly one pod per pass, gated on
`not self._booting`. A floor is a DECLARATION, not a prediction -- min_pods,
keep_warm or a client lease already said how many should be warm -- so the
deficit is filled at once, bounded by the model's existing
`scale_up_max_parallel_boots` (no new knob; it already means "how many pods may
be booting at once"). Second, `_provision_one` is "Blocking (minutes)" and this
called it INLINE from the scaler loop, so one warm boot stalled that loop --
including the registry heartbeat that lives in it and runs every 60s. A stalled
heartbeat is exactly what makes a sibling treat our booting pod as abandoned,
which 0.3.68 made load-bearing. Warm boots now run on their own threads.
Adoption loops too (a restart orphans the whole fleet; eleven pods at one per
tick took six minutes), but BOUNDED two ways: `_adopt_shared_pod()` returning
True is a report, not a guarantee -- it can succeed without the pool gaining a
healthy pod -- and the first `while ... and adopt()` form span forever the first
time that happened, taking the scaler thread and the heartbeat with it. At most
`deficit` attempts, and stop when an adoption does not raise the count. A pass
that adopted rents nothing: renting beside a fresh adoption is how one job pays
for two cards.
THE IMAGE. coderai-vllm-ocr existed to pin vLLM 0.20.1, the version surya
spawns, because a page of crisp Italian came back from 0.27.1 with one U+FFFD
per accented letter. That was the wrong conclusion from a real measurement:
surya-ocr-2's tokenizer is BYTE-level, and the fleet has been reading accented
Italian on 0.27.1 ever since. Two images serving the same weights on two vLLMs
is an invisible difference, and it cost three rounds of blaming the pod image.
So the pins are now IDENTICAL and the check READS the fleet's pin from disk
rather than restating a version -- bumping profiles/vllm cannot silently leave
this image behind.
What distinguishes the image instead is the OCR PIPELINE on the pod: the surya
client venv beside the model, plus pypdfium2 in the main venv, so a page is
rasterised and read on the card rather than crossing the network as a ~1 MB
base64 PNG (and four more times over for a page resumed four times).
CPU TORCH, and it is the whole image-size story. Measured in a resolved surya
venv: nvidia 3196 MB + torch 1175 MB + triton 896 MB = ~5.3 GB of CUDA stack,
for a venv whose only local torch workload is surya's small line detector. On
the pod the GPU is already committed to serving the model, so giving the
detector the card would take VRAM and SMs from the thing that matters; on a
GPU-less orchestrator it can never execute at all (cuda available: False today
while its surya venv carries cu130). ~280 MB instead of ~5.3 GB, and the venv
check asserts torch.version.cuda is None so a resolve cannot quietly add 5 GB
back.
THE DETECTOR rides that same CPU torch, which is why it costs ~30 MB rather
than a third torch. detect.py tries ultralytics in-process first (every existing
install is unchanged) and falls back to codai/ocr/workers/detect_worker.py in
whichever venv has it -- surya's first. A detector that cannot be reached looks
like a MISSING detector (the ordinary 503), never a broken install. No weights
are baked: detect_model_path defaults to "" and a released image must contain no
models, configs, keys or secrets.
Tests: 17 warm-floor (+4 for the spin), 14 detector. Four older tests encoded
the decisions this reverses -- two asserted the 0.20.1 pin (replaced by one
requiring the two pin files to be identical), one asserted the warm floor "must
still be single-flight", one counted a source string globally and could not tell
intent-held-spelling-changed from a regression (now per path). Suite at the 25
pre-existing failures.
NOT BUILT and NOT USABLE YET: nothing has resolved surya-ocr + pillow<11 +
torch+cpu + ultralytics together, and the orchestrator cannot drive a pod-side
pipeline until the dual mode lands.
Co-Authored-By:
Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXRMe9ZPUQCCCtBai9uqt3
Showing
This diff is collapsed.
Please
register
or
sign in
to comment