diff --git a/packages/sie_server/models/xiaomi-research__Xiaomi-OCR-0.yaml b/packages/sie_server/models/xiaomi-research__Xiaomi-OCR-0.yaml new file mode 100644 index 000000000..aad0189a0 --- /dev/null +++ b/packages/sie_server/models/xiaomi-research__Xiaomi-OCR-0.yaml @@ -0,0 +1,60 @@ +sie_id: xiaomi-research/Xiaomi-OCR-0 +hf_id: xiaomi-research/Xiaomi-OCR-0 +hf_revision: bbd8cf8d21f256b3644b44cb860f3a7cecb93c30 +inputs: + text: false + image: true + audio: false + video: false +tasks: + encode: null + score: null + extract: {} +# A page at the processor's own pixel limit is 16384 visual tokens; the +# window adds the card's 4096 new tokens and the prompt on top. +max_sequence_length: 24576 +profiles: + # Plain Hugging Face reference path (Qwen3_5ForConditionalGeneration, native + # in transformers 5; no remote code). The card decodes greedily with 4096 new + # tokens; the checkpoint's own dtype is bfloat16. + transformers: + max_batch_tokens: 16384 + compute_precision: bfloat16 + adapter_path: sie_server.adapters.glm_ocr:GlmOcrAdapter + adapter_options: + loadtime: + # The model card's document-parsing prompt. + user_text: "Extract all information from the main body of the document image and represent it in markdown format, ignoring headers and footers. Tables should be expressed in OTSL format, formulas in the document should be represented using LATEX format, and the parsing should be organized according to the reading order." + runtime: + max_new_tokens: 4096 + num_beams: 1 + default: + max_batch_tokens: 16384 + compute_precision: bfloat16 + adapter_path: sie_server.adapters.sglang_vision_extract.adapter:SGLangVisionExtractAdapter + adapter_options: + loadtime: + # Native SGLang architecture (Qwen3_5ForConditionalGeneration); never + # execute repository code. + trust_remote_code: false + # Pages in flight, each up to 24576 tokens of page image and output, + # need far more KV than the automatic one-sequence pool of 24576. + # SGLang keeps (1 - fraction) of the memory free at launch as runtime + # headroom. One 16 MP page peaks about 3.5 GiB above the memory in use + # after load; on an L4, 0.85 leaves too little and the engine OOMs. + mem_fraction_static: 0.70 + served_model_name: xiaomi-research/Xiaomi-OCR-0 + # 64 full-size uploads at once outrun the adapter's /generate write + # timeout on an L4 worker; 32 was measured clean. + max_concurrent_requests: 32 + # Model card prompts. The default is document parsing; callers pick + # another region type with the runtime ``task`` option. + default_task: document + task_prompts: + document: "Extract all information from the main body of the document image and represent it in markdown format, ignoring headers and footers. Tables should be expressed in OTSL format, formulas in the document should be represented using LATEX format, and the parsing should be organized according to the reading order." + text: "Extract the text in the image." + table: "Parse the table in the image into OTSL." + formula: "Identify the formula in the image and represent it using LATEX format." + runtime: + max_new_tokens: 4096 + num_beams: 1 diff --git a/packages/sie_server/tests/config/test_bundle_coverage.py b/packages/sie_server/tests/config/test_bundle_coverage.py index 09651d712..09778b47a 100644 --- a/packages/sie_server/tests/config/test_bundle_coverage.py +++ b/packages/sie_server/tests/config/test_bundle_coverage.py @@ -148,6 +148,7 @@ def test_candle_bundle_only_exposes_profile_variants() -> None: ("lightonai/LightOnOCR-2-1B", "transformers5.yaml"), ("PaddlePaddle/PaddleOCR-VL-1.5", "default.yaml"), ("zai-org/GLM-OCR", "transformers5.yaml"), + ("xiaomi-research/Xiaomi-OCR-0", "transformers5.yaml"), ], ) def test_generative_ocr_defaults_to_isolated_sglang_extract_and_keeps_transformers_fallback( diff --git a/packages/sie_server/tests/config/test_xiaomi_ocr_profiles.py b/packages/sie_server/tests/config/test_xiaomi_ocr_profiles.py new file mode 100644 index 000000000..43792eaf3 --- /dev/null +++ b/packages/sie_server/tests/config/test_xiaomi_ocr_profiles.py @@ -0,0 +1,43 @@ +"""Xiaomi-OCR-0 serves native weights with the model card's prompts.""" + +from pathlib import Path + +import yaml + +_CONFIG = Path(__file__).resolve().parents[2] / "models" / "xiaomi-research__Xiaomi-OCR-0.yaml" +_PROCESSOR_MAX_PAGE_TOKENS = 16_777_216 // (32 * 32) +_PROMPT_TOKENS = 256 + + +def _profiles() -> dict: + return yaml.safe_load(_CONFIG.read_text())["profiles"] + + +def test_sglang_profile_never_trusts_repository_code() -> None: + default = _profiles()["default"] + assert default["adapter_path"].endswith(":SGLangVisionExtractAdapter") + assert default["adapter_options"]["loadtime"]["trust_remote_code"] is False + + +def test_both_profiles_default_to_the_same_card_prompt_and_decoding() -> None: + profiles = _profiles() + reference = profiles["transformers"]["adapter_options"] + sglang = profiles["default"]["adapter_options"] + task = sglang["loadtime"]["default_task"] + assert sglang["loadtime"]["task_prompts"][task] == reference["loadtime"]["user_text"] + assert sglang["runtime"] == reference["runtime"] == {"max_new_tokens": 4096, "num_beams": 1} + + +def test_a_full_resolution_page_and_its_output_fit_the_sequence_window() -> None: + config = yaml.safe_load(_CONFIG.read_text()) + for profile in config["profiles"].values(): + loadtime = profile["adapter_options"]["loadtime"] + assert "--mm-process-config" not in loadtime.get("extra_launch_args", []) + budget = _PROCESSOR_MAX_PAGE_TOKENS + profile["adapter_options"]["runtime"]["max_new_tokens"] + _PROMPT_TOKENS + assert budget <= config["max_sequence_length"] + + +def test_sglang_profile_keeps_its_l4_calibrated_reservation() -> None: + loadtime = _profiles()["default"]["adapter_options"]["loadtime"] + assert loadtime["mem_fraction_static"] == 0.70 + assert loadtime["max_concurrent_requests"] == 32