ocr: ship an engine in the image, and open the gate in front of it

The OCR pod image installed NO OCR engine — only transformers and pypdfium2. A
pod could boot, pass its health check, accept a request and have nothing to do
it with. Three gates in front of that each reported their own refusal first —
'OCR subsystem is disabled', then 'OCR engine surya is not enabled' — so it took
three rounds to see that the engine was never there.

docTR goes in, because it is the only one of the three that runs in-process.
Surya caps pillow<11 and lives in an isolated venv by design; paddle wants
paddlepaddle in one of its own. Either would put a second torch in the image —
6 GB to avoid a Pillow conflict — and the venv mechanism is there if a pod ever
needs Surya's layout quality specifically.

Each engine also has its own enable flag on top of the subsystem's, and two of
the three default to off. A pod now enables the engine it was asked for, falls
back to docTR when the request names something the image cannot serve, and still
inherits the Surya licence decision from here rather than making it itself.
Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182DhR1eFNPQyGmDrpHbedY
parent 0fba12e0
...@@ -609,8 +609,13 @@ def _plan(engine, image, args, mcfg, api_key, entry, served, ...@@ -609,8 +609,13 @@ def _plan(engine, image, args, mcfg, api_key, entry, served,
if cap == "ocr": if cap == "ocr":
env["CODERAI_OCR_ENABLED"] = "1" env["CODERAI_OCR_ENABLED"] = "1"
wanted = str((entry or {}).get("path") or "").strip().lower() wanted = str((entry or {}).get("path") or "").strip().lower()
if wanted in ("paddle", "doctr", "surya"): if wanted not in ("paddle", "doctr", "surya"):
env["CODERAI_OCR_DEFAULT_ENGINE"] = wanted # The pod images ship docTR, the only engine that runs in-process.
wanted = "doctr"
env["CODERAI_OCR_DEFAULT_ENGINE"] = wanted
# The subsystem flag is not enough: each engine has a gate of its own,
# and doctr and surya both default to off.
env[f"CODERAI_OCR_{wanted.upper()}_ENABLED"] = "1"
if wanted == "surya" or _surya_accepted(): if wanted == "surya" or _surya_accepted():
# Surya is GPL and gated on an explicit acceptance. The pod inherits # Surya is GPL and gated on an explicit acceptance. The pod inherits
# the decision made HERE — it cannot make it for itself, and asking # the decision made HERE — it cannot make it for itself, and asking
......
...@@ -1339,6 +1339,13 @@ class ConfigManager: ...@@ -1339,6 +1339,13 @@ class ConfigManager:
engine = (_os.environ.get("CODERAI_OCR_DEFAULT_ENGINE") or "").strip() engine = (_os.environ.get("CODERAI_OCR_DEFAULT_ENGINE") or "").strip()
if engine: if engine:
self.config.ocr.default_engine = engine self.config.ocr.default_engine = engine
# Each engine has its OWN gate on top of the subsystem's, and two of
# the three default to off. Enabling the subsystem alone got a pod as
# far as "OCR engine 'surya' is not enabled" — a second refusal from
# a second flag, one round later.
for name in ("paddle", "doctr", "surya"):
if _flag(f"CODERAI_OCR_{name.upper()}_ENABLED"):
setattr(self.config.ocr, f"{name}_enabled", True)
if _flag("CODERAI_OCR_SURYA_ACCEPT_LICENSE"): if _flag("CODERAI_OCR_SURYA_ACCEPT_LICENSE"):
# Surya is GPL and gated on an explicit acceptance. The pod # Surya is GPL and gated on an explicit acceptance. The pod
# inherits the decision made here; it cannot make it itself. # inherits the decision made here; it cannot make it itself.
......
# /v1/ocr — see also requirements-ocr.txt / requirements-surya.txt for the # /v1/ocr
# engine-specific extras a given OCR backend needs. #
# docTR, in this venv. The image used to install NO OCR engine at all — only
# transformers and pypdfium2 — so an OCR pod could boot, pass its health check,
# accept a request and then have nothing to do it with. Three separate gates
# (subsystem enabled, engine enabled, licence accepted) reported their own
# refusal first, which hid the real problem for three rounds.
#
# docTR and not the others, because it is the only one that runs IN-PROCESS:
# Surya caps pillow<11 and lives in an isolated venv by design, and paddle wants
# paddlepaddle in one of its own. Either would mean a second torch in the image
# — 6 GB to avoid a Pillow conflict. Surya remains available on a local install,
# and can be baked in later with the <profile>.venv-<name>.txt mechanism if a
# pod ever needs its layout quality.
transformers>=4.35.0 transformers>=4.35.0
pypdfium2>=4.20.0 pypdfium2>=4.20.0
python-doctr>=0.8.0
ultralytics>=8.1.0
jsonschema>=4.0.0
...@@ -603,11 +603,23 @@ def test_an_ocr_pod_arrives_with_ocr_switched_on(): ...@@ -603,11 +603,23 @@ def test_an_ocr_pod_arrives_with_ocr_switched_on():
assert env["CODERAI_OCR_DEFAULT_ENGINE"] == "surya" assert env["CODERAI_OCR_DEFAULT_ENGINE"] == "surya"
# Asking for surya by name IS the licence decision; the pod cannot make it. # Asking for surya by name IS the licence decision; the pod cannot make it.
assert env["CODERAI_OCR_SURYA_ACCEPT_LICENSE"] == "1" assert env["CODERAI_OCR_SURYA_ACCEPT_LICENSE"] == "1"
# …and the engine's OWN gate, which is separate from the subsystem's. A pod
# with only the subsystem enabled answered "OCR engine 'surya' is not
# enabled" — a second refusal from a second flag, a round later.
assert env["CODERAI_OCR_SURYA_ENABLED"] == "1"
# A non-gated engine does not carry the licence flag unless we accepted it. # A non-gated engine does not carry the licence flag unless we accepted it.
env = pod_plan(mcfg, "ocr", entry={"path": "paddle", env = pod_plan(mcfg, "ocr", entry={"path": "paddle",
"model_type": "ocr_models"})["env"] "model_type": "ocr_models"})["env"]
assert env["CODERAI_OCR_DEFAULT_ENGINE"] == "paddle" assert env["CODERAI_OCR_DEFAULT_ENGINE"] == "paddle"
assert env["CODERAI_OCR_PADDLE_ENABLED"] == "1"
# An unnamed engine falls to docTR: the only one the images ship, because it
# is the only one that runs in-process. The others need a venv of their own.
env = pod_plan(mcfg, "ocr", entry={"path": "whatever",
"model_type": "ocr_models"})["env"]
assert env["CODERAI_OCR_DEFAULT_ENGINE"] == "doctr"
assert env["CODERAI_OCR_DOCTR_ENABLED"] == "1"
# And a pod for anything else is untouched. # And a pod for anything else is untouched.
env = pod_plan(mcfg, "images", entry={"path": "org/sd", env = pod_plan(mcfg, "images", entry={"path": "org/sd",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment