mathcraft-ocr 0.2.6__tar.gz → 0.2.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mathcraft-ocr might be problematic. Click here for more details.

Files changed (42) hide show
  1. {mathcraft_ocr-0.2.6/mathcraft_ocr.egg-info → mathcraft_ocr-0.2.7}/PKG-INFO +2 -2
  2. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/README_MATHCRAFT_OCR.md +1 -1
  3. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/__init__.py +1 -1
  4. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/adapters/common.py +9 -1
  5. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/adapters/text_recognizer.py +32 -3
  6. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/error_patterns.py +14 -0
  7. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/providers.py +11 -18
  8. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/runtime.py +2 -2
  9. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/serialization.py +0 -1
  10. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7/mathcraft_ocr.egg-info}/PKG-INFO +2 -2
  11. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/pyproject.toml +1 -1
  12. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/LICENSE +0 -0
  13. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/MANIFEST.in +0 -0
  14. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/__main__.py +0 -0
  15. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/adapters/__init__.py +0 -0
  16. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/adapters/formula_detector.py +0 -0
  17. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/adapters/formula_recognizer.py +0 -0
  18. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/adapters/text_detector.py +0 -0
  19. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/api.py +0 -0
  20. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/cache.py +0 -0
  21. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/cli.py +0 -0
  22. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/debug_blocks.py +0 -0
  23. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/doctor.py +0 -0
  24. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/downloader.py +0 -0
  25. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/errors.py +0 -0
  26. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/formula_lines.py +0 -0
  27. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/hardware.py +0 -0
  28. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/image.py +0 -0
  29. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/latex_alignment.py +0 -0
  30. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/latex_quality.py +0 -0
  31. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/layout.py +0 -0
  32. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/manifest.py +0 -0
  33. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/manifests/models.v1.json +0 -0
  34. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/profiles.py +0 -0
  35. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/results.py +0 -0
  36. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr/worker.py +0 -0
  37. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr.egg-info/SOURCES.txt +0 -0
  38. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr.egg-info/dependency_links.txt +0 -0
  39. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr.egg-info/entry_points.txt +0 -0
  40. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr.egg-info/requires.txt +0 -0
  41. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/mathcraft_ocr.egg-info/top_level.txt +0 -0
  42. {mathcraft_ocr-0.2.6 → mathcraft_ocr-0.2.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mathcraft-ocr
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: ONNX-only OCR runtime for mathematical documents
5
5
  Author: SakuraMathcraft
6
6
  License-Expression: GPL-3.0-only
@@ -215,4 +215,4 @@ python -m build --no-isolation --outdir .\release_assets\mathcraft-ocr-package\d
215
215
 
216
216
  ## License
217
217
 
218
- MIT. See `LICENSE`.
218
+ GPL-3.0-only. See `LICENSE`.
@@ -169,4 +169,4 @@ python -m build --no-isolation --outdir .\release_assets\mathcraft-ocr-package\d
169
169
 
170
170
  ## License
171
171
 
172
- MIT. See `LICENSE`.
172
+ GPL-3.0-only. See `LICENSE`.
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.2.6"
5
+ __version__ = "0.2.7"
6
6
 
7
7
  __all__ = [
8
8
  "DoctorReport",
@@ -33,13 +33,21 @@ def create_session(model_path: str | Path, provider_info: ProviderInfo):
33
33
  raise RuntimeError(
34
34
  f"requested ONNX GPU provider {active}, but session providers are {actual}"
35
35
  )
36
+ disable_fallback = getattr(session, "disable_fallback", None)
37
+ if not callable(disable_fallback):
38
+ raise RuntimeError("ONNX Runtime session cannot disable provider fallback")
39
+ disable_fallback()
36
40
  return session
37
41
 
38
42
 
39
43
  @lru_cache(maxsize=16)
40
44
  def _create_session_cached(model_path: str, providers: tuple[str, ...]):
41
45
  ort = _ort()
42
- return ort.InferenceSession(model_path, providers=list(providers))
46
+ return ort.InferenceSession(
47
+ model_path,
48
+ providers=list(providers),
49
+ enable_fallback=False,
50
+ )
43
51
 
44
52
 
45
53
  def clear_session_cache() -> None:
@@ -57,11 +57,21 @@ def _create_pp_text_recognizer(model_dir: Path, provider_info) -> TextRecognizer
57
57
  active_provider = str(getattr(provider_info, "active_provider", "") or "")
58
58
  use_cuda = active_provider in {"CUDAExecutionProvider", "TensorrtExecutionProvider"}
59
59
  use_dml = active_provider == "DmlExecutionProvider"
60
- return _create_pp_text_recognizer_cached(str(model_dir), use_cuda, use_dml)
60
+ return _create_pp_text_recognizer_cached(
61
+ str(model_dir),
62
+ active_provider,
63
+ use_cuda,
64
+ use_dml,
65
+ )
61
66
 
62
67
 
63
68
  @lru_cache(maxsize=8)
64
- def _create_pp_text_recognizer_cached(model_dir: str, use_cuda: bool, use_dml: bool) -> TextRecognizer:
69
+ def _create_pp_text_recognizer_cached(
70
+ model_dir: str,
71
+ active_provider: str,
72
+ use_cuda: bool,
73
+ use_dml: bool,
74
+ ) -> TextRecognizer:
65
75
  model_dir = Path(model_dir)
66
76
  model_candidates = sorted(model_dir.glob("**/*rec*.onnx"))
67
77
  if not model_candidates:
@@ -111,7 +121,26 @@ def _create_pp_text_recognizer_cached(model_dir: str, use_cuda: bool, use_dml: b
111
121
  },
112
122
  },
113
123
  })
114
- return TextRecognizer(config)
124
+ recognizer = TextRecognizer(config)
125
+ _enforce_strict_provider(recognizer, active_provider)
126
+ return recognizer
127
+
128
+
129
+ def _enforce_strict_provider(recognizer: TextRecognizer, active_provider: str) -> None:
130
+ engine = getattr(recognizer, "session", None)
131
+ session = getattr(engine, "session", None)
132
+ if session is None:
133
+ raise RuntimeError("RapidOCR did not expose its ONNX Runtime session")
134
+ actual = list(session.get_providers() or [])
135
+ if not active_provider or not actual or actual[0] != active_provider:
136
+ raise RuntimeError(
137
+ f"requested ONNX provider {active_provider or '<none>'}, "
138
+ f"but RapidOCR session providers are {actual}"
139
+ )
140
+ disable_fallback = getattr(session, "disable_fallback", None)
141
+ if not callable(disable_fallback):
142
+ raise RuntimeError("RapidOCR ONNX Runtime session cannot disable provider fallback")
143
+ disable_fallback()
115
144
 
116
145
 
117
146
  def clear_text_recognizer_cache() -> None:
@@ -17,6 +17,13 @@ CUDA_RUNTIME_ERROR_MARKERS = (
17
17
  "cufft",
18
18
  )
19
19
 
20
+ GPU_PROVIDER_ERROR_MARKERS = (
21
+ "gpu provider was requested",
22
+ "requested onnx gpu provider",
23
+ "requested onnx provider dmlexecutionprovider",
24
+ "requested onnx provider tensorrtexecutionprovider",
25
+ )
26
+
20
27
  ONNXRUNTIME_INSTALL_ERROR_MARKERS = (
21
28
  "failed to import onnxruntime",
22
29
  "failed to query onnx providers",
@@ -31,6 +38,11 @@ def looks_like_cuda_runtime_error(detail: object) -> bool:
31
38
  return any(marker in text for marker in CUDA_RUNTIME_ERROR_MARKERS)
32
39
 
33
40
 
41
+ def looks_like_gpu_provider_error(detail: object) -> bool:
42
+ text = str(detail or "").lower()
43
+ return any(marker in text for marker in GPU_PROVIDER_ERROR_MARKERS)
44
+
45
+
34
46
  def looks_like_onnxruntime_install_error(detail: object) -> bool:
35
47
  text = str(detail or "").lower()
36
48
  return any(marker in text for marker in ONNXRUNTIME_INSTALL_ERROR_MARKERS)
@@ -38,7 +50,9 @@ def looks_like_onnxruntime_install_error(detail: object) -> bool:
38
50
 
39
51
  __all__ = [
40
52
  "CUDA_RUNTIME_ERROR_MARKERS",
53
+ "GPU_PROVIDER_ERROR_MARKERS",
41
54
  "ONNXRUNTIME_INSTALL_ERROR_MARKERS",
42
55
  "looks_like_cuda_runtime_error",
56
+ "looks_like_gpu_provider_error",
43
57
  "looks_like_onnxruntime_install_error",
44
58
  ]
@@ -3,7 +3,6 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import importlib
6
- import os
7
6
  from dataclasses import dataclass
8
7
 
9
8
  from .errors import ProviderError
@@ -23,7 +22,6 @@ class ProviderInfo:
23
22
  device: str
24
23
  gpu_requested: bool
25
24
  gpu_runtime_ok: bool
26
- cpu_fallback: bool
27
25
 
28
26
  @property
29
27
  def use_cuda(self) -> bool:
@@ -56,26 +54,17 @@ def detect_providers(prefer: str = "auto") -> ProviderInfo:
56
54
  except Exception as exc: # pragma: no cover - runtime environment dependent
57
55
  raise ProviderError(f"failed to query ONNX providers: {exc}") from exc
58
56
 
59
- force_cpu = os.environ.get("MATHCRAFT_FORCE_ORT_CPU", "").strip() == "1"
60
57
  gpu_candidates = tuple(name for name in GPU_PROVIDER_NAMES if name in available)
61
58
  gpu_visible = bool(gpu_candidates)
62
- if force_cpu:
63
- return ProviderInfo(
64
- available_providers=available,
65
- active_provider="CPUExecutionProvider" if "CPUExecutionProvider" in available else None,
66
- device="cpu",
67
- gpu_requested=(prefer_norm in {"auto", "gpu"}),
68
- gpu_runtime_ok=False,
69
- cpu_fallback=True,
70
- )
71
59
  if prefer_norm == "cpu":
60
+ if "CPUExecutionProvider" not in available:
61
+ raise ProviderError(f"CPUExecutionProvider unavailable: {available}")
72
62
  return ProviderInfo(
73
63
  available_providers=available,
74
- active_provider="CPUExecutionProvider" if "CPUExecutionProvider" in available else None,
64
+ active_provider="CPUExecutionProvider",
75
65
  device="cpu",
76
66
  gpu_requested=False,
77
67
  gpu_runtime_ok=False,
78
- cpu_fallback=False,
79
68
  )
80
69
  if gpu_visible:
81
70
  return ProviderInfo(
@@ -84,13 +73,17 @@ def detect_providers(prefer: str = "auto") -> ProviderInfo:
84
73
  device="gpu",
85
74
  gpu_requested=True,
86
75
  gpu_runtime_ok=True,
87
- cpu_fallback=False,
88
76
  )
77
+ if prefer_norm == "gpu":
78
+ raise ProviderError(
79
+ f"GPU provider was requested but none is available: {available}"
80
+ )
81
+ if "CPUExecutionProvider" not in available:
82
+ raise ProviderError(f"no supported ONNX execution provider is available: {available}")
89
83
  return ProviderInfo(
90
84
  available_providers=available,
91
- active_provider="CPUExecutionProvider" if "CPUExecutionProvider" in available else None,
85
+ active_provider="CPUExecutionProvider",
92
86
  device="cpu",
93
- gpu_requested=(prefer_norm in {"auto", "gpu"}),
87
+ gpu_requested=False,
94
88
  gpu_runtime_ok=False,
95
- cpu_fallback=(prefer_norm == "gpu"),
96
89
  )
@@ -22,7 +22,7 @@ from .adapters.text_recognizer import (
22
22
  from .cache import ModelCacheState, inspect_manifest_roots, resolve_model_roots, resolve_user_models_dir
23
23
  from .doctor import DoctorReport, run_doctor
24
24
  from .downloader import download_model_archive
25
- from .error_patterns import looks_like_cuda_runtime_error
25
+ from .error_patterns import looks_like_cuda_runtime_error, looks_like_gpu_provider_error
26
26
  from .errors import ModelCacheError
27
27
  from .formula_lines import (
28
28
  FormulaLineGroup,
@@ -207,7 +207,7 @@ class MathCraftRuntime:
207
207
  @staticmethod
208
208
  def _looks_like_broken_model_error(exc: Exception) -> bool:
209
209
  text = str(exc).lower()
210
- if looks_like_cuda_runtime_error(text):
210
+ if looks_like_cuda_runtime_error(text) or looks_like_gpu_provider_error(text):
211
211
  return False
212
212
  needles = (
213
213
  "missing",
@@ -71,7 +71,6 @@ def provider_info_to_json(provider_info) -> dict:
71
71
  "device": provider_info.device,
72
72
  "gpu_requested": provider_info.gpu_requested,
73
73
  "gpu_runtime_ok": provider_info.gpu_runtime_ok,
74
- "cpu_fallback": provider_info.cpu_fallback,
75
74
  "use_cuda": provider_info.use_cuda,
76
75
  "use_dml": provider_info.use_dml,
77
76
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mathcraft-ocr
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: ONNX-only OCR runtime for mathematical documents
5
5
  Author: SakuraMathcraft
6
6
  License-Expression: GPL-3.0-only
@@ -215,4 +215,4 @@ python -m build --no-isolation --outdir .\release_assets\mathcraft-ocr-package\d
215
215
 
216
216
  ## License
217
217
 
218
- MIT. See `LICENSE`.
218
+ GPL-3.0-only. See `LICENSE`.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mathcraft-ocr"
7
- version = "0.2.6"
7
+ version = "0.2.7"
8
8
  description = "ONNX-only OCR runtime for mathematical documents"
9
9
  readme = "README_MATHCRAFT_OCR.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes
File without changes