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
325 lines
13 KiB
Python
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()
|