Coverage for src/qdrant_loader/core/conversion/config.py: 97%
63 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-20 10:15 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-20 10:15 +0000
1"""Engine-agnostic conversion configuration.
3Frozen dataclasses describing *our* conversion knobs — never docling types. The
4option builder in :mod:`.options` is the single place that translates this into
5docling objects, which keeps the config swappable and the engine behind a seam.
7The defaults below ARE the ``fast`` profile (CPU-only, born-digital corpus,
8conversion opt-in per source); each field documents its own rationale inline.
9"""
11from __future__ import annotations
13import dataclasses
14from dataclasses import dataclass, field
15from enum import StrEnum
16from typing import Any
19@dataclass(frozen=True, slots=True)
20class OcrConfig:
21 """OCR settings. Off in the baseline — the corpus is born-digital."""
23 enabled: bool = False
24 backend: str = (
25 "torch" # the only rapidocr backend in the slim image (no onnxruntime)
26 )
27 languages: tuple[str, ...] = ("english",) # rapidocr supports english | chinese
28 full_page: bool = False
31@dataclass(frozen=True, slots=True)
32class TableConfig:
33 """Table-structure recognition settings."""
35 enabled: bool = True
36 mode: str = "fast" # FAST on CPU; the "accurate" profile flips this
37 cell_matching: bool = True
40@dataclass(frozen=True, slots=True)
41class PictureDescriptionConfig:
42 """API image-captioning. Off by default but fully wired (matches today)."""
44 enabled: bool = False
45 url: str = "https://api.openai.com/v1/chat/completions"
46 model: str = "gpt-4o-mini"
47 api_key: str | None = None
48 prompt: str = "Describe this image in a few sentences."
49 timeout: float = 60.0
50 concurrency: int = 1
51 scale: float = 2.0 # docling's own picture-description default scale
54@dataclass(frozen=True, slots=True)
55class ExcelConfig:
56 """Excel backend tuning. docling's defaults already handle merged cells."""
58 gap_tolerance: int = 0
59 singleton_as_text: bool = False
60 sheet_names: tuple[str, ...] | None = None
63class ConversionProfile(StrEnum):
64 """Named bundles of the three cost axes (OCR / table fidelity / captioning).
66 Operators choose one profile instead of reasoning about a dozen interacting
67 knobs. The string values are what a connector config carries in YAML, so the
68 enum doubles as the type-safe parse target for that string.
69 """
71 FAST = "fast"
72 ACCURATE = "accurate"
73 SCANNED = "scanned"
76@dataclass(frozen=True, slots=True)
77class ConversionConfig:
78 """The complete, immutable conversion configuration for one engine instance."""
80 # ── policy / ops ──
81 max_file_size: int = 50 * 1024 * 1024
82 document_timeout: float | None = 300.0 # wall-clock; docling default is None
83 enabled_formats: tuple[str, ...] = ("pdf", "docx", "pptx", "xlsx", "image", "csv")
85 # ── hardware ──
86 device: str = "auto"
87 num_threads: int = 4
88 compile_models: bool = False # disable torch.compile warm-up cost on CPU
89 artifacts_path: str | None = None # offline model dir; None = fetch-on-first-run
91 # ── pdf / image quality ──
92 ocr: OcrConfig = field(default_factory=OcrConfig)
93 table: TableConfig = field(default_factory=TableConfig)
94 code_formula_enrichment: bool = False
96 # ── enrichment / office ──
97 picture: PictureDescriptionConfig = field(default_factory=PictureDescriptionConfig)
98 excel: ExcelConfig = field(default_factory=ExcelConfig)
100 @classmethod
101 def from_profile(
102 cls, profile: ConversionProfile, **overrides: Any
103 ) -> ConversionConfig:
104 """Build the baseline config for ``profile``, then apply top-level overrides.
106 Overrides replace top-level fields only; nested sub-configs (``ocr=...``,
107 ``table=...``) are passed as whole frozen objects, not field-by-field.
108 """
109 baseline = cls._baseline_for(profile)
110 return dataclasses.replace(baseline, **overrides) if overrides else baseline
112 @classmethod
113 def _baseline_for(cls, profile: ConversionProfile) -> ConversionConfig:
114 """The unmodified config for each profile. Replaces the old _PROFILES dict."""
115 match profile:
116 case ConversionProfile.FAST:
117 return cls() # the schema defaults are the fast profile
118 case ConversionProfile.ACCURATE:
119 return cls(table=TableConfig(mode="accurate"))
120 case ConversionProfile.SCANNED:
121 return cls(
122 ocr=OcrConfig(enabled=True, full_page=True),
123 table=TableConfig(mode="accurate"),
124 document_timeout=600.0,
125 )
126 raise ValueError(f"unknown conversion profile: {profile!r}")