provinans / src /endopath /gpu_backend.py
reversely's picture
Upload folder using huggingface_hub
4b059c7 verified
Raw History Blame Contribute Delete
3.95 kB
"""The on-prem GPU backend: open weights served on vLLM, for hosts with a GPU and no hosted API (#83).
Two stages, two models, both reached through the provider seam so no call site changes:
- Page transcription. `reocr.py` sends each page image to a vision model. olmOCR-2-7B is the default,
a 7B model trained for document transcription, served on its own vLLM endpoint.
- Extraction. `llm_extraction.py` forces one tool call whose schema `fields.py` derives. A larger
instruct model answers it, and vLLM constrains generation to that schema with xgrammar, so the output
cannot take a shape the checklist rejects.
Configured with four environment variables. `VLLM_URL` and `VLLM_MODEL` name the extractor.
`VLLM_REOCR_URL` and `VLLM_REOCR_MODEL` name the transcription endpoint, each falling back to the
extractor's value and the olmOCR default, so a single server hosting one model still works.
The backend reports unavailable until `VLLM_URL` and `VLLM_MODEL` are set, so the settings list disables
it on a host with no server rather than letting a switch fail at extraction (#94).
"""
from __future__ import annotations
import os
from typing import Any, Optional
from endopath import llm
GPU_VLLM = "gpu_vllm"
URL_ENV = "VLLM_URL"
MODEL_ENV = "VLLM_MODEL"
REOCR_URL_ENV = "VLLM_REOCR_URL"
REOCR_MODEL_ENV = "VLLM_REOCR_MODEL"
# The transcription model the ticket names. Overridable, so a host serving a different vision model
# points at it without a code change.
DEFAULT_REOCR_MODEL = "allenai/olmOCR-2-7B-1025"
# vLLM's constrained-decoding backend. The extraction request sends the checklist schema as `guided_json`
# alongside this, so the server admits only tokens the schema allows.
GUIDED_DECODING = "xgrammar"
def _env(name: str) -> str:
return (os.environ.get(name) or "").strip()
def _require(name: str) -> str:
value = _env(name)
if not value:
raise RuntimeError(
f"{name} is not set. The GPU backend serves open weights on vLLM: set {URL_ENV} and "
f"{MODEL_ENV} to the extractor endpoint, and optionally {REOCR_URL_ENV} and "
f"{REOCR_MODEL_ENV} for a separate transcription endpoint."
)
return value
def build_extraction_client() -> Any:
"""The extractor: a larger instruct model on vLLM, with xgrammar holding its output to the checklist
schema `llm_extraction.build_checklist_tool` derives from `fields.py`."""
return llm.openai_compatible_client(
_require(URL_ENV), _require(MODEL_ENV), guided_decoding=GUIDED_DECODING
)
def build_reocr_client() -> Any:
"""The page-transcription model. Falls back to the extractor's endpoint when only one server runs, so
a single-server host still transcribes."""
base_url = _env(REOCR_URL_ENV) or _require(URL_ENV)
model = _env(REOCR_MODEL_ENV) or DEFAULT_REOCR_MODEL
return llm.openai_compatible_client(base_url, model)
def _extraction_model() -> Optional[str]:
"""The extractor model this backend serves, recorded on the activity a run writes (#151). None when
VLLM_MODEL is unset, because no extraction can have run under it."""
return _env(MODEL_ENV) or None
def _available() -> bool:
"""Whether a vLLM endpoint is configured. The settings list disables the backend until it is, rather
than offering a switch that fails at the first model call."""
return bool(_env(URL_ENV) and _env(MODEL_ENV))
llm.register_backend(
llm.Backend(
GPU_VLLM,
"GPU on-prem (open weights on vLLM)",
needs_key=False,
provider=llm.OPENAI_COMPATIBLE,
build=build_extraction_client,
build_reocr=build_reocr_client,
runs_llm_extraction=True,
extraction_model=_extraction_model,
# No classical page reader: the vision model transcribes the page through the seam, the same way
# the cloud backend does.
page_reader=None,
available=_available,
)
)