open-code-review-toolkit 0.8.0__tar.gz → 0.8.2__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.
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/PKG-INFO +2 -1
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/README.md +1 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/_version.py +2 -2
- open_code_review_toolkit-0.8.2/src/ocr_toolkit/configure.py +96 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/result.py +8 -6
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/workflow.py +95 -47
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/preflight.py +21 -68
- open_code_review_toolkit-0.8.2/src/ocr_toolkit/provider_config.py +296 -0
- open_code_review_toolkit-0.8.2/src/ocr_toolkit/provider_failure.py +247 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/review_runner.py +38 -8
- open_code_review_toolkit-0.8.0/src/ocr_toolkit/configure.py +0 -188
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/.gitignore +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/LICENSE +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/pyproject.toml +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/cli.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/common/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/common/filesystem.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/common/git.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/common/language.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/common/markdown.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/common/redaction.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/config_writer.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/adapters.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/broker.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/contracts.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/dlp.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/mcp.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/policy.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/recognizers.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/context/store.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/__main__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/actions.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/artifacts.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/categorize.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/collect.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/collectors/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/collectors/graphs.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/collectors/orchestration.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/collectors/projections.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/collectors/registry.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/collectors/sources.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/coverage.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/ansible/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/ansible/requirements.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/ansible/topology.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/contracts.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/go.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/javascript.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/php.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/ecosystems/python.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/contracts.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/detection.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/providers/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/providers/frontend.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/providers/go.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/providers/php.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/providers/python.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/registry.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/schema.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/frameworks/templates.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/infrastructure.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/invocation.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/mcp.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/model.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/policy/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/policy/contracts.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/policy/decisions.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/policy/guidance.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/policy/registry.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/policy/schema.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/policy/scopes.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/project.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/repository.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/review_context.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/store/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/store/atomic.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/store/contracts.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/store/core.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/store/readback.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/evidence/store/values.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/mcp_config.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/ocr_result.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/__main__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/approval.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/comments.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/formatting.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/gitlab.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/gitlab_approval.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/markers.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/payloads.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/reconciliation.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/settings.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/snapshot.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/suggestions.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/transaction.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/pre_execution.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/providers/__init__.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/providers/gitlab.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/providers/gitlab_context.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/providers/gitlab_discussions.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/providers/gitlab_identity.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/providers/gitlab_remediation.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/py.typed +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/result_contract.py +0 -0
- {open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/result_usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: open-code-review-toolkit
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.2
|
|
4
4
|
Summary: Unofficial GitLab CI integration layer for Open Code Review
|
|
5
5
|
Project-URL: Homepage, https://github.com/xeonvs/open-code-review-toolkit
|
|
6
6
|
Project-URL: Repository, https://github.com/xeonvs/open-code-review-toolkit
|
|
@@ -249,6 +249,7 @@ ocr-ci --help
|
|
|
249
249
|
The exact recommended OCR release and its verified asset checksums live in the [versioned compatibility manifest](compatibility/ocr-support.json). CI should pin that release and checksum before execution.
|
|
250
250
|
The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases.
|
|
251
251
|
Review output defaults to English. `OCR_REVIEW_LANGUAGE` accepts another explicit language name when a project needs localized review output; for example, `OCR_REVIEW_LANGUAGE=Russian`.
|
|
252
|
+
The current OCR 1.10.0 integration defaults `OCR_REVIEW_EFFORT` to `medium` for two review rounds. `low` and `high` are explicit one- and three-round alternatives; see the [configuration reference](docs/configuration.md#review-effort) for cost, budget, and precedence boundaries.
|
|
252
253
|
|
|
253
254
|
Stable distributions are published to [PyPI](https://pypi.org/project/open-code-review-toolkit/) and mirrored as checksum-listed, provenance-attested assets in the corresponding [GitHub Release](https://github.com/xeonvs/open-code-review-toolkit/releases). Development snapshots are published only to TestPyPI.
|
|
254
255
|
|
|
@@ -22,6 +22,7 @@ ocr-ci --help
|
|
|
22
22
|
The exact recommended OCR release and its verified asset checksums live in the [versioned compatibility manifest](compatibility/ocr-support.json). CI should pin that release and checksum before execution.
|
|
23
23
|
The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases.
|
|
24
24
|
Review output defaults to English. `OCR_REVIEW_LANGUAGE` accepts another explicit language name when a project needs localized review output; for example, `OCR_REVIEW_LANGUAGE=Russian`.
|
|
25
|
+
The current OCR 1.10.0 integration defaults `OCR_REVIEW_EFFORT` to `medium` for two review rounds. `low` and `high` are explicit one- and three-round alternatives; see the [configuration reference](docs/configuration.md#review-effort) for cost, budget, and precedence boundaries.
|
|
25
26
|
|
|
26
27
|
Stable distributions are published to [PyPI](https://pypi.org/project/open-code-review-toolkit/) and mirrored as checksum-listed, provenance-attested assets in the corresponding [GitHub Release](https://github.com/xeonvs/open-code-review-toolkit/releases). Development snapshots are published only to TestPyPI.
|
|
27
28
|
|
{open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/_version.py
RENAMED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.8.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 8,
|
|
21
|
+
__version__ = version = '0.8.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 8, 2)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Configure OCR runtime settings from CI environment variables."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from ocr_toolkit.common.language import resolve_review_language
|
|
10
|
+
from ocr_toolkit.common.redaction import redact_sensitive
|
|
11
|
+
from ocr_toolkit.config_writer import OCRConfigError, update_ocr_config
|
|
12
|
+
from ocr_toolkit.provider_config import (
|
|
13
|
+
ProviderConfigError,
|
|
14
|
+
provider_config_from_environment,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OCRRuntimeConfigError(Exception):
|
|
19
|
+
"""OCR runtime config from CI env is invalid."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
REVIEW_EFFORTS = frozenset({"low", "medium", "high"})
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _env(name: str, default: str = "") -> str:
|
|
26
|
+
return os.environ.get(name, default).strip()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _bool_env(name: str) -> bool:
|
|
30
|
+
return _env(name).lower() == "true"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def review_effort() -> str:
|
|
34
|
+
"""Return the closed OCR review-effort preset, defaulting to medium."""
|
|
35
|
+
|
|
36
|
+
value = (_env("OCR_REVIEW_EFFORT") or "medium").lower()
|
|
37
|
+
if value not in REVIEW_EFFORTS:
|
|
38
|
+
raise OCRRuntimeConfigError("OCR_REVIEW_EFFORT must be one of: low, medium, high")
|
|
39
|
+
return value
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def build_config_updates() -> dict[str, Any]:
|
|
43
|
+
"""Build OCR config updates from already-normalized CI environment."""
|
|
44
|
+
|
|
45
|
+
review_language = resolve_review_language()
|
|
46
|
+
try:
|
|
47
|
+
provider = provider_config_from_environment()
|
|
48
|
+
llm_url = provider.require_inference_url()
|
|
49
|
+
llm_token = provider.require_token()
|
|
50
|
+
llm_model = provider.require_model()
|
|
51
|
+
except ProviderConfigError as exc:
|
|
52
|
+
raise OCRRuntimeConfigError(str(exc)) from exc
|
|
53
|
+
request_controls = provider.request_controls
|
|
54
|
+
|
|
55
|
+
updates: dict[str, Any] = {
|
|
56
|
+
"effort": review_effort(),
|
|
57
|
+
"language": review_language,
|
|
58
|
+
"llm.url": llm_url,
|
|
59
|
+
"llm.auth_token": llm_token,
|
|
60
|
+
"llm.model": llm_model,
|
|
61
|
+
"llm.protocol": request_controls.protocol,
|
|
62
|
+
"llm.use_anthropic": request_controls.protocol == "anthropic",
|
|
63
|
+
"llm.auth_header": request_controls.auth_header,
|
|
64
|
+
"telemetry.enabled": _bool_env("OCR_TELEMETRY_ENABLED"),
|
|
65
|
+
"telemetry.content_logging": _bool_env("OCR_TELEMETRY_CONTENT_LOGGING"),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if request_controls.extra_headers:
|
|
69
|
+
updates["llm.extra_headers"] = request_controls.extra_headers
|
|
70
|
+
|
|
71
|
+
if request_controls.extra_body is not None:
|
|
72
|
+
updates["llm.extra_body"] = request_controls.extra_body
|
|
73
|
+
|
|
74
|
+
if _bool_env("OCR_TELEMETRY_ENABLED"):
|
|
75
|
+
updates["telemetry.exporter"] = _env("OCR_TELEMETRY_EXPORTER")
|
|
76
|
+
otlp_endpoint = _env("OCR_TELEMETRY_OTLP_ENDPOINT")
|
|
77
|
+
if otlp_endpoint:
|
|
78
|
+
updates["telemetry.otlp_endpoint"] = otlp_endpoint
|
|
79
|
+
|
|
80
|
+
return updates
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def main() -> int:
|
|
84
|
+
"""Write OCR config and return a process exit code."""
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
update_ocr_config(build_config_updates())
|
|
88
|
+
except (OCRRuntimeConfigError, OCRConfigError) as exc:
|
|
89
|
+
print(f"Failed to configure OCR runtime: {redact_sensitive(str(exc))}", file=sys.stderr)
|
|
90
|
+
return 1
|
|
91
|
+
print("OCR runtime config written")
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
raise SystemExit(main())
|
{open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/posting/result.py
RENAMED
|
@@ -17,13 +17,14 @@ from ocr_toolkit.ocr_result import (
|
|
|
17
17
|
load_ocr_result,
|
|
18
18
|
)
|
|
19
19
|
from ocr_toolkit.posting.comments import clean_text, compact_escaped_text
|
|
20
|
+
from ocr_toolkit.provider_failure import ProviderFailureReason
|
|
20
21
|
from ocr_toolkit.result_contract import ReviewOutcome
|
|
21
22
|
|
|
22
23
|
__all__ = [
|
|
23
24
|
"OcrResultMalformed",
|
|
24
25
|
"OcrResultMissing",
|
|
25
26
|
"OcrResultTooLarge",
|
|
26
|
-
"
|
|
27
|
+
"llm_billing_failure_reason",
|
|
27
28
|
"load_ocr_result",
|
|
28
29
|
"normalize_coverage_diagnostics",
|
|
29
30
|
"ocr_warning_text",
|
|
@@ -199,12 +200,13 @@ def ocr_warning_text(warning: Any, *, _seen: set[int] | None = None) -> str:
|
|
|
199
200
|
return clean_text(warning)
|
|
200
201
|
|
|
201
202
|
|
|
202
|
-
def
|
|
203
|
-
|
|
203
|
+
def llm_billing_failure_reason(
|
|
204
|
+
warnings: Sequence[Any],
|
|
205
|
+
) -> ProviderFailureReason | None:
|
|
206
|
+
"""Map a legacy OCR billing warning to the shared safe provider reason."""
|
|
204
207
|
|
|
205
|
-
matches: list[str] = []
|
|
206
208
|
for warning in warnings:
|
|
207
209
|
text = ocr_warning_text(warning)
|
|
208
210
|
if text and LLM_BILLING_FAILURE_RE.search(text):
|
|
209
|
-
|
|
210
|
-
return
|
|
211
|
+
return ProviderFailureReason.RATE_OR_SPENDING_LIMIT
|
|
212
|
+
return None
|
|
@@ -15,7 +15,6 @@ from typing import Any
|
|
|
15
15
|
|
|
16
16
|
from ocr_toolkit.common.git import isolated_git_environment, read_only_git_prefix
|
|
17
17
|
from ocr_toolkit.common.markdown import markdown_code_block, neutralize_quick_actions
|
|
18
|
-
from ocr_toolkit.common.redaction import redact_sensitive
|
|
19
18
|
from ocr_toolkit.evidence.artifacts import repository_artifacts
|
|
20
19
|
from ocr_toolkit.ocr_result import (
|
|
21
20
|
TOOLKIT_RESULT_KEY,
|
|
@@ -37,7 +36,6 @@ from ocr_toolkit.posting.comments import (
|
|
|
37
36
|
clean_text,
|
|
38
37
|
code_text,
|
|
39
38
|
comment_line,
|
|
40
|
-
compact_escaped_text,
|
|
41
39
|
)
|
|
42
40
|
from ocr_toolkit.posting.formatting import (
|
|
43
41
|
format_fallback_comment_chunks,
|
|
@@ -71,12 +69,11 @@ from ocr_toolkit.posting.markers import (
|
|
|
71
69
|
)
|
|
72
70
|
from ocr_toolkit.posting.payloads import build_marked_note_body
|
|
73
71
|
from ocr_toolkit.posting.result import (
|
|
74
|
-
|
|
72
|
+
llm_billing_failure_reason,
|
|
75
73
|
normalize_coverage_diagnostics,
|
|
76
74
|
)
|
|
77
75
|
from ocr_toolkit.posting.snapshot import (
|
|
78
76
|
BotCommentRefs,
|
|
79
|
-
cleanup_drafts_created_by_this_run,
|
|
80
77
|
collect_previous_bot_comment_refs,
|
|
81
78
|
delete_previous_bot_comments_if_collected,
|
|
82
79
|
delete_previous_setup_notes,
|
|
@@ -101,6 +98,10 @@ from ocr_toolkit.pre_execution import (
|
|
|
101
98
|
PreExecutionStatusError,
|
|
102
99
|
read_pre_execution_status,
|
|
103
100
|
)
|
|
101
|
+
from ocr_toolkit.provider_failure import (
|
|
102
|
+
ProviderFailureReason,
|
|
103
|
+
provider_failure_reason,
|
|
104
|
+
)
|
|
104
105
|
from ocr_toolkit.result_contract import OcrResultContractError, ReviewOutcome, parse_result_outcome
|
|
105
106
|
|
|
106
107
|
# Kept as a module-level compatibility seam for tests and external monkey-patching.
|
|
@@ -662,19 +663,13 @@ def post_results(config: GitLabConfig, result: dict[str, Any]) -> int:
|
|
|
662
663
|
token_usage_summary=token_usage_summary,
|
|
663
664
|
)
|
|
664
665
|
|
|
665
|
-
|
|
666
|
-
if
|
|
666
|
+
billing_reason = llm_billing_failure_reason(warnings)
|
|
667
|
+
if billing_reason is not None:
|
|
667
668
|
print(
|
|
668
669
|
"OCR reported an LLM provider billing/quota failure; refusing to publish normal review notes.",
|
|
669
670
|
file=sys.stderr,
|
|
670
671
|
)
|
|
671
|
-
return post_llm_provider_failure(
|
|
672
|
-
config,
|
|
673
|
-
billing_warnings,
|
|
674
|
-
tool_calls_summary=tool_calls_summary,
|
|
675
|
-
mcp_usage_summary=mcp_usage_summary,
|
|
676
|
-
token_usage_summary=token_usage_summary,
|
|
677
|
-
)
|
|
672
|
+
return post_llm_provider_failure(config, billing_reason)
|
|
678
673
|
|
|
679
674
|
previous_bot_comment_refs = collect_previous_bot_comment_refs(config)
|
|
680
675
|
transaction = PostingTransaction()
|
|
@@ -1101,38 +1096,75 @@ def post_manifest_failure(
|
|
|
1101
1096
|
|
|
1102
1097
|
def post_llm_provider_failure(
|
|
1103
1098
|
config: GitLabConfig,
|
|
1104
|
-
|
|
1105
|
-
tool_calls_summary: str = "",
|
|
1106
|
-
mcp_usage_summary: str = "",
|
|
1107
|
-
token_usage_summary: str = "",
|
|
1099
|
+
reason: ProviderFailureReason,
|
|
1108
1100
|
) -> int:
|
|
1109
|
-
"""
|
|
1101
|
+
"""Render one closed provider failure without publishing private diagnostics."""
|
|
1110
1102
|
|
|
1111
1103
|
transaction = PostingTransaction()
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1104
|
+
summary, remediation = {
|
|
1105
|
+
ProviderFailureReason.AUTHENTICATION: (
|
|
1106
|
+
"The LLM provider did not accept the configured credential.",
|
|
1107
|
+
"Check the protected credential and its authentication header, then rerun the pipeline.",
|
|
1108
|
+
),
|
|
1109
|
+
ProviderFailureReason.AUTHORIZATION: (
|
|
1110
|
+
"The LLM provider authenticated the request but did not authorize it.",
|
|
1111
|
+
"Check the credential permissions and provider access policy, then rerun the pipeline.",
|
|
1112
|
+
),
|
|
1113
|
+
ProviderFailureReason.RATE_OR_SPENDING_LIMIT: (
|
|
1114
|
+
"The LLM provider rejected the request under a rate or spending limit.",
|
|
1115
|
+
"The cause may be ordinary throttling, an account or API-key spending limit, or cost reservation from the requested output cap. Retry later and check account limits. If short probes pass but a full review fails before generation, try an explicit `OCR_LLM_MAX_COMPLETION_TOKENS`, for example `4096`.",
|
|
1116
|
+
),
|
|
1117
|
+
ProviderFailureReason.OVERLOADED: (
|
|
1118
|
+
"The LLM provider reported that it was overloaded.",
|
|
1119
|
+
"Retry later or use another already-qualified provider deployment.",
|
|
1120
|
+
),
|
|
1121
|
+
ProviderFailureReason.TIMEOUT: (
|
|
1122
|
+
"The LLM provider request timed out.",
|
|
1123
|
+
"Check provider latency and the network path, then rerun the pipeline.",
|
|
1124
|
+
),
|
|
1125
|
+
ProviderFailureReason.NETWORK: (
|
|
1126
|
+
"The LLM provider could not be reached reliably.",
|
|
1127
|
+
"Check DNS, TLS, proxy, and runner connectivity, then rerun the pipeline.",
|
|
1128
|
+
),
|
|
1129
|
+
ProviderFailureReason.ENDPOINT_OR_MODEL_NOT_FOUND: (
|
|
1130
|
+
"The LLM provider reported that the configured endpoint or model was not found.",
|
|
1131
|
+
"Verify both the provider API root and model identifier; the safe diagnostic cannot distinguish which one was absent.",
|
|
1132
|
+
),
|
|
1133
|
+
ProviderFailureReason.REQUEST_REJECTED: (
|
|
1134
|
+
"The LLM provider rejected the review request.",
|
|
1135
|
+
"Check the explicit protocol, endpoint shape, model contract, and request controls, then rerun the pipeline.",
|
|
1136
|
+
),
|
|
1137
|
+
ProviderFailureReason.PROVIDER_UNAVAILABLE: (
|
|
1138
|
+
"The LLM provider was unavailable while processing the review.",
|
|
1139
|
+
"Retry later and check the provider service status.",
|
|
1140
|
+
),
|
|
1141
|
+
ProviderFailureReason.INVALID_RESPONSE: (
|
|
1142
|
+
"The LLM provider returned a response that could not be consumed safely.",
|
|
1143
|
+
"Check protocol compatibility and provider response health, then rerun the pipeline.",
|
|
1144
|
+
),
|
|
1145
|
+
ProviderFailureReason.CANCELLED: (
|
|
1146
|
+
"The LLM provider request was cancelled before the review completed.",
|
|
1147
|
+
"Check pipeline cancellation and deadline signals, then rerun when the job can complete.",
|
|
1148
|
+
),
|
|
1149
|
+
ProviderFailureReason.MIXED: (
|
|
1150
|
+
"The review encountered more than one provider failure category.",
|
|
1151
|
+
"Inspect the private CI diagnostics, correct every provider-side failure, then rerun the pipeline.",
|
|
1152
|
+
),
|
|
1153
|
+
ProviderFailureReason.UNKNOWN: (
|
|
1154
|
+
"The LLM provider request failed for an unclassified safe reason.",
|
|
1155
|
+
"Inspect the private CI diagnostics and provider health, then rerun the pipeline.",
|
|
1156
|
+
),
|
|
1157
|
+
}[reason]
|
|
1120
1158
|
|
|
1121
1159
|
body_parts = [
|
|
1122
|
-
|
|
1160
|
+
summary,
|
|
1123
1161
|
"",
|
|
1162
|
+
f"- Safe classification: `{reason.value}`.",
|
|
1124
1163
|
"- Normal review comments were not published.",
|
|
1125
1164
|
"- Previous OCR review comments were preserved.",
|
|
1126
|
-
"-
|
|
1165
|
+
"- Automatic approval was not attempted.",
|
|
1166
|
+
f"- {remediation}",
|
|
1127
1167
|
]
|
|
1128
|
-
if warning_items:
|
|
1129
|
-
body_parts.extend(["", "**Provider warnings:**", *warning_items])
|
|
1130
|
-
if tool_calls_summary:
|
|
1131
|
-
body_parts.extend(["", tool_calls_summary])
|
|
1132
|
-
if mcp_usage_summary:
|
|
1133
|
-
body_parts.append(mcp_usage_summary)
|
|
1134
|
-
if token_usage_summary:
|
|
1135
|
-
body_parts.append(token_usage_summary)
|
|
1136
1168
|
|
|
1137
1169
|
response = post_review_note_bounded(
|
|
1138
1170
|
config,
|
|
@@ -1142,18 +1174,23 @@ def post_llm_provider_failure(
|
|
|
1142
1174
|
)
|
|
1143
1175
|
if response is None:
|
|
1144
1176
|
print("Failed to create OCR provider-failure note.", file=sys.stderr)
|
|
1145
|
-
|
|
1146
|
-
print_posting_failure_banner()
|
|
1147
|
-
return 1
|
|
1177
|
+
return posting_failure_exit(config, None, transaction)
|
|
1148
1178
|
|
|
1149
1179
|
if not finalize_posting(config, transaction):
|
|
1150
1180
|
return publish_failure_exit(config, transaction)
|
|
1151
1181
|
|
|
1152
|
-
print(
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1182
|
+
print(f"Open Code Review provider failure: {reason.value}.", file=sys.stderr)
|
|
1183
|
+
return 1 if strict_posting() else 0
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
def _result_provider_failure_reason(result_path: Path) -> ProviderFailureReason | None:
|
|
1187
|
+
"""Hostile-read one bounded private result and return only its closed reason."""
|
|
1188
|
+
|
|
1189
|
+
try:
|
|
1190
|
+
result = load_ocr_result(result_path)
|
|
1191
|
+
except (OcrResultMalformed, OcrResultMissing, OcrResultTooLarge):
|
|
1192
|
+
return None
|
|
1193
|
+
return provider_failure_reason(result)
|
|
1157
1194
|
|
|
1158
1195
|
|
|
1159
1196
|
def post_parse_error(config: GitLabConfig, stderr_path: Path) -> int:
|
|
@@ -1194,11 +1231,18 @@ def post_parse_error(config: GitLabConfig, stderr_path: Path) -> int:
|
|
|
1194
1231
|
return 1 if strict_posting() else 0
|
|
1195
1232
|
|
|
1196
1233
|
|
|
1197
|
-
def post_ocr_failure(
|
|
1234
|
+
def post_ocr_failure(
|
|
1235
|
+
config: GitLabConfig,
|
|
1236
|
+
stderr_path: Path,
|
|
1237
|
+
exit_code: int,
|
|
1238
|
+
result_path: Path | None = None,
|
|
1239
|
+
) -> int:
|
|
1198
1240
|
"""Post a safe failure note when OCR exits with a non-zero status.
|
|
1199
1241
|
|
|
1200
1242
|
A non-zero OCR exit code means the JSON output may be partial or misleading,
|
|
1201
|
-
so this script intentionally does not publish normal review comments.
|
|
1243
|
+
so this script intentionally does not publish normal review comments. A
|
|
1244
|
+
bounded retry-report v1 may contribute only a closed provider-failure
|
|
1245
|
+
reason; its raw diagnostics and stderr remain private.
|
|
1202
1246
|
Previous OCR bot notes are intentionally NOT cleaned up here: the last
|
|
1203
1247
|
valid review must remain visible until a successful run replaces it.
|
|
1204
1248
|
|
|
@@ -1222,6 +1266,10 @@ def post_ocr_failure(config: GitLabConfig, stderr_path: Path, exit_code: int) ->
|
|
|
1222
1266
|
if status is not None:
|
|
1223
1267
|
return post_pre_execution_status(config, status)
|
|
1224
1268
|
|
|
1269
|
+
reason = _result_provider_failure_reason(result_path) if result_path is not None else None
|
|
1270
|
+
if reason is not None:
|
|
1271
|
+
return post_llm_provider_failure(config, reason)
|
|
1272
|
+
|
|
1225
1273
|
transaction = PostingTransaction()
|
|
1226
1274
|
details_enabled = os.environ.get("OCR_POST_ERROR_DETAILS") == "1"
|
|
1227
1275
|
details = read_stderr_excerpt(stderr_path) if details_enabled else ""
|
|
@@ -1342,7 +1390,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1342
1390
|
|
|
1343
1391
|
exit_code = ocr_exit_code()
|
|
1344
1392
|
if exit_code != 0:
|
|
1345
|
-
return post_ocr_failure(config, stderr_path, exit_code)
|
|
1393
|
+
return post_ocr_failure(config, stderr_path, exit_code, result_path)
|
|
1346
1394
|
|
|
1347
1395
|
try:
|
|
1348
1396
|
result = load_ocr_result(result_path)
|
{open_code_review_toolkit-0.8.0 → open_code_review_toolkit-0.8.2}/src/ocr_toolkit/preflight.py
RENAMED
|
@@ -15,16 +15,20 @@ import urllib.request
|
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
17
|
from ocr_toolkit.common.redaction import redact_sensitive
|
|
18
|
+
from ocr_toolkit.provider_config import (
|
|
19
|
+
HEADER_NAME_RE,
|
|
20
|
+
ProviderConfigError,
|
|
21
|
+
provider_config_from_environment,
|
|
22
|
+
)
|
|
18
23
|
|
|
19
24
|
HTTP_TIMEOUT_SECONDS = 30
|
|
20
25
|
MAX_RESPONSE_BODY_BYTES = 2_000_000
|
|
21
26
|
RESPONSE_READ_CHUNK_BYTES = 64 * 1024
|
|
22
|
-
HEADER_NAME_RE = re.compile(r"^[!#$%&'*+.^_`|~0-9A-Za-z-]+$")
|
|
23
27
|
DEFAULT_REQUEST_HEADERS = {
|
|
24
28
|
"Accept": "application/json",
|
|
25
29
|
"User-Agent": "open-code-review-ci-preflight/1.0",
|
|
26
30
|
}
|
|
27
|
-
EXPECTED_OCR_VERSION = "1.
|
|
31
|
+
EXPECTED_OCR_VERSION = "1.10.0"
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
class PreflightError(Exception):
|
|
@@ -209,63 +213,10 @@ def validate_ocr_binary() -> None:
|
|
|
209
213
|
|
|
210
214
|
|
|
211
215
|
def _models_url() -> str:
|
|
212
|
-
explicit_url = _env("OCR_LLM_MODELS_URL")
|
|
213
|
-
if explicit_url:
|
|
214
|
-
return explicit_url
|
|
215
|
-
|
|
216
|
-
base = ""
|
|
217
|
-
llm_url = _env("OCR_LLM_URL")
|
|
218
|
-
if not base:
|
|
219
|
-
normalized_llm_url = llm_url.rstrip("/")
|
|
220
|
-
for endpoint in ("/chat/completions", "/responses"):
|
|
221
|
-
if normalized_llm_url.endswith(endpoint):
|
|
222
|
-
base = normalized_llm_url[: -len(endpoint)]
|
|
223
|
-
break
|
|
224
|
-
else:
|
|
225
|
-
if normalized_llm_url.endswith("/v1"):
|
|
226
|
-
base = normalized_llm_url
|
|
227
|
-
|
|
228
|
-
if not base:
|
|
229
|
-
raise PreflightError(
|
|
230
|
-
"Cannot derive LLM /models URL; set OCR_LLM_MODELS_URL or "
|
|
231
|
-
"set OCR_LLM_VALIDATE_MODEL=false"
|
|
232
|
-
)
|
|
233
|
-
return f"{base.rstrip('/')}/models"
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _parse_extra_headers(value: str) -> dict[str, str]:
|
|
237
|
-
"""Parse optional OCR LLM extra headers JSON for preflight metadata calls."""
|
|
238
|
-
|
|
239
|
-
if not value.strip():
|
|
240
|
-
return {}
|
|
241
216
|
try:
|
|
242
|
-
|
|
243
|
-
except
|
|
244
|
-
raise PreflightError(
|
|
245
|
-
if not isinstance(payload, dict):
|
|
246
|
-
raise PreflightError("OCR_LLM_EXTRA_HEADERS must be a JSON object")
|
|
247
|
-
|
|
248
|
-
headers: dict[str, str] = {}
|
|
249
|
-
for key, header_value in payload.items():
|
|
250
|
-
if not isinstance(key, str) or not HEADER_NAME_RE.fullmatch(key.strip()):
|
|
251
|
-
raise PreflightError("OCR_LLM_EXTRA_HEADERS contains an invalid header name")
|
|
252
|
-
if not isinstance(header_value, str):
|
|
253
|
-
raise PreflightError("OCR_LLM_EXTRA_HEADERS contains a non-string header value")
|
|
254
|
-
if "\r" in header_value or "\n" in header_value:
|
|
255
|
-
raise PreflightError("OCR_LLM_EXTRA_HEADERS contains an invalid header value")
|
|
256
|
-
headers[key.strip()] = header_value
|
|
257
|
-
return headers
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def _llm_headers(token: str) -> dict[str, str]:
|
|
261
|
-
"""Build the same auth/header set used by OCR's llm.* configuration."""
|
|
262
|
-
|
|
263
|
-
auth_header = _env("OCR_LLM_AUTH_HEADER", "Authorization") or "Authorization"
|
|
264
|
-
if not HEADER_NAME_RE.fullmatch(auth_header):
|
|
265
|
-
raise PreflightError("OCR_LLM_AUTH_HEADER is not a valid HTTP header name")
|
|
266
|
-
headers = _parse_extra_headers(_env("OCR_LLM_EXTRA_HEADERS"))
|
|
267
|
-
headers[auth_header] = f"Bearer {token}"
|
|
268
|
-
return headers
|
|
217
|
+
return provider_config_from_environment().require_models_url()
|
|
218
|
+
except ProviderConfigError as exc:
|
|
219
|
+
raise PreflightError(str(exc)) from exc
|
|
269
220
|
|
|
270
221
|
|
|
271
222
|
def _context_length(model: dict[str, Any]) -> int:
|
|
@@ -320,9 +271,11 @@ def _validate_allowed_model(model_id: str) -> bool:
|
|
|
320
271
|
def validate_llm_model() -> None:
|
|
321
272
|
"""Verify the selected model exists in OpenAI-compatible metadata."""
|
|
322
273
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
274
|
+
try:
|
|
275
|
+
provider = provider_config_from_environment()
|
|
276
|
+
model_id = provider.require_model()
|
|
277
|
+
except ProviderConfigError as exc:
|
|
278
|
+
raise PreflightError(str(exc)) from exc
|
|
326
279
|
allowlist_matched = _validate_allowed_model(model_id)
|
|
327
280
|
|
|
328
281
|
validate_mode = _env("OCR_LLM_VALIDATE_MODEL", "false").lower()
|
|
@@ -332,15 +285,15 @@ def validate_llm_model() -> None:
|
|
|
332
285
|
if validate_mode not in {"true", "1", "yes", "on", "auto"}:
|
|
333
286
|
raise PreflightError("OCR_LLM_VALIDATE_MODEL must be true, false, or auto")
|
|
334
287
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
288
|
+
try:
|
|
289
|
+
headers = provider.request_headers()
|
|
290
|
+
except ProviderConfigError as exc:
|
|
291
|
+
raise PreflightError(str(exc)) from exc
|
|
339
292
|
|
|
340
293
|
print("Validating OCR model against LLM gateway metadata")
|
|
341
294
|
try:
|
|
342
|
-
models_url =
|
|
343
|
-
except
|
|
295
|
+
models_url = provider.require_models_url()
|
|
296
|
+
except ProviderConfigError as exc:
|
|
344
297
|
if validate_mode == "auto" and allowlist_matched:
|
|
345
298
|
print(
|
|
346
299
|
"OCR LLM /models URL unavailable; continuing because "
|
|
@@ -348,7 +301,7 @@ def validate_llm_model() -> None:
|
|
|
348
301
|
file=sys.stderr,
|
|
349
302
|
)
|
|
350
303
|
return
|
|
351
|
-
raise
|
|
304
|
+
raise PreflightError(str(exc)) from exc
|
|
352
305
|
try:
|
|
353
306
|
payload = _request_json(models_url, headers)
|
|
354
307
|
except PreflightError as exc:
|