litellm/tests/rust-python-harness/shared/unit_runners/contracts.py
devin-ai-integration[bot] e2302be068
refactor(ocr): remove the Python OCR execution path and require the Rust route (#43081)
* refactor(ocr): remove the Python OCR execution path and require the Rust route

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fmt

* refactor(ocr): tidy the native OCR passthrough binding

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* style(ocr): ruff format the azure passthrough transformation

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(ocr): resolve passthrough OCR costing in one Rust call

Replace passthrough_url/passthrough_transform with passthrough_response,
which matches the relayed endpoint against each Azure config's path
segments instead of building a fake request to call get_complete_url.
The binding drops the unused headers, status and api_base arguments.

Catch the ValueError/RuntimeError the binding raises so a relayed body
that is not OCR-shaped falls back to the passthrough object instead of
failing logging, and cover the relay against the real binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ocr): drop the unused LlmProviders import from health check helpers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: drop the ocr_testing job now that tests/ocr_tests is gone

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ocr): restore the live OCR matrix and the ocr_testing job

The public litellm.ocr / aocr / Router interface is unchanged by the Rust
migration, so the live provider matrix still applies. Drops the stale VCR skip
list for the deleted test_rust_bridge.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ocr): import Final in the health check helper tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Yujong Lee <yujong@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 18:18:50 -07:00

88 lines
2.8 KiB
Python

from __future__ import annotations
from collections import Counter
from collections.abc import Mapping
from types import MappingProxyType
from typing import Final
from pydantic import BaseModel, ConfigDict, field_validator, model_validator
from typing_extensions import Self
from ..reporting.models import SdkFunction
class _ContractModel(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
def _clean_unique(values: tuple[str, ...], field: str) -> tuple[str, ...]:
cleaned: Final = tuple(value.strip().rstrip("/") for value in values)
if not cleaned or any(not value for value in cleaned):
raise ValueError(f"{field} must contain non-empty paths")
duplicates: Final = tuple(value for value, count in Counter(cleaned).items() if count > 1)
if duplicates:
raise ValueError(f"{field} contains duplicates: {sorted(duplicates)}")
return cleaned
class UnitParityExclusionSpec(_ContractModel):
nodeid: str
reason: str
@field_validator("nodeid", "reason")
@classmethod
def validate_fields(cls, value: str) -> str:
stripped: Final = value.strip()
if not stripped:
raise ValueError("must be a non-empty string")
return stripped
class UnitParitySpec(_ContractModel):
python_selectors: tuple[str, ...]
exclusions: tuple[UnitParityExclusionSpec, ...] = ()
@field_validator("python_selectors")
@classmethod
def validate_python_selectors(cls, value: tuple[str, ...]) -> tuple[str, ...]:
return _clean_unique(value, "unit parity python_selectors")
@model_validator(mode="after")
def validate_exclusions(self) -> Self:
nodeids: Final = tuple(exclusion.nodeid for exclusion in self.exclusions)
duplicates: Final = tuple(nodeid for nodeid, count in Counter(nodeids).items() if count > 1)
if duplicates:
raise ValueError(f"unit parity exclusions contain duplicate nodeids: {sorted(duplicates)}")
return self
class RustUnitSpec(_ContractModel):
cargo_manifest: str
cargo_filter: str
cargo_package: str | None = None
@field_validator("cargo_manifest", "cargo_filter")
@classmethod
def validate_required_fields(cls, value: str) -> str:
stripped: Final = value.strip()
if not stripped:
raise ValueError("must be a non-empty string")
return stripped
@field_validator("cargo_package")
@classmethod
def validate_package(cls, value: str | None) -> str | None:
if value is None:
return None
stripped: Final = value.strip()
if not stripped:
raise ValueError("must be a non-empty string when provided")
return stripped
class UnitTestContract(_ContractModel):
unit_parity: UnitParitySpec
rust: RustUnitSpec
UNIT_TEST_CONTRACTS: Final[Mapping[SdkFunction, UnitTestContract]] = MappingProxyType({})