Files
wehub-resource-sync 461bf6fd40
CI / lint (3.11) (push) Has been cancelled
CI / lint (3.12) (push) Has been cancelled
CI / lint (3.13) (push) Has been cancelled
CI / shellcheck (push) Has been cancelled
CI / shfmt (push) Has been cancelled
CI / setup (3.11) (push) Has been cancelled
CI / setup (3.12) (push) Has been cancelled
CI / setup (3.13) (push) Has been cancelled
CI / check-licenses (3.12) (push) Has been cancelled
CI / test_unit (3.11) (push) Has been cancelled
CI / test_unit (3.12) (push) Has been cancelled
CI / test_unit (3.13) (push) Has been cancelled
CI / test_unit_no_extras (3.11) (push) Has been cancelled
CI / test_unit_no_extras (3.12) (push) Has been cancelled
CI / test_json_to_html (3.12) (push) Has been cancelled
CI / test_unit_no_extras (3.13) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.12, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.11, --extra xlsx) (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.12, --extra xlsx) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.11, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.13, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.11, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.12, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.13, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.11, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.12, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.13, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.11, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.12, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.13, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.11, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.12, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.13, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.11, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.12, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.13, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.11, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.12, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.13, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
Build And Push Docker Image / set-short-sha (push) Has been cancelled
Partition Benchmark / setup (push) Has been cancelled
Partition Benchmark / Measure and compare partition() runtime (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.13, --extra xlsx) (push) Has been cancelled
CI / test_ingest_src (3.12) (push) Has been cancelled
CI / test_json_to_markdown (3.12) (push) Has been cancelled
CI / changelog (push) Has been cancelled
CI / test_dockerfile (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build And Push Docker Image / build-images (linux/amd64, opensource-linux-8core) (push) Has been cancelled
Build And Push Docker Image / build-images (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Build And Push Docker Image / publish-images (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:33:56 +08:00

325 lines
13 KiB
Python

"""
This module contains variables that can permitted to be tweaked by the system environment. For
example, model parameters that changes the output of an inference call. Constants do NOT belong in
this module. Constants are values that are usually names for common options (e.g., color names) or
settings that should not be altered without making a code change (e.g., definition of 1Gb of memory
in bytes). Constants should go into `./constants.py`
"""
import os
import tempfile
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from typing import Optional
from unstructured.partition.utils.constants import OCR_AGENT_TESSERACT, STT_AGENT_WHISPER
@lru_cache(maxsize=1)
def get_tempdir(dir: str) -> str:
tempdir = Path(dir) / f"tmp/{os.getpgid(0)}"
return str(tempdir)
@dataclass
class ENVConfig:
"""class for configuring enviorment parameters"""
def __post_init__(self):
if self.GLOBAL_WORKING_DIR_ENABLED:
self._setup_tmpdir(self.GLOBAL_WORKING_PROCESS_DIR)
def _get_string(self, var: str, default_value: str = "") -> str:
"""attempt to get the value of var from the os environment; if not present return the
default_value"""
return os.environ.get(var, default_value)
def _get_int(self, var: str, default_value: int) -> int:
if value := self._get_string(var):
return int(value)
return default_value
def _get_float(self, var: str, default_value: float) -> float:
if value := self._get_string(var):
return float(value)
return default_value
def _get_bool(self, var: str, default_value: bool) -> bool:
if value := self._get_string(var):
return value.lower() in ("true", "1", "t")
return default_value
def _setup_tmpdir(self, tmpdir: str) -> None:
Path(tmpdir).mkdir(parents=True, exist_ok=True)
tempfile.tempdir = tmpdir
@property
def IMAGE_CROP_PAD(self) -> int:
"""extra image content to add around an identified element region; measured in pixels"""
return self._get_int("IMAGE_CROP_PAD", 0)
@property
def TABLE_IMAGE_CROP_PAD(self) -> int:
"""extra image content to add around an identified table region; measured in pixels
The padding adds image data around an identified table bounding box for downstream table
structure detection model use as input
"""
return self._get_int("TABLE_IMAGE_CROP_PAD", 0)
@property
def TESSERACT_TEXT_HEIGHT_QUANTILE(self) -> float:
"""the quantile to check for text height"""
return self._get_float("TESSERACT_TEXT_HEIGHT_QUANTILE", 0.5)
@property
def TESSERACT_MIN_TEXT_HEIGHT(self) -> int:
"""minimum text height acceptable from tesseract OCR results
if estimated text height from tesseract OCR results is lower than this value the image is
scaled up to be processed again
"""
return self._get_int("TESSERACT_MIN_TEXT_HEIGHT", 12)
@property
def TESSERACT_MAX_TEXT_HEIGHT(self) -> int:
"""maximum text height acceptable from tesseract OCR results
if estimated text height from tesseract OCR results is higher than this value the image is
scaled down to be processed again
"""
return self._get_int("TESSERACT_MAX_TEXT_HEIGHT", 100)
@property
def TESSERACT_OPTIMUM_TEXT_HEIGHT(self) -> int:
"""optimum text height for tesseract OCR"""
return self._get_int("TESSERACT_OPTIMUM_TEXT_HEIGHT", 20)
@property
def TESSERACT_CHARACTER_CONFIDENCE_THRESHOLD(self) -> int:
"""Tesseract predictions with confidence below this threshold are ignored"""
return self._get_float("TESSERACT_CHARACTER_CONFIDENCE_THRESHOLD", 0.0)
@property
def GOOGLEVISION_API_ENDPOINT(self) -> str:
"""API endpoint to use for Google Vision"""
return self._get_string("GOOGLEVISION_API_ENDPOINT", "")
@property
def OCR_AGENT(self) -> str:
"""OCR Agent to use"""
return self._get_string("OCR_AGENT", OCR_AGENT_TESSERACT)
@property
def OCR_AGENT_CACHE_SIZE(self) -> int:
"""Maximum number of OCR agents to cache per process"""
return self._get_int("OCR_AGENT_CACHE_SIZE", 1)
@property
def STT_AGENT_CACHE_SIZE(self) -> int:
"""Maximum number of speech-to-text agents to cache per process."""
return self._get_int("STT_AGENT_CACHE_SIZE", 1)
@property
def STT_AGENT(self) -> str:
"""Speech-to-text agent module for partition_audio (e.g. Whisper)."""
return self._get_string("STT_AGENT", STT_AGENT_WHISPER)
@property
def WHISPER_MODEL_SIZE(self) -> str:
"""Whisper model size for SpeechToTextAgentWhisper.
One of: tiny, base, small, medium, large, large-v3.
Larger models are more accurate but slower and use more memory (~1GB VRAM for tiny
to ~10GB for large-v3). Default is \"base\".
"""
return self._get_string("WHISPER_MODEL_SIZE", "base")
@property
def WHISPER_DEVICE(self) -> str:
"""Device for Whisper model (e.g. \"cuda\", \"cpu\", \"cuda:0\").
When empty or not set, Whisper auto-selects (CUDA if available, else CPU).
Set to \"cpu\" to force CPU; set to \"cuda\" or \"cuda:0\" for GPU.
"""
return self._get_string("WHISPER_DEVICE", "")
@property
def WHISPER_FP16(self) -> bool:
"""Use FP16 for Whisper transcription.
FP16 gives roughly 2x GPU speedup on CUDA with minimal quality impact, but is
unsupported on CPU and will raise a RuntimeError there. The default is auto-detected:
True when a CUDA GPU is available, False otherwise.
Set WHISPER_FP16=true/false explicitly to override. When set to empty, treated as false.
"""
if "WHISPER_FP16" not in os.environ:
try:
import torch
return bool(torch.cuda.is_available())
except ImportError:
return False
return self._get_bool("WHISPER_FP16", False)
@property
def EXTRACT_IMAGE_BLOCK_CROP_HORIZONTAL_PAD(self) -> int:
"""extra image block content to add around an identified element(`Image`, `Table`) region
horizontally; measured in pixels
"""
return self._get_int("EXTRACT_IMAGE_BLOCK_CROP_HORIZONTAL_PAD", 0)
@property
def EXTRACT_IMAGE_BLOCK_CROP_VERTICAL_PAD(self) -> int:
"""extra image block content to add around an identified element(`Image`, `Table`) region
vertically; measured in pixels
"""
return self._get_int("EXTRACT_IMAGE_BLOCK_CROP_VERTICAL_PAD", 0)
@property
def EXTRACT_TABLE_AS_CELLS(self) -> bool:
"""adds `table_as_cells` to a Table element's metadata when it is True"""
return self._get_bool("EXTRACT_TABLE_AS_CELLS", False)
@property
def OCR_LAYOUT_SUBREGION_THRESHOLD(self) -> float:
"""threshold to determine if an OCR region is a sub-region of a given block
when aggregating the text from OCR'd elements that lie within the given block
When the intersection region area divided by self area is larger than this threshold self is
considered a subregion of the other
"""
return self._get_float("OCR_LAYOUT_SUBREGION_THRESHOLD", 0.5)
@property
def EMBEDDED_IMAGE_SAME_REGION_THRESHOLD(self) -> float:
"""threshold to consider the bounding boxes of two embedded images as the same region"""
return self._get_float("EMBEDDED_IMAGE_SAME_REGION_THRESHOLD", 0.6)
@property
def EMBEDDED_TEXT_AGGREGATION_SUBREGION_THRESHOLD(self) -> float:
"""threshold to determine if an embedded region is a sub-region of a given block
when aggregating the text from embedded elements that lie within the given block
When the intersection region area divided by self area is larger than this threshold self is
considered a subregion of the other
"""
return self._get_float("EMBEDDED_TEXT_AGGREGATION_SUBREGION_THRESHOLD", 0.99)
@property
def EMBEDDED_TEXT_SAME_REGION_THRESHOLD(self) -> float:
"""threshold to consider the bounding boxes of two embedded images as the same region"""
return self._get_float("EMBEDDED_TEXT_SAME_REGION_THRESHOLD", 0.9)
@property
def PDF_ANNOTATION_THRESHOLD(self) -> float:
"""The threshold value (between 0.0 and 1.0) that determines the minimum overlap required
for an annotation to be considered within the element.
"""
return self._get_float("PDF_ANNOTATION_THRESHOLD", 0.9)
@property
def PDF_MAX_EMBED_LOW_FIDELITY_TEXT_RATIO(self) -> float:
"""maximum ratio of low fidelity charcaters for a text to be considered embedded text"""
return self._get_float("PDF_MAX_EMBED_LOW_FIDELITY_TEXT_RATIO", 0.1)
@property
def GLOBAL_WORKING_DIR_ENABLED(self) -> bool:
"""Enable usage of GLOBAL_WORKING_DIR and GLOBAL_WORKING_PROCESS_DIR."""
return self._get_bool("GLOBAL_WORKING_DIR_ENABLED", False)
@property
def GLOBAL_WORKING_DIR(self) -> str:
"""Path to Unstructured cache directory."""
return self._get_string("GLOBAL_WORKING_DIR", str(Path.home() / ".cache/unstructured"))
@property
def GLOBAL_WORKING_PROCESS_DIR(self) -> str:
"""Path to Unstructured cache tempdir. Overrides TMPDIR, TEMP and TMP.
Defaults to '{GLOBAL_WORKING_DIR}/tmp/{os.getpgid(0)}'.
"""
default_tmpdir = get_tempdir(dir=self.GLOBAL_WORKING_DIR)
tmpdir = self._get_string("GLOBAL_WORKING_PROCESS_DIR", default_tmpdir)
if tmpdir == "":
tmpdir = default_tmpdir
if self.GLOBAL_WORKING_DIR_ENABLED:
self._setup_tmpdir(tmpdir)
return tmpdir
@property
def ANALYSIS_DUMP_OD_SKIP(self) -> bool:
"""Analysis dump object detection skip flag."""
return self._get_bool("ANALYSIS_DUMP_OD_SKIP", False)
@property
def ANALYSIS_BBOX_SKIP(self) -> bool:
"""Analysis draw bboxes on pages skip flag."""
return self._get_bool("ANALYSIS_BBOX_SKIP", False)
@property
def ANALYSIS_BBOX_DRAW_GRID(self) -> bool:
"""Flag for drawing the analysis bboxes on a single image (as grid)"""
return self._get_bool("ANALYSIS_BBOX_DRAW_GRID", False)
@property
def ANALYSIS_BBOX_DRAW_CAPTION(self) -> bool:
"""Flag for drawing the caption above the analysed page (for e.g. layout source)"""
return self._get_bool("ANALYSIS_BBOX_DRAW_CAPTION", True)
@property
def ANALYSIS_BBOX_RESIZE(self) -> Optional[float]:
"""Analaysis bbox resize value"""
resize = self._get_float("ANALYSIS_BBOX_RESIZE", -1.0)
if resize == -1.0:
return None
return resize
@property
def ANALYSIS_BBOX_FORMAT(self) -> str:
"""The format for analysed pages with bboxes drawn on them. Default is 'png'."""
return self._get_string("ANALYSIS_BBOX_FORMAT", "png")
@property
def TEXT_COVERAGE_THRESHOLD(self) -> float:
"""the minimum iou between extracted text bboxes and their target inferred element bbox for
the inferred element to be considered contaning extracted text"""
return self._get_float("TEXT_COVERAGE_THRESHOLD", 0.25)
@property
def PDF_CHAR_DUPLICATE_THRESHOLD(self) -> float:
"""Maximum pixel distance to consider two characters as duplicates (fake bold rendering).
Some PDFs create bold text by rendering the same character twice at slightly offset
positions. This threshold determines how close two identical characters must be to be
considered duplicates. The algorithm also checks for bounding box overlap to avoid
false positives with legitimate double letters. Set to 0 to disable duplicate character
removal.
"""
return self._get_float("PDF_CHAR_DUPLICATE_THRESHOLD", 2.0)
@property
def PDF_CHAR_OVERLAP_RATIO_THRESHOLD(self) -> float:
"""Minimum overlap ratio to consider two characters as duplicates (fake bold rendering).
When detecting fake-bold duplicates, this threshold determines the minimum bounding box
overlap ratio required. Fake-bold duplicates typically have >70% overlap, while legitimate
consecutive identical letters (like "ll" in "skills") have <30% overlap. Default is 0.5
(50%) as a conservative threshold. Valid range is 0.0 to 1.0.
"""
return self._get_float("PDF_CHAR_OVERLAP_RATIO_THRESHOLD", 0.5)
@property
def PDF_RENDER_DPI(self) -> int:
"""The DPI to use for rendering PDF pages"""
return self._get_int("PDF_RENDER_DPI", 350)
@property
def PDF_RENDER_MAX_PIXELS_PER_PAGE(self) -> int:
"""Maximum rendered pixels allowed for a single PDF page"""
return self._get_int("PDF_RENDER_MAX_PIXELS_PER_PAGE", 1_000_000_000)
env_config = ENVConfig()