test(ocr): expand provider boundary fixtures

This commit is contained in:
Yujong Lee 2026-09-01 13:05:50 -07:00
parent a555e8e98b
commit dc7ddfc349
48 changed files with 3159 additions and 282 deletions

View file

@ -38,6 +38,24 @@ class VertexAIDeepSeekOCRConfig(BaseOCRConfig):
super().__init__()
self.vertex_base = VertexBase()
def get_supported_ocr_params(self, model: str) -> list[str]: # mutable-ok: base contract
return ["stream", "temperature", "max_tokens", "top_p", "n", "stop"] # mutable-ok: base contract
def map_ocr_params(
self,
non_default_params: dict[str, object], # mutable-ok: base contract
optional_params: dict[str, object], # mutable-ok: base contract
model: str,
) -> dict[str, object]: # mutable-ok: base contract
return { # mutable-ok: base contract
**optional_params,
**{ # mutable-ok: base contract
key: value
for key, value in non_default_params.items()
if key in self.get_supported_ocr_params(model)
},
}
def get_api_key_env_var(self) -> str | None:
return VERTEX_AI_DEEPSEEK_OCR_API_KEY_ENV_VAR
@ -177,8 +195,9 @@ class VertexAIDeepSeekOCRConfig(BaseOCRConfig):
content_item = {"type": "image_url", "image_url": document_url}
# Build DeepSeek OCR request
upstream_model: Final = model if model.startswith("deepseek-ai/") else f"deepseek-ai/{model}"
data: Final = {
"model": "deepseek-ai/" + model,
"model": upstream_model,
"messages": [{"role": "user", "content": [content_item]}],
}

View file

@ -32,6 +32,22 @@
- Provider responses are replayed unchanged, so the parity test does not fuzz or validate provider behavior
- Because Hypothesis does not run the parity assertion directly, parity failures are not automatically shrunk
## OCR input boundaries
OCR strategies generate only public `litellm.ocr()` and `litellm.aocr()` inputs. Every case contains the normalized
`model`, Mistral-shaped `document`, optional `custom_llm_provider`, and LiteLLM keyword arguments. The fixture-only
`boundary` tag selects the valid keyword-argument set and is removed before calling the SDK. Strategies never build
provider wire payloads.
Each boundary has a required corpus containing a baseline and one case for every supported top-level LiteLLM OCR
parameter. `--examples` controls additional Hypothesis-generated cases; it does not replace the required corpus.
The explicit boundaries are Mistral, Azure-hosted Mistral, Vertex-hosted Mistral, Azure Document Intelligence,
Vertex DeepSeek, Reducto v3, and Reducto legacy. Provider credentials and endpoints only control target discovery, so
a machine records the boundaries it has configured and skips the rest. Reducto fixtures record both upload and parse
responses, but Reducto remains outside Python/Rust parity until the Rust OCR bridge supports it.
Azure-hosted Mistral discovery also requires `AZURE_AI_OCR_MODEL`, the deployment name or full `azure_ai/...` model.
## References
- [Hypothesis documentation](https://hypothesis.readthedocs.io/en/latest/)

View file

@ -10,23 +10,27 @@ ModelT = TypeVar("ModelT", bound=BaseModel)
def validate_harness(python: Execution, accelerated: Execution, python_user_agent: str) -> None:
if python.request.user_agent != python_user_agent:
raise AssertionError(
f"Python provider request did not carry fallback sentinel user-agent {python_user_agent!r}: "
f"{python.request.user_agent!r}"
)
if accelerated.request.user_agent == python_user_agent:
raise AssertionError("accelerated route fell back to the Python HTTP implementation")
for request in python.requests:
if request.user_agent != python_user_agent:
raise AssertionError(
f"Python provider request did not carry fallback sentinel user-agent {python_user_agent!r}: "
f"{request.user_agent!r}"
)
for request in accelerated.requests:
if request.user_agent == python_user_agent:
raise AssertionError("accelerated route fell back to the Python HTTP implementation")
def _request_after_transformation(request: CapturedRequest) -> CapturedRequest:
return request.model_copy(update={"user_agent": None})
def assert_request_parity(python: CapturedRequest, accelerated: CapturedRequest) -> None:
python_request: Final = _request_after_transformation(python)
accelerated_request: Final = _request_after_transformation(accelerated)
assert python_request == accelerated_request
def assert_request_parity(
python: tuple[CapturedRequest, ...], accelerated: tuple[CapturedRequest, ...]
) -> None:
python_requests: Final = tuple(_request_after_transformation(request) for request in python)
accelerated_requests: Final = tuple(_request_after_transformation(request) for request in accelerated)
assert python_requests == accelerated_requests
def public_model_copy(model: ModelT) -> ModelT:
@ -42,5 +46,5 @@ def assert_model_parity(python: BaseModel, accelerated: BaseModel) -> None:
def assert_parity(python: Execution, accelerated: Execution, python_user_agent: str) -> None:
validate_harness(python, accelerated, python_user_agent)
assert_request_parity(python.request, accelerated.request)
assert_request_parity(python.requests, accelerated.requests)
assert python.report.response == accelerated.report.response

View file

@ -31,6 +31,7 @@ class FixtureTarget(Generic[InputT]):
provider_spec: ProviderSpec
strategy: SearchStrategy[InputT]
invoke: Callable[[str, InputT], object]
required_inputs: tuple[InputT, ...] = ()
def generate_target_fixtures(
@ -40,10 +41,11 @@ def generate_target_fixtures(
concurrency: int,
case_type: type[CaseT],
) -> None:
case_inputs: Final = generate_case_inputs(target.strategy, examples)
generated_inputs: Final = generate_case_inputs(target.strategy, examples)
case_inputs: Final = (*target.required_inputs, *generated_inputs)
results: Final = record_cases(
target.provider_spec,
root,
root / target.name,
case_inputs,
target.invoke,
case_type,

View file

@ -1,8 +1,9 @@
from __future__ import annotations
from typing import Generic, Literal, TypeVar, cast
from collections.abc import Mapping
from typing import Final, Generic, Literal, TypeVar, cast
from pydantic import BaseModel, ConfigDict, Field, JsonValue
from pydantic import BaseModel, ConfigDict, Field, JsonValue, model_validator
from tests.route_parity.recorded_http import RecordedResponse
@ -14,11 +15,15 @@ class FixtureModel(BaseModel):
class SdkInputBase(FixtureModel):
boundary: str = "default"
def as_sdk_kwargs(self) -> dict[str, object]:
return cast(dict[str, object], self.model_dump(mode="python", exclude_unset=True))
dumped: Final = cast(dict[str, object], self.model_dump(mode="python", exclude_unset=True))
return {key: value for key, value in dumped.items() if key != "boundary"}
def canonical_input(self) -> dict[str, object]:
return cast(dict[str, object], self.model_dump(mode="json", exclude_unset=True))
dumped: Final = cast(dict[str, object], self.model_dump(mode="json", exclude_unset=True))
return {"boundary": self.boundary, **{key: value for key, value in dumped.items() if key != "boundary"}}
class JsonSchemaDefinition(FixtureModel):
@ -38,4 +43,36 @@ InputT = TypeVar("InputT", bound=SdkInputBase)
class ParityCase(FixtureModel, Generic[InputT]):
litellm_input: InputT
provider_response: RecordedResponse
provider_responses: tuple[RecordedResponse, ...]
@model_validator(mode="before")
@classmethod
def load_legacy_single_response(cls, value: object) -> object:
if not isinstance(value, Mapping):
return value
migrated: Final = dict(value)
provider_response: Final = migrated.pop("provider_response", None)
if "provider_responses" not in migrated and provider_response is not None:
migrated["provider_responses"] = (provider_response,)
litellm_input: Final = migrated.get("litellm_input")
if isinstance(litellm_input, Mapping) and "boundary" not in litellm_input:
model: Final = litellm_input.get("model")
if isinstance(model, str):
migrated["litellm_input"] = {"boundary": _legacy_boundary(model), **litellm_input}
return migrated
def _legacy_boundary(model: str) -> str:
if model.startswith("azure_ai/doc-intelligence/"):
return "azure_document_intelligence"
if model.startswith("azure_ai/"):
return "azure_mistral"
if model.startswith("vertex_ai/deepseek"):
return "vertex_deepseek"
if model.startswith("vertex_ai/"):
return "vertex_mistral"
if model.endswith("parse-v3"):
return "reducto_v3"
if model.endswith("parse-legacy"):
return "reducto_legacy"
return "mistral"

View file

@ -12,6 +12,7 @@ from datetime import datetime, timezone
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from typing import Final, Generic, Protocol, TypeVar, cast
from urllib.parse import urlsplit, urlunsplit
import httpx
import pytest
@ -29,6 +30,7 @@ from tests.route_parity.recorded_http import (
)
FIXTURE_SCHEMA_VERSION: Final = 1
_PARITY_PROVIDER_HOST: Final = "parity-provider.invalid"
_HOP_BY_HOP_HEADERS: Final = frozenset(
{
@ -82,7 +84,29 @@ def _excluded_headers(headers: tuple[tuple[str, str], ...]) -> frozenset[str]:
def _end_to_end_headers(headers: httpx.Headers) -> tuple[HttpHeader, ...]:
decoded: Final = tuple((name.decode("ascii"), value.decode("latin-1")) for name, value in headers.raw)
excluded: Final = _excluded_headers(decoded) | {"content-encoding", "content-length"}
return tuple(HttpHeader(name=name, value=value) for name, value in decoded if name.lower() not in excluded)
return tuple(
HttpHeader(name=name, value=_normalized_response_header(name, value))
for name, value in decoded
if name.lower() not in excluded
)
def _normalized_response_header(name: str, value: str) -> str:
if name.lower() not in {"location", "operation-location"}:
return value
parsed: Final = urlsplit(value)
if not parsed.netloc:
return value
return urlunsplit(("http", _PARITY_PROVIDER_HOST, parsed.path, parsed.query, parsed.fragment))
def _local_response_header(name: str, value: str, provider_url: str) -> str:
if name.lower() not in {"location", "operation-location"}:
return value
parsed: Final = urlsplit(value)
if parsed.hostname != _PARITY_PROVIDER_HOST:
return value
return f"{provider_url}{parsed.path}{'?' + parsed.query if parsed.query else ''}"
class _RecordingProvider(ThreadingHTTPServer):
@ -97,21 +121,29 @@ class _RecordingProvider(ThreadingHTTPServer):
def url(self) -> str:
return f"http://127.0.0.1:{self.server_address[1]}"
def take_response(self) -> RecordedResponse:
def take_responses(self) -> tuple[RecordedResponse, ...]:
try:
return self.responses.get(timeout=5)
first: Final = self.responses.get(timeout=5)
except queue.Empty as error:
raise RuntimeError("successful SDK call did not produce a recorded response") from error
remaining: Final = tuple(self.responses.get_nowait() for _ in range(self.responses.qsize()))
return (first, *remaining)
class _RecordingHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def do_POST(self) -> None:
self._forward()
def do_GET(self) -> None:
self._forward()
def _forward(self) -> None:
provider: Final = self.server
assert isinstance(provider, _RecordingProvider)
length: Final = int(self.headers.get("content-length") or "0")
request_body: Final = self.rfile.read(length)
request_body: Final = self.rfile.read(length) if length else b""
raw_headers: Final = tuple(self.headers.raw_items())
excluded: Final = _excluded_headers(raw_headers) | {"host", "content-length"}
forwarded_headers: Final = tuple((name, value) for name, value in raw_headers if name.lower() not in excluded)
@ -160,7 +192,9 @@ class _RecordingHandler(BaseHTTPRequestHandler):
) -> RecordedHttpStreamResponse:
self.send_response_only(upstream.status_code)
for header in headers:
self.send_header(header.name, header.value)
provider: Final = self.server
assert isinstance(provider, _RecordingProvider)
self.send_header(header.name, _local_response_header(header.name, header.value, provider.url))
self.send_header("transfer-encoding", "chunked")
self.end_headers()
chunks: Final = tuple(self._relay_chunks(upstream.iter_bytes()))
@ -184,7 +218,9 @@ class _RecordingHandler(BaseHTTPRequestHandler):
def _send_response(self, status_code: int, headers: tuple[HttpHeader, ...], body: bytes) -> None:
self.send_response_only(status_code)
for header in headers:
self.send_header(header.name, header.value)
provider: Final = self.server
assert isinstance(provider, _RecordingProvider)
self.send_header(header.name, _local_response_header(header.name, header.value, provider.url))
self.send_header("content-length", str(len(body)))
self.end_headers()
self.wfile.write(body)
@ -252,9 +288,9 @@ def record_case(
with _recording_provider(spec) as recorder:
sdk_call(recorder.url, case_input)
upstream_response: Final = recorder.take_response()
upstream_responses: Final = recorder.take_responses()
case: Final = case_type.model_validate({"litellm_input": case_input, "provider_response": upstream_response})
case: Final = case_type.model_validate({"litellm_input": case_input, "provider_responses": upstream_responses})
envelope: Final = FixtureEnvelope(
schema_version=FIXTURE_SCHEMA_VERSION,
recorded_at=datetime.now(timezone.utc),

View file

@ -13,19 +13,20 @@ ResponseT = TypeVar("ResponseT")
@dataclass(frozen=True, slots=True)
class InProcessExecution(Generic[ResponseT]):
request: CapturedRequest
requests: tuple[CapturedRequest, ...]
response: ResponseT
def run_in_process(
provider: ReplayServer,
recorded_response: RecordedResponse,
recorded_responses: tuple[RecordedResponse, ...],
call: Callable[[str], ResponseT],
) -> InProcessExecution[ResponseT]:
provider.enqueue_response(recorded_response)
for recorded_response in recorded_responses:
provider.enqueue_response(recorded_response)
try:
response = call(provider.url)
return InProcessExecution(request=provider.take_request(), response=response)
return InProcessExecution(requests=provider.take_requests(len(recorded_responses)), response=response)
except Exception:
provider.reset()
raise

View file

@ -24,7 +24,7 @@ class SDKReport(BaseModel):
class Execution(BaseModel):
model_config = ConfigDict(frozen=True)
request: CapturedRequest
requests: tuple[CapturedRequest, ...]
report: SDKReport

View file

@ -1,5 +1,6 @@
from __future__ import annotations
import base64
import queue
import threading
from collections.abc import Generator
@ -9,6 +10,7 @@ from typing import Final
from pydantic import JsonValue, TypeAdapter
from tests.route_parity.fixture_recorder import _local_response_header
from tests.route_parity.models import CapturedRequest
from tests.route_parity.recorded_http import RecordedHttpResponse, RecordedHttpStreamResponse, RecordedResponse
@ -41,11 +43,11 @@ class ReplayServer(ThreadingHTTPServer):
def enqueue_response(self, response: RecordedResponse) -> None:
self.responses.put(response)
def take_request(self) -> CapturedRequest:
def take_requests(self, expected_count: int) -> tuple[CapturedRequest, ...]:
request_count: Final = self.requests.qsize()
if request_count != 1:
raise AssertionError(f"expected exactly one provider request, received {request_count}")
return self.requests.get_nowait()
if request_count != expected_count:
raise AssertionError(f"expected exactly {expected_count} provider requests, received {request_count}")
return tuple(self.requests.get_nowait() for _ in range(request_count))
def reset(self) -> None:
while not self.responses.empty():
@ -58,10 +60,24 @@ class _ReplayHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def do_POST(self) -> None:
self._replay()
def do_GET(self) -> None:
self._replay()
def _replay(self) -> None:
provider: Final = self.server
assert isinstance(provider, ReplayServer)
length: Final = int(self.headers.get("content-length") or "0")
body: Final = JSON_VALUE.validate_json(self.rfile.read(length))
raw_body: Final = self.rfile.read(length) if length else b""
content_type: Final = self.headers.get("content-type", "")
body: Final = (
JSON_VALUE.validate_json(raw_body)
if raw_body and content_type.lower().startswith("application/json")
else base64.b64encode(raw_body).decode("ascii")
if raw_body
else None
)
headers: Final = tuple(
sorted(
(name.lower(), value)
@ -86,7 +102,7 @@ class _ReplayHandler(BaseHTTPRequestHandler):
self.send_response_only(response.status_code)
for header in response.headers:
if header.name.lower() not in EXCLUDED_RESPONSE_HEADERS:
self.send_header(header.name, header.value)
self.send_header(header.name, _local_response_header(header.name, header.value, provider.url))
if isinstance(response, RecordedHttpResponse):
response_body: Final = response.body_bytes()
self.send_header("content-length", str(len(response_body)))

View file

@ -73,12 +73,13 @@ class PythonScriptWorker:
self,
case_file: Path,
route: str,
response: RecordedResponse,
responses: tuple[RecordedResponse, ...],
) -> Execution:
stdin: Final = self.process.stdin
if stdin is None or self.process.poll() is not None:
raise AssertionError(f"{self.mode} {self.route_label} worker exited before processing {case_file}")
self.provider.enqueue_response(response)
for response in responses:
self.provider.enqueue_response(response)
command: Final = SDKCommand(case_file=str(case_file), route=route)
try:
stdin.write(f"{command.model_dump_json()}\n")
@ -103,7 +104,7 @@ class PythonScriptWorker:
)
assert isinstance(result, WorkerSuccess)
try:
return Execution(request=self.provider.take_request(), report=result.report)
return Execution(requests=self.provider.take_requests(len(responses)), report=result.report)
except AssertionError:
self.provider.reset()
raise
@ -160,9 +161,9 @@ def run_execution(
worker: PythonScriptWorker,
case_file: Path,
route: str,
response: RecordedResponse,
responses: tuple[RecordedResponse, ...],
) -> Execution:
return worker.execute(case_file, route, response)
return worker.execute(case_file, route, responses)
@contextmanager

View file

@ -1,7 +1,7 @@
from __future__ import annotations
import threading
from collections.abc import Generator
from collections.abc import Callable, Generator
from contextlib import contextmanager
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
@ -50,7 +50,7 @@ class _ParityCase(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
litellm_input: _FixtureInput
provider_response: RecordedResponse
provider_responses: tuple[RecordedResponse, ...]
class _ControlledUpstream(ThreadingHTTPServer):
@ -90,6 +90,20 @@ class _ControlledUpstreamHandler(BaseHTTPRequestHandler):
assert isinstance(upstream, _ControlledUpstream)
length: Final = int(self.headers.get("content-length") or "0")
self.rfile.read(length)
if self.path == "/upload":
self._send_json(200, b'{"file_id":"reducto://fixture.pdf"}')
return
if self.path == "/parse":
self._send_json(200, b'{"result":{"chunks":[]}}')
return
if self.path == "/analyze":
upstream: Final = self.server
assert isinstance(upstream, _ControlledUpstream)
self.send_response(202)
self.send_header("operation-location", f"{upstream.url}/results/1")
self.send_header("content-length", "0")
self.end_headers()
return
if self.path == "/v1/chat/completions":
with upstream.lock:
upstream.request_count += 1
@ -116,6 +130,19 @@ class _ControlledUpstreamHandler(BaseHTTPRequestHandler):
finally:
upstream.end_tracked_request()
def do_GET(self) -> None:
if self.path == "/results/1":
self._send_json(200, b'{"status":"succeeded","analyzeResult":{"pages":[]}}')
return
self.send_error(404)
def _send_json(self, status: int, body: bytes) -> None:
self.send_response(status)
self.send_header("content-type", "application/json")
self.send_header("content-length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, format: str, *args: object) -> None:
return
@ -145,6 +172,22 @@ def _stream_sdk_call(api_base: str, case_input: _FixtureInput) -> object:
return httpx.post(f"{api_base}/v1/chat/completions", content=b"{}", timeout=5)
def _multi_sdk_call(api_base: str, case_input: _FixtureInput) -> object:
upload: Final = httpx.post(f"{api_base}/upload", json={"document": case_input.identifier}, timeout=5)
upload.raise_for_status()
parsed: Final = httpx.post(f"{api_base}/parse", json={"input": upload.json()["file_id"]}, timeout=5)
parsed.raise_for_status()
return parsed
def _polling_sdk_call(api_base: str, case_input: _FixtureInput) -> object:
started: Final = httpx.post(f"{api_base}/analyze", json={"document": case_input.identifier}, timeout=5)
operation_location: Final = started.headers["operation-location"]
completed: Final = httpx.get(operation_location, timeout=5)
completed.raise_for_status()
return completed
def test_generate_case_inputs_is_deterministic() -> None:
strategy: Final = st.builds(_FixtureInput, identifier=st.integers().map(str))
@ -192,7 +235,7 @@ def test_streaming_response_records_and_replays_chunks(tmp_path: Path) -> None:
_ParityCase,
)
response: Final = result.case.provider_response
response: Final = result.case.provider_responses[0]
assert isinstance(response, RecordedHttpStreamResponse)
assert tuple(chunk.data_bytes() for chunk in response.chunks) == _SSE_CHUNKS
@ -200,7 +243,7 @@ def test_streaming_response_records_and_replays_chunks(tmp_path: Path) -> None:
provider.enqueue_response(response)
with httpx.stream("POST", f"{provider.url}/v1/chat/completions", json={}) as replayed:
replayed_chunks: Final = tuple(replayed.iter_raw())
provider.take_request()
provider.take_requests(1)
assert replayed_chunks == _SSE_CHUNKS
@ -216,3 +259,27 @@ def test_stream_response_model_rejects_buffered_body() -> None:
"body_b64": "",
}
)
@pytest.mark.parametrize("sdk_call", (_multi_sdk_call, _polling_sdk_call))
def test_multiple_provider_calls_record_and_replay_in_order(
tmp_path: Path,
sdk_call: Callable[[str, _FixtureInput], object],
) -> None:
with _controlled_upstream() as upstream:
result: Final = record_case(
ProviderSpec(upstream_base=upstream.url),
tmp_path,
_case(sdk_call.__name__),
sdk_call,
_ParityCase,
)
assert len(result.case.provider_responses) == 2
with replay_server() as provider:
for response in result.case.provider_responses:
provider.enqueue_response(response)
sdk_call(provider.url, _case(sdk_call.__name__))
requests: Final = provider.take_requests(2)
assert len(requests) == 2

View file

@ -25,12 +25,14 @@ class _DifferentResponse(BaseModel):
def _execution(*, body: JsonValue = None, markdown: str = "same", user_agent: str | None = None) -> Execution:
return Execution(
request=CapturedRequest(
method="POST",
path="/v1/test-route?mode=test",
headers=(("authorization", "Bearer test-key"), ("content-type", "application/json")),
body={"model": "test-model"} if body is None else body,
user_agent=user_agent,
requests=(
CapturedRequest(
method="POST",
path="/v1/test-route?mode=test",
headers=(("authorization", "Bearer test-key"), ("content-type", "application/json")),
body={"model": "test-model"} if body is None else body,
user_agent=user_agent,
),
),
report=SDKReport(response={"items": [{"text": markdown}], "model": "test-model"}),
)

View file

@ -27,7 +27,7 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
env_var=FIXTURE_DIR_ENV,
default_directory=default_directory,
regeneration_command=(
f"uv run python tests/test_litellm/ocr/generate_fixtures.py --fixture-dir {default_directory}"
f"uv run python -m tests.test_litellm.ocr.generate_fixtures --fixture-dir {default_directory}"
),
id_builder=_fixture_id,
)

View file

@ -16,7 +16,13 @@ from tests.route_parity.fixture_models import (
SdkInputBase,
)
__all__ = ("JsonSchemaDefinition", "JsonSchemaResponseFormat", "OcrParityCase", "OcrSdkInputBase")
__all__ = (
"JsonSchemaDefinition",
"JsonSchemaResponseFormat",
"OcrParityCase",
"OcrSdkInput",
"OcrSdkInputBase",
)
OcrSdkInputBase = SdkInputBase
@ -57,10 +63,8 @@ MistralModel = Literal[
]
class MistralOcrSdkInput(OcrSdkInputBase):
model: MistralModel
class MistralCompatibleOcrSdkInput(OcrSdkInputBase):
document: MistralDocument
custom_llm_provider: Literal["mistral"] | None = None
pages: str | list[int] | None = None
include_image_base64: bool | None = None
image_limit: int | None = None
@ -76,14 +80,74 @@ class MistralOcrSdkInput(OcrSdkInputBase):
id: str | None = None
@model_validator(mode="after")
def validate_provider_routing(self) -> Self:
if not self.model.startswith("mistral/") and self.custom_llm_provider != "mistral":
raise ValueError("unqualified Mistral models require custom_llm_provider='mistral'")
def validate_annotation_prompt(self) -> Self:
if self.document_annotation_prompt is not None and self.document_annotation_format is None:
raise ValueError("document_annotation_prompt requires document_annotation_format")
return self
class MistralOcrSdkInput(MistralCompatibleOcrSdkInput):
boundary: Literal["mistral"] = "mistral"
model: MistralModel
custom_llm_provider: Literal["mistral"] | None = None
@model_validator(mode="after")
def validate_provider_routing(self) -> Self:
if not self.model.startswith("mistral/") and self.custom_llm_provider != "mistral":
raise ValueError("unqualified Mistral models require custom_llm_provider='mistral'")
return self
class AzureMistralOcrSdkInput(MistralCompatibleOcrSdkInput):
boundary: Literal["azure_mistral"] = "azure_mistral"
model: str
custom_llm_provider: Literal["azure_ai"] | None = None
@field_validator("model")
@classmethod
def validate_model_namespace(cls, model: str) -> str:
if not model.startswith("azure_ai/"):
raise ValueError("Azure Mistral models must use the azure_ai/ LiteLLM namespace")
return model
class VertexMistralOcrSdkInput(MistralCompatibleOcrSdkInput):
boundary: Literal["vertex_mistral"] = "vertex_mistral"
model: Literal["vertex_ai/mistral-ocr-2505"] = "vertex_ai/mistral-ocr-2505"
custom_llm_provider: Literal["vertex_ai"] | None = None
vertex_project: str
vertex_location: str = "us-central1"
class AzureDocumentIntelligenceOcrSdkInput(OcrSdkInputBase):
boundary: Literal["azure_document_intelligence"] = "azure_document_intelligence"
model: Literal[
"azure_ai/doc-intelligence/prebuilt-read",
"azure_ai/doc-intelligence/prebuilt-layout",
"azure_ai/doc-intelligence/prebuilt-document",
]
document: MistralDocument
custom_llm_provider: Literal["azure_ai"] | None = None
pages: str | list[int] | None = None
features: str | list[str] | None = None
req_format: Literal["litellm"] = "litellm"
class VertexDeepSeekOcrSdkInput(OcrSdkInputBase):
boundary: Literal["vertex_deepseek"] = "vertex_deepseek"
model: Literal["vertex_ai/deepseek-ai/deepseek-ocr-maas"] = "vertex_ai/deepseek-ai/deepseek-ocr-maas"
document: MistralDocument
custom_llm_provider: Literal["vertex_ai"] | None = None
vertex_project: str
vertex_location: str = "us-central1"
stream: bool | None = None
temperature: float | None = None
max_tokens: int | None = None
top_p: float | None = None
n: int | None = None
stop: str | list[str] | None = None
def _validate_reducto_source(source: str) -> str:
if source.startswith("reducto://"):
return source
@ -243,6 +307,7 @@ class ReductoSettings(FixtureModel):
class ReductoParseV3SdkInput(OcrSdkInputBase):
boundary: Literal["reducto_v3"] = "reducto_v3"
model: Literal["reducto/parse-v3", "parse-v3"]
document: ReductoDocument
custom_llm_provider: Literal["reducto"] | None = None
@ -258,6 +323,7 @@ class ReductoParseV3SdkInput(OcrSdkInputBase):
class ReductoParseLegacySdkInput(OcrSdkInputBase):
boundary: Literal["reducto_legacy"] = "reducto_legacy"
model: Literal["reducto/parse-legacy", "parse-legacy"]
document: ReductoDocument
custom_llm_provider: Literal["reducto"] | None = None
@ -270,5 +336,17 @@ class ReductoParseLegacySdkInput(OcrSdkInputBase):
return self
class OcrParityCase(ParityCase[MistralOcrSdkInput]):
OcrSdkInput = Annotated[
MistralOcrSdkInput
| AzureMistralOcrSdkInput
| VertexMistralOcrSdkInput
| AzureDocumentIntelligenceOcrSdkInput
| VertexDeepSeekOcrSdkInput
| ReductoParseV3SdkInput
| ReductoParseLegacySdkInput,
Field(discriminator="boundary"),
]
class OcrParityCase(ParityCase[OcrSdkInput]):
pass

View file

@ -1,93 +0,0 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"id": "case-1",
"model": "mistral/mistral-ocr-latest"
},
"provider_response": {
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Sat, 29 Aug 2026 23:18:02 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=xhZ4oMdiKbThFP71tBxvQtwQRHpnzzS31o6bhmCrX_o-1788045481.8243725-1.0.1.1-ju2kNNBEq.Ff7jRRXRNgRw7jODlxu2bjMlA9ni_JBOIo6Mw.5QKyVVN8wN38J.gLItzZCZAKXFSCWAqVM39oZ9NMBCNTuwXEbf6bM5NIOCn0LgE2DrvxielM2VJkpDan; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Sat, 29 Aug 2026 23:48:02 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a04fd0-d7b3-7279-a78d-c48ecdcde419"
},
{
"name": "x-kong-request-id",
"value": "01a04fd0-d7b3-7279-a78d-c48ecdcde419"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "59"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "379"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "380"
},
{
"name": "x-kong-proxy-latency",
"value": "12"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a32f45c56be31975-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
},
"recorded_at": "2026-08-29T23:18:02.736046Z",
"schema_version": 1
}

View file

@ -1,92 +0,0 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"model": "mistral/mistral-ocr-latest"
},
"provider_response": {
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Sat, 29 Aug 2026 23:18:02 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=4Y4BNG5seFc4759BsUBjy4Z2odUy3Rgf0SOYkyESdPA-1788045481.8373353-1.0.1.1-eirQC2neaOT8r6_9qxcWeUjOMH7L1NmsXj2YyMbcVmTO62TWrDCjf7VqxKigiJ1lViQQkJQwVpMsSHTfUOaziX4zDTloRi4b.oaA37lqE.gBbH9FylA_nMTl2mYg65X9; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Sat, 29 Aug 2026 23:48:02 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a04fd0-d7b8-799c-9ac2-48ab880605d6"
},
{
"name": "x-kong-request-id",
"value": "01a04fd0-d7b8-799c-9ac2-48ab880605d6"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "59"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "355"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "356"
},
{
"name": "x-kong-proxy-latency",
"value": "17"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a32f45c57f8e7834-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
},
"recorded_at": "2026-08-29T23:18:02.735198Z",
"schema_version": 1
}

View file

@ -0,0 +1,142 @@
{
"case": {
"litellm_input": {
"bbox_annotation_format": {
"json_schema": {
"description": "Extract the visible document fields",
"name": "bounding_boxes",
"schema": {
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
}
},
"required": [
"title"
],
"type": "object"
},
"strict": true
},
"type": "json_schema"
},
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"document_annotation_format": {
"json_schema": {
"description": "Extract the visible document fields",
"name": "document_title",
"schema": {
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
}
},
"required": [
"title"
],
"type": "object"
},
"strict": true
},
"type": "json_schema"
},
"extract_header": true,
"image_min_size": 300,
"include_blocks": true,
"include_image_base64": false,
"model": "mistral/mistral-ocr-latest",
"pages": [
0
],
"table_format": "markdown"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOiJ7XCJ0aXRsZVwiOiBcIkludm9pY2VfMTIzXCJ9IiwidXNhZ2VfaW5mbyI6eyJwYWdlc19wcm9jZXNzZWQiOjEsImRvY19zaXplX2J5dGVzIjo0MTI0fX0=",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:26 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=c5QAleICyoEptgycuGlRuItpEqcNe2MRZASOE9DeW80-1788292466.0205042-1.0.1.1-_PjnHuYd9Ba1XjxpUtrCWo_gUI5uKAvA73jcZAj6t_RARG9jiuX8N3t6bXu7K15RP3WG6AYFN4hbSKwlEpfksa9krmM7JYpf_JEpS5GDbst5vpFFLNtv.ymdX8lDSb5a; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:26 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-85bd-7075-af53-8e341b35a63e"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-85bd-7075-af53-8e341b35a63e"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "43"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "432"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "433"
},
{
"name": "x-kong-proxy-latency",
"value": "13"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d3a89a85f953-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:26.965078Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"extract_footer": true,
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:20 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-6c42-7ea1-b08b-07cae50734e0"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-6c42-7ea1-b08b-07cae50734e0"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "50"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "523"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "525"
},
{
"name": "x-kong-proxy-latency",
"value": "13"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "set-cookie",
"value": "__cf_bm=guezPmBNpI0djDo3Kvw0miZywJszLF5AN8loY9Uma0E-1788292459.497073-1.0.1.1-meRuKWGaBsHhkiA58SnRIUHMOaMgSPVzPrmAuu4_xq4UqYETqJn06.dJRx2xrVcoseTuUZNKdyRxTbkz2pdMRMe2AMQWaAMXXW27bG1VkapsA2hW1KtB48F3ubpGbp0v; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:20 GMT"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "CF-RAY",
"value": "a346d37fde0f1703-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:20.425444Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"model": "mistral/mistral-ocr-latest",
"table_format": "markdown"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:21 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=XcXH8M77UDLCt6rebhVaPfmyZAa4b4OQ6qSuzN8zcxQ-1788292460.477192-1.0.1.1-jIqoIYlVPq1KWC06tEhpWZ2IGqr1TQU1jPokZk1wkK2UVb0L0LVboaLWA9UnCvYLvy5JzgUf1srFJQlzr7edR8HiWMaxsHfRsCVCPVlCr3H6S_Pikt3AqQmlW8xWM9vh; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:21 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-7015-7a8d-a18c-37b9ceacbe77"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-7015-7a8d-a18c-37b9ceacbe77"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "49"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "337"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "338"
},
{
"name": "x-kong-proxy-latency",
"value": "21"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d385ffbda0f2-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:21.430472Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"image_limit": 1,
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:15 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=zdJ57Tl12b3GlmPJbN9IJjqlrvpX6J37Pmt6.Jj4vEs-1788292454.9590237-1.0.1.1-KS5i0m8mBhan9C3JxsJ488KsWHHpjYMz7ASc2SsaJ9MdFLx6JQWyViWZcw3ohhK1y_8a7DwExnKFQEIkFvF8J6HRVhoD2qaYjFW7DHz3t.2DBZunmBgvOouYMcjJyi7X; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:15 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-5a87-7148-8ae4-4e14967bebf3"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-5a87-7148-8ae4-4e14967bebf3"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "56"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "226"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "227"
},
{
"name": "x-kong-proxy-latency",
"value": "19"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d3637dc2c090-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:15.394028Z",
"schema_version": 1
}

View file

@ -0,0 +1,114 @@
{
"case": {
"litellm_input": {
"bbox_annotation_format": {
"json_schema": {
"description": "Extract the visible document fields",
"name": "bounding_boxes",
"schema": {
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
}
},
"required": [
"title"
],
"type": "object"
},
"strict": true
},
"type": "json_schema"
},
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:16 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=VjsfRKiwC7YjbVgfHbbpQfMBCW9MC1ehUXU3LsRZeUY-1788292455.9575524-1.0.1.1-KL3St9l4QfN1eWLUV07XDYmwE0xr2.26Z85yKf1yBMTDFCHBnz_3_RP.oNw6p.GfJsd2mnKb2lOvgPQcBDZygv7yJhN41wN87pIkAvurdr1z_snzwBpofXTvcjibOV1K; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:16 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-5e6e-70ac-890d-c0110a5e81af"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-5e6e-70ac-890d-c0110a5e81af"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "54"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "210"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "212"
},
{
"name": "x-kong-proxy-latency",
"value": "17"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d369be838fc5-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:16.402593Z",
"schema_version": 1
}

View file

@ -0,0 +1,94 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:13 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=0BJujMXEX2uflOAjZGqeTJi9lXNgGJmX61YbHtpbRKI-1788292452.9600976-1.0.1.1-7idLuOz.nd3kpO10lpC8IkPMArZ8M9iDs13Z.kTwKLTBHVm4.lyUeSqedg4EQV_Mo3D6JLf6TJgvdhWHsemZe6NyIzTBQwA3cwyM0cMQ5Aw68sVgtDOuGBaqhtelZ0RW; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:13 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-52b4-7e7b-983e-5bc4e5469b39"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-52b4-7e7b-983e-5bc4e5469b39"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "59"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "554"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "557"
},
{
"name": "x-kong-proxy-latency",
"value": "13"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d356fb3698ce-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:13.880852Z",
"schema_version": 1
}

View file

@ -0,0 +1,126 @@
{
"case": {
"litellm_input": {
"bbox_annotation_format": {
"json_schema": {
"description": "Extract the visible document fields",
"name": "bounding_boxes",
"schema": {
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
}
},
"required": [
"title"
],
"type": "object"
},
"strict": true
},
"type": "json_schema"
},
"confidence_scores_granularity": "page",
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"extract_footer": false,
"extract_header": true,
"id": "case-1",
"image_limit": 1,
"image_min_size": 300,
"include_blocks": false,
"include_image_base64": true,
"model": "mistral/mistral-ocr-latest",
"pages": [
0
],
"table_format": "markdown"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiVGVzdCBQREYgRmlsZSIsImltYWdlcyI6W10sInRhYmxlcyI6W10sImh5cGVybGlua3MiOltdLCJoZWFkZXIiOm51bGwsImZvb3RlciI6bnVsbCwiZGltZW5zaW9ucyI6eyJkcGkiOjkzLCJoZWlnaHQiOjEwMjMsIndpZHRoIjo3OTF9LCJjb25maWRlbmNlX3Njb3JlcyI6eyJ3b3JkX2NvbmZpZGVuY2Vfc2NvcmVzIjpbXSwiYXZlcmFnZV9wYWdlX2NvbmZpZGVuY2Vfc2NvcmUiOjAuOTM3NjIyOTc0NDMyMjkzNiwibWluaW11bV9wYWdlX2NvbmZpZGVuY2Vfc2NvcmUiOjAuMjI1OTA1NTA3OTYwMzY4MzV9LCJibG9ja3MiOm51bGx9XSwibW9kZWwiOiJtaXN0cmFsLW9jci1sYXRlc3QiLCJkb2N1bWVudF9hbm5vdGF0aW9uIjpudWxsLCJ1c2FnZV9pbmZvIjp7InBhZ2VzX3Byb2Nlc3NlZCI6MSwiZG9jX3NpemVfYnl0ZXMiOjU4OX19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:24 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=sPN0c81343UATX4OL40rLiXwZzjj6.f9wiR.sUEwidI-1788292464.0167594-1.0.1.1-1I1p0tYuzSZefNGWXeuwPPCH2oi83ZL7wHVVU_skvYhidQGDYesdwH32zscGrLGoxF1LPkg9h4yruaTw9Pai6ahKkyPofBTXU4pjatWxIYJOV8ILJQRwjJAY9r5x01g5; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:24 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-7deb-72b4-976d-4c244b056782"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-7deb-72b4-976d-4c244b056782"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "45"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "373"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "373"
},
{
"name": "x-kong-proxy-latency",
"value": "17"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d39c1fe5cf12-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:24.951956Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"image_min_size": 300,
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:15 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-5c60-769b-abf3-44dcefdf5282"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-5c60-769b-abf3-44dcefdf5282"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "55"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "272"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "273"
},
{
"name": "x-kong-proxy-latency",
"value": "17"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "set-cookie",
"value": "__cf_bm=Ld7W50uqSPv414iFBL3xHBo1US6FgEGi176rm3Ir7jU-1788292455.4420552-1.0.1.1-U1t1LZ3WH0stmljgl3EZ7Q.vhOhxNGrc3Zj75kgYaDNZCxuwaMc55Khvf.CeGempT.Y5ajCYMNsBwGv0h3rJ4PtpKYxp6Jt16enjPTx6jUkPdvUVHJwVTRMSTNSJxf5S; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:15 GMT"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "CF-RAY",
"value": "a346d36688d203c2-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:15.898087Z",
"schema_version": 1
}

View file

@ -0,0 +1,115 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"document_annotation_format": {
"json_schema": {
"description": "Extract the visible document fields",
"name": "document_title",
"schema": {
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
}
},
"required": [
"title"
],
"type": "object"
},
"strict": true
},
"type": "json_schema"
},
"document_annotation_prompt": "Extract the visible title",
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOiJ7XCJ0aXRsZVwiOiBcImludm9pY2UgMTIzXCJ9IiwidXNhZ2VfaW5mbyI6eyJwYWdlc19wcm9jZXNzZWQiOjEsImRvY19zaXplX2J5dGVzIjo0MTI0fX0=",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:18 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=kVVa_K4blNT7UfkHNHEr2jJrVZoVVcKoCLeEqC9ojSY-1788292457.9748948-1.0.1.1-4aZN8zUH7fXP1NZK_aaS15fCbNd8A8ZJbBeCartOJWpgZMx2hW3klcPhDDvC0KSV6e.K1Ahz9wRggeTiUZQjsVuKYbR_vJ8dGfKXVNP2KyYcOfgaHyr0Kb1JOZSDGGnS; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:18 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-664a-740d-a112-0a1183c302b3"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-664a-740d-a112-0a1183c302b3"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "52"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "402"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "403"
},
{
"name": "x-kong-proxy-latency",
"value": "20"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d376581f74f9-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:18.915814Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"confidence_scores_granularity": "page",
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOnsid29yZF9jb25maWRlbmNlX3Njb3JlcyI6W10sImF2ZXJhZ2VfcGFnZV9jb25maWRlbmNlX3Njb3JlIjowLjkwODQ1NTY0NTU0ODk3LCJtaW5pbXVtX3BhZ2VfY29uZmlkZW5jZV9zY29yZSI6MC4xNjE2ODIwODgzOTgyMzQ3NX0sImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:22 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-7411-7d2c-9e43-01320e4cad1c"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-7411-7d2c-9e43-01320e4cad1c"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "48"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "329"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "330"
},
{
"name": "x-kong-proxy-latency",
"value": "14"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "set-cookie",
"value": "__cf_bm=vGgqF1m3.RSN4S6h3XP_Kk8ojWPq3vypiJ2NZgCazl0-1788292461.4966974-1.0.1.1-OYrG7cwlLUmuwb_TTkXh4Te8fKHOoi5hlP5Mok.vCVqZD0lY1bv2Efm83lRx69__NMYwiYqJwrzDNChyYI5Vl9HpHmBybXfpbZ6WpjVEIscEyaLBDMJ_0UI5whsB2.xf; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:22 GMT"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "CF-RAY",
"value": "a346d38c59681749-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:22.437385Z",
"schema_version": 1
}

View file

@ -0,0 +1,97 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"model": "mistral/mistral-ocr-latest",
"pages": [
0
]
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:14 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=CJBaWJ.SsBbSNRd1h3ENyFHO8QA2S5qq0irYhSrOSQY-1788292453.9335105-1.0.1.1-IIqKfxCHaT.6ATkFZNdqQ87W8B2Lnv_N5RSB8sU.dZa5E0gROwfWTZcIsI3aQm_r6aqQT3o.c86aG9ltQq25grYhzFc1LXxWUsmCyMEW7WunMgbzYacSazYXeAxE0yPi; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:14 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-5685-747f-b76d-dab242ea7512"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-5685-747f-b76d-dab242ea7512"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "58"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "179"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "180"
},
{
"name": "x-kong-proxy-latency",
"value": "12"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d35d1fbbebe5-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:14.385374Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"include_image_base64": true,
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:14 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=RwMrJUmbvjo3BcW48Leja7wReidkx5xcJUtnE.9z924-1788292454.453593-1.0.1.1-oxMCFin79BJrtZjXcFewsGcQA0dzFQlC6NHkBN9AuMlUSYoT8EZty3nieW5ORoOnygaYSrpckLffOUJddtxb4TgxSNpkJCBFZOzs3QLLlGpkZrsJx8Rw9ype09j_OKZx; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:14 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-588e-7e32-a54b-f816bc6c1dc4"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-588e-7e32-a54b-f816bc6c1dc4"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "57"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "242"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "242"
},
{
"name": "x-kong-proxy-latency",
"value": "16"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d360593c138a-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:14.889667Z",
"schema_version": 1
}

View file

@ -0,0 +1,114 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"document_annotation_format": {
"json_schema": {
"description": "Extract the visible document fields",
"name": "document_title",
"schema": {
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
}
},
"required": [
"title"
],
"type": "object"
},
"strict": true
},
"type": "json_schema"
},
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOiJ7XCJ0aXRsZVwiOiBcIkludm9pY2VfMTIzXCJ9IiwidXNhZ2VfaW5mbyI6eyJwYWdlc19wcm9jZXNzZWQiOjEsImRvY19zaXplX2J5dGVzIjo0MTI0fX0=",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:17 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-6060-71cb-9e35-66ab873b99f3"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-6060-71cb-9e35-66ab873b99f3"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "53"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "888"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "889"
},
{
"name": "x-kong-proxy-latency",
"value": "12"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "set-cookie",
"value": "__cf_bm=xoadxKi40.IFnjiaqOmRs2WH6OMEVyT3As6eFw.vBvY-1788292456.4529853-1.0.1.1-jbao7uSuL.oLriQMYYO4SXltYHP3IPGIYg7yiRKk3Xqlwsc3xaQu0ES59EpMR3XVjJADyl9rHQfnKY3HvKRxXdGIA8Jerdfcu68wRUZNHcXd_epa4pcO5nnks7YPh93z; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:17 GMT"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "CF-RAY",
"value": "a346d36cd82f15ba-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:17.908846Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"extract_header": true,
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:19 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-6a3a-76c7-85b8-d40a7156155e"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-6a3a-76c7-85b8-d40a7156155e"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "51"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "230"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "230"
},
{
"name": "x-kong-proxy-latency",
"value": "16"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "set-cookie",
"value": "__cf_bm=O0x.ne9EKgb7nXAnOFoTqrbi69sVwHkdvll_fTS6PYE-1788292458.978935-1.0.1.1-2MYJEhW_I9Kf6XeYxBAqVLCXAnN8HABHbK351INmQpOQo036RmkEgf2..oRaM4BDqtR_F7Xrf72ekGCSm4C1h2tQaM6qXV0eCbt78fDRKI3i4N_cWLzghtZs0RJRw7Ir; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:19 GMT"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "CF-RAY",
"value": "a346d37c9944d8a7-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:19.420285Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"id": "case-1",
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6W3sidG9wX2xlZnRfeCI6MzM1LCJ0b3BfbGVmdF95IjoxMzgsImJvdHRvbV9yaWdodF94Ijo0NjQsImJvdHRvbV9yaWdodF95IjoxNjIsImNvbnRlbnQiOiJpbnZvaWNlIDEyMyIsImNvbmZpZGVuY2Vfc2NvcmVzIjpudWxsLCJ0eXBlIjoidGV4dCJ9XX1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:23 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-7bdc-74c5-b8eb-15fbd25b6cfd"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-7bdc-74c5-b8eb-15fbd25b6cfd"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "46"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "182"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "183"
},
{
"name": "x-kong-proxy-latency",
"value": "22"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "set-cookie",
"value": "__cf_bm=0rjHVJtpp3aXFzTWuyNNxtBvLU2V3RPEgQGcO8MQIJo-1788292463.4929135-1.0.1.1-39O3ne30GQFGDB22kGltMQVL6E9Hk2pTTGEyKXMcYXy8Dzs.BogwfvcphEP473aOQPn_RYs3f5Jz_fhcd4BzKbs1YHm0AQbi84qoKxg.2BQ7kpkarYT1GHgFIfOs_Pxw; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:23 GMT"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "CF-RAY",
"value": "a346d398da783ad4-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:23.946966Z",
"schema_version": 1
}

View file

@ -0,0 +1,114 @@
{
"case": {
"litellm_input": {
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"document_annotation_format": {
"json_schema": {
"description": "Extract the visible document fields",
"name": "document_title",
"schema": {
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
}
},
"required": [
"title"
],
"type": "object"
},
"strict": true
},
"type": "json_schema"
},
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiVGVzdCBQREYgRmlsZSIsImltYWdlcyI6W10sInRhYmxlcyI6W10sImh5cGVybGlua3MiOltdLCJoZWFkZXIiOm51bGwsImZvb3RlciI6bnVsbCwiZGltZW5zaW9ucyI6eyJkcGkiOjkzLCJoZWlnaHQiOjEwMjMsIndpZHRoIjo3OTF9LCJjb25maWRlbmNlX3Njb3JlcyI6bnVsbCwiYmxvY2tzIjpbeyJ0b3BfbGVmdF94IjoxMjYsInRvcF9sZWZ0X3kiOjEwNCwiYm90dG9tX3JpZ2h0X3giOjIyOSwiYm90dG9tX3JpZ2h0X3kiOjEyNywiY29udGVudCI6IlRlc3QgUERGIEZpbGUiLCJjb25maWRlbmNlX3Njb3JlcyI6bnVsbCwidHlwZSI6InRleHQifV19XSwibW9kZWwiOiJtaXN0cmFsLW9jci1sYXRlc3QiLCJkb2N1bWVudF9hbm5vdGF0aW9uIjoie1widGl0bGVcIjogXCJUZXN0X1BERl9GaWxlXCJ9IiwidXNhZ2VfaW5mbyI6eyJwYWdlc19wcm9jZXNzZWQiOjEsImRvY19zaXplX2J5dGVzIjo1ODl9fQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:25 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-81d5-79f8-914d-e3863af2d24b"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-81d5-79f8-914d-e3863af2d24b"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "44"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "448"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "449"
},
{
"name": "x-kong-proxy-latency",
"value": "15"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "set-cookie",
"value": "__cf_bm=dDZPnK7j_xGIDo5mFF6PHBwL.E.iE3aIhXdIZ6xZIPg-1788292465.0206954-1.0.1.1-XVajgKnQ8WEsB75xPcp1hdJXhMUfK.oz.Apkd1nRlGofoxlEWK4F7M2bC8p67VFjDyEZK3O6mCzpiePraLUty31M1H92EBrZBT9wRwuaet2H7P73KO8QHZmO2Jm5tgyC; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:25 GMT"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "CF-RAY",
"value": "a346d3a25d0cccb8-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:25.959391Z",
"schema_version": 1
}

View file

@ -0,0 +1,95 @@
{
"case": {
"litellm_input": {
"document": {
"image_url": "https://dummyjson.com/image/800x300/ffffff/000000?text=invoice%20123&fontSize=24",
"type": "image_url"
},
"include_blocks": false,
"model": "mistral/mistral-ocr-latest"
},
"provider_responses": [
{
"body_b64": "eyJwYWdlcyI6W3siaW5kZXgiOjAsIm1hcmtkb3duIjoiaW52b2ljZSAxMjMiLCJpbWFnZXMiOltdLCJ0YWJsZXMiOltdLCJoeXBlcmxpbmtzIjpbXSwiaGVhZGVyIjpudWxsLCJmb290ZXIiOm51bGwsImRpbWVuc2lvbnMiOnsiZHBpIjoyMDAsImhlaWdodCI6MzAwLCJ3aWR0aCI6ODAwfSwiY29uZmlkZW5jZV9zY29yZXMiOm51bGwsImJsb2NrcyI6bnVsbH1dLCJtb2RlbCI6Im1pc3RyYWwtb2NyLWxhdGVzdCIsImRvY3VtZW50X2Fubm90YXRpb24iOm51bGwsInVzYWdlX2luZm8iOnsicGFnZXNfcHJvY2Vzc2VkIjoxLCJkb2Nfc2l6ZV9ieXRlcyI6NDEyNH19",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:22 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "set-cookie",
"value": "__cf_bm=h45pW85qKj22bgXtkfz55PHEqryQZKiE7tImTTQx2u4-1788292462.5055265-1.0.1.1-kea4VbQ7Y.FRb9t5HgroFMzOCmbkU.byJfToALDpbgme.ScLO1wX13v5qEdll59C0DGKoXk2YYLAXvz0D6QfKN9mwTDRu23O72VJGgXtGu80xcMz16ZSRJfPn7hqnVFI; HttpOnly; SameSite=None; Secure; Path=/; Domain=mistral.ai; Expires=Tue, 01 Sep 2026 20:24:22 GMT"
},
{
"name": "mistral-correlation-id",
"value": "01a05e89-7802-768c-bf57-9021a209ab48"
},
{
"name": "x-kong-request-id",
"value": "01a05e89-7802-768c-bf57-9021a209ab48"
},
{
"name": "x-ratelimit-limit-ocr-pages-minute",
"value": "60"
},
{
"name": "x-ratelimit-remaining-ocr-pages-minute",
"value": "47"
},
{
"name": "x-ratelimit-ocr-pages-query-cost",
"value": "1"
},
{
"name": "x-envoy-upstream-service-time",
"value": "213"
},
{
"name": "Server",
"value": "cloudflare"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "x-kong-upstream-latency",
"value": "214"
},
{
"name": "x-kong-proxy-latency",
"value": "17"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=15552000; includeSubDomains; preload"
},
{
"name": "CF-RAY",
"value": "a346d392a8432af7-SJC"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:23.442341Z",
"schema_version": 1
}

View file

@ -0,0 +1,46 @@
{
"case": {
"litellm_input": {
"custom_llm_provider": "reducto",
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"model": "parse-legacy"
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovLzQ1ZDNjYmJjLTRkNzctNDk2Ny04OTQxLTkzNWIwYzRhMDQ5My5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:58 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiIyNjJlZDY4My1lY2Q4LTQwYjktYTU2OC1mN2I3MDE0ODU0YzkiLCJkdXJhdGlvbiI6Mi40MTQ2NTk3Mzg1NDA2NDk0LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvYTkxZjU2OGItNmU5OC00OTYyLTk5MjQtMDk3MjAyMDkzNzc5LnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ1NlomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPWFhNzdiMzNlMjBmYzE0MjYzMDc2ODhmNTUxNDdkNTdlOGQzYjIyODZhNzg3YzA5NmIyZGEwMTZkN2Q3ZDBjOTMiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iL2NkZGQ2MzVlLTVkMTYtNDYyMS1iZjQ4LTAzMWU3NjM3ZGMwYiIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MzUyOTY5Mzg3MzU2ODAzLCJ0b3AiOjAuMTA0OTAyMzc2NjM1MDcwNTYsIndpZHRoIjowLjExODU1NjQwMDc3NDA1NTA4LCJoZWlnaHQiOjAuMDExMzU5MDQ1ODgxNDQyMjIxLCJwYWdlIjoxLCJvcmlnaW5hbF9wYWdlIjoxfSwiY29udGVudCI6IlRlc3QgUERGIEZpbGUiLCJpbWFnZV91cmwiOm51bGwsImNoYXJ0X2RhdGEiOm51bGwsImNvbmZpZGVuY2UiOiJoaWdoIiwiZ3JhbnVsYXJfY29uZmlkZW5jZSI6eyJleHRyYWN0X2NvbmZpZGVuY2UiOm51bGwsInBhcnNlX2NvbmZpZGVuY2UiOjAuNzEyNjcxNTMzMjI2OTY2OX0sImV4dHJhIjpudWxsfV19XSwib2NyIjpudWxsLCJjdXN0b20iOm51bGx9LCJwYXJzZV9tb2RlIjpudWxsLCJkb2N1bWVudF9wcm9wZXJ0aWVzIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:59 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:59.198829Z",
"schema_version": 1
}

View file

@ -0,0 +1,46 @@
{
"case": {
"litellm_input": {
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"enhance": {},
"model": "reducto/parse-legacy"
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovL2I5ZjI0MmZkLWZkYjQtNGIwYS05NTM1LWYxMWY0MzJjNzY3OC5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:57 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiIyNWI3ODE4OS02NmZlLTQ2ZDktODExNC0xNDM3MmE5ZDQ0MmQiLCJkdXJhdGlvbiI6Mi40MTQ2NTk3Mzg1NDA2NDk0LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvYTkxZjU2OGItNmU5OC00OTYyLTk5MjQtMDk3MjAyMDkzNzc5LnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ1NlomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPWFhNzdiMzNlMjBmYzE0MjYzMDc2ODhmNTUxNDdkNTdlOGQzYjIyODZhNzg3YzA5NmIyZGEwMTZkN2Q3ZDBjOTMiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iL2NkZGQ2MzVlLTVkMTYtNDYyMS1iZjQ4LTAzMWU3NjM3ZGMwYiIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MzUyOTY5Mzg3MzU2ODAzLCJ0b3AiOjAuMTA0OTAyMzc2NjM1MDcwNTYsIndpZHRoIjowLjExODU1NjQwMDc3NDA1NTA4LCJoZWlnaHQiOjAuMDExMzU5MDQ1ODgxNDQyMjIxLCJwYWdlIjoxLCJvcmlnaW5hbF9wYWdlIjoxfSwiY29udGVudCI6IlRlc3QgUERGIEZpbGUiLCJpbWFnZV91cmwiOm51bGwsImNoYXJ0X2RhdGEiOm51bGwsImNvbmZpZGVuY2UiOiJoaWdoIiwiZ3JhbnVsYXJfY29uZmlkZW5jZSI6eyJleHRyYWN0X2NvbmZpZGVuY2UiOm51bGwsInBhcnNlX2NvbmZpZGVuY2UiOjAuNzEyNjcxNTMzMjI2OTY2OX0sImV4dHJhIjpudWxsfV19XSwib2NyIjpudWxsLCJjdXN0b20iOm51bGx9LCJwYXJzZV9tb2RlIjpudWxsLCJkb2N1bWVudF9wcm9wZXJ0aWVzIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:57 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:58.471820Z",
"schema_version": 1
}

View file

@ -0,0 +1,45 @@
{
"case": {
"litellm_input": {
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"model": "reducto/parse-legacy"
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovL2E5MWY1NjhiLTZlOTgtNDk2Mi05OTI0LTA5NzIwMjA5Mzc3OS5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:53 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiJjZGRkNjM1ZS01ZDE2LTQ2MjEtYmY0OC0wMzFlNzYzN2RjMGIiLCJkdXJhdGlvbiI6Mi40MTQ2NTk3Mzg1NDA2NDk0LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvYTkxZjU2OGItNmU5OC00OTYyLTk5MjQtMDk3MjAyMDkzNzc5LnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ1NlomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPWFhNzdiMzNlMjBmYzE0MjYzMDc2ODhmNTUxNDdkNTdlOGQzYjIyODZhNzg3YzA5NmIyZGEwMTZkN2Q3ZDBjOTMiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iL2NkZGQ2MzVlLTVkMTYtNDYyMS1iZjQ4LTAzMWU3NjM3ZGMwYiIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MzUyOTY5Mzg3MzU2ODAzLCJ0b3AiOjAuMTA0OTAyMzc2NjM1MDcwNTYsIndpZHRoIjowLjExODU1NjQwMDc3NDA1NTA4LCJoZWlnaHQiOjAuMDExMzU5MDQ1ODgxNDQyMjIxLCJwYWdlIjoxLCJvcmlnaW5hbF9wYWdlIjoxfSwiY29udGVudCI6IlRlc3QgUERGIEZpbGUiLCJpbWFnZV91cmwiOm51bGwsImNoYXJ0X2RhdGEiOm51bGwsImNvbmZpZGVuY2UiOiJoaWdoIiwiZ3JhbnVsYXJfY29uZmlkZW5jZSI6eyJleHRyYWN0X2NvbmZpZGVuY2UiOm51bGwsInBhcnNlX2NvbmZpZGVuY2UiOjAuNzEyNjcxNTMzMjI2OTY2OX0sImV4dHJhIjpudWxsfV19XSwib2NyIjpudWxsLCJjdXN0b20iOm51bGx9LCJwYXJzZV9tb2RlIjpudWxsLCJkb2N1bWVudF9wcm9wZXJ0aWVzIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:57 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:57.246119Z",
"schema_version": 1
}

View file

@ -0,0 +1,46 @@
{
"case": {
"litellm_input": {
"custom_llm_provider": "reducto",
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"model": "parse-v3"
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovL2E2ZDNjM2JiLWE2ZjEtNDYzNi04YjNhLWVlOTIwYjI5ZDM4Ny5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:46 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiI0YTc4Yzk3NS0xODY1LTQ2ZmUtOGY4OS1iZWE5MmE0OWUyMTUiLCJkdXJhdGlvbiI6MS4xMTk0NDI3MDEzMzk3MjE3LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvNmFiOWJjZjQtMTg5My00YjM3LWJkZGItYWNkYThjZTQ1ZGZiLnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ0NVomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPTFkYjYxZDMzZjYwMmYxM2U4NDQ0ZTg1ODBiNzM3YjMxZjM0NmU0NzQ5M2NjN2VjYTdjNGZjZGRiZGUwZGZiNDMiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iL2QzNDU1NDlmLTVkOTgtNGM2Mi1iM2UzLTI0Yzk5MDE0NGRmNCIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MTg5NTc0NTI3MTgyNzQ4LCJ0b3AiOjAuMDk0NzkyOTU1NTM4MTQxMjEsIndpZHRoIjowLjExOTQ2MjkwNjUwODIwODc1LCJoZWlnaHQiOjAuMDIxNDY0MTQzMDE3MTA1MDcsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiVGVzdCBQREYgRmlsZSIsImltYWdlX3VybCI6bnVsbCwiY2hhcnRfZGF0YSI6bnVsbCwiY29uZmlkZW5jZSI6ImhpZ2giLCJncmFudWxhcl9jb25maWRlbmNlIjp7ImV4dHJhY3RfY29uZmlkZW5jZSI6bnVsbCwicGFyc2VfY29uZmlkZW5jZSI6MC43MTg1MzU1NjkzMTAxODgyfSwiZXh0cmEiOm51bGx9XX1dLCJvY3IiOm51bGwsImN1c3RvbSI6bnVsbH0sInBhcnNlX21vZGUiOm51bGwsImRvY3VtZW50X3Byb3BlcnRpZXMiOm51bGx9",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:47 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:47.967951Z",
"schema_version": 1
}

View file

@ -0,0 +1,56 @@
{
"case": {
"litellm_input": {
"custom_llm_provider": null,
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"formatting": {
"add_page_markers": true,
"include": [
"change_tracking",
"highlight",
"comments"
],
"merge_tables": true,
"table_output_format": "json"
},
"model": "reducto/parse-v3"
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovL2Y4YmYxN2U4LWNhMGItNGFkZC1iNDg0LTk5ODJkMmU0YWMyYS5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:50 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiJlMzc5MjM4Ny04Mzg0LTQ1YWEtYjAyZS1hODQ1MjFkYjExNmEiLCJkdXJhdGlvbiI6MS4wNDk3MzYyNjEzNjc3OTc5LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvZjhiZjE3ZTgtY2EwYi00YWRkLWI0ODQtOTk4MmQyZTRhYzJhLnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ1MlomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPTczNjcyNzVhNjlkZmQ1M2Y5ZmNmOWYwMjc3N2YwZmU5NzNlN2U1NDFmYTE4NzFlMDM0N2Y3ODhiOGM1MDc4ZjQiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iL2UzNzkyMzg3LTgzODQtNDVhYS1iMDJlLWE4NDUyMWRiMTE2YSIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IltbU1RBUlQgT0YgUEFHRSAxXV1cblxuIyBUZXN0IFBERiBGaWxlXG5cbltbRU5EIE9GIFBBR0UgMV1dIiwiZW1iZWQiOiJbW1NUQVJUIE9GIFBBR0UgMV1dXG5cbiMgVGVzdCBQREYgRmlsZVxuXG5bW0VORCBPRiBQQUdFIDFdXSIsImVucmljaGVkIjpudWxsLCJlbnJpY2htZW50X3N1Y2Nlc3MiOmZhbHNlLCJibG9ja3MiOlt7InR5cGUiOiJQYWdlIE51bWJlciIsImJib3giOnsibGVmdCI6MC4wLCJ0b3AiOjAuMCwid2lkdGgiOjAuMCwiaGVpZ2h0IjowLjAsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiW1tTVEFSVCBPRiBQQUdFIDFdXSIsImltYWdlX3VybCI6bnVsbCwiY2hhcnRfZGF0YSI6bnVsbCwiY29uZmlkZW5jZSI6ImhpZ2giLCJncmFudWxhcl9jb25maWRlbmNlIjp7ImV4dHJhY3RfY29uZmlkZW5jZSI6bnVsbCwicGFyc2VfY29uZmlkZW5jZSI6bnVsbH0sImV4dHJhIjpudWxsfSx7InR5cGUiOiJUaXRsZSIsImJib3giOnsibGVmdCI6MC4xNjE4OTU3NDUyNzE4Mjc0OCwidG9wIjowLjA5NDc5Mjk1NTUzODE0MTIxLCJ3aWR0aCI6MC4xMTk0NjI5MDY1MDgyMDg3NSwiaGVpZ2h0IjowLjAyMTQ2NDE0MzAxNzEwNTA3LCJwYWdlIjoxLCJvcmlnaW5hbF9wYWdlIjoxfSwiY29udGVudCI6IlRlc3QgUERGIEZpbGUiLCJpbWFnZV91cmwiOm51bGwsImNoYXJ0X2RhdGEiOm51bGwsImNvbmZpZGVuY2UiOiJoaWdoIiwiZ3JhbnVsYXJfY29uZmlkZW5jZSI6eyJleHRyYWN0X2NvbmZpZGVuY2UiOm51bGwsInBhcnNlX2NvbmZpZGVuY2UiOjAuNzE4NTM1NTY5MzEwMTg4Mn0sImV4dHJhIjpudWxsfSx7InR5cGUiOiJQYWdlIE51bWJlciIsImJib3giOnsibGVmdCI6MC4wLCJ0b3AiOjAuMCwid2lkdGgiOjAuMCwiaGVpZ2h0IjowLjAsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiW1tFTkQgT0YgUEFHRSAxXV0iLCJpbWFnZV91cmwiOm51bGwsImNoYXJ0X2RhdGEiOm51bGwsImNvbmZpZGVuY2UiOiJoaWdoIiwiZ3JhbnVsYXJfY29uZmlkZW5jZSI6eyJleHRyYWN0X2NvbmZpZGVuY2UiOm51bGwsInBhcnNlX2NvbmZpZGVuY2UiOm51bGx9LCJleHRyYSI6bnVsbH1dfV0sIm9jciI6bnVsbCwiY3VzdG9tIjpudWxsfSwicGFyc2VfbW9kZSI6bnVsbCwiZG9jdW1lbnRfcHJvcGVydGllcyI6bnVsbH0=",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:52 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:53.454592Z",
"schema_version": 1
}

View file

@ -0,0 +1,48 @@
{
"case": {
"litellm_input": {
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"model": "reducto/parse-v3",
"settings": {
"return_ocr_data": true
}
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovL2ZjNmViY2IxLTk1ZDQtNDZlYy05MGQwLWVmY2EzM2VmMTk3Zi5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:41 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiIzZjZlZjY0ZC1jOTQ5LTRiOGQtOTg5OC1hZmIzYjAxNjY5ZTYiLCJkdXJhdGlvbiI6MS40NDgwNzQzNDA4MjAzMTI1LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvZmM2ZWJjYjEtOTVkNC00NmVjLTkwZDAtZWZjYTMzZWYxOTdmLnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ0M1omWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPTA5NjVjYTEyOWNlZTQ5YTQxNjUwZGYxNTIwNTdhMTdhNGY5ZTI2NWY5MmYyMjYxYTRmNjU3NjU4NzI3MmViMDMiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iLzNmNmVmNjRkLWM5NDktNGI4ZC05ODk4LWFmYjNiMDE2NjllNiIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MTg5NTc0NTI3MTgyNzQ4LCJ0b3AiOjAuMDk0NzkyOTU1NTM4MTQxMjEsIndpZHRoIjowLjExOTQ2MjkwNjUwODIwODc1LCJoZWlnaHQiOjAuMDIxNDY0MTQzMDE3MTA1MDcsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiVGVzdCBQREYgRmlsZSIsImltYWdlX3VybCI6bnVsbCwiY2hhcnRfZGF0YSI6bnVsbCwiY29uZmlkZW5jZSI6ImhpZ2giLCJncmFudWxhcl9jb25maWRlbmNlIjp7ImV4dHJhY3RfY29uZmlkZW5jZSI6bnVsbCwicGFyc2VfY29uZmlkZW5jZSI6MC43MTg1MzU1NjkzMTAxODgyfSwiZXh0cmEiOm51bGx9XX1dLCJvY3IiOnsid29yZHMiOlt7InRleHQiOiJUZXN0IiwiYmJveCI6eyJsZWZ0IjowLjE2MTg5NDc1NDY0NjY1MDMyLCJ0b3AiOjAuMDk0OTE5MjE0MzQ0OTg3OTcsIndpZHRoIjowLjAzODMyMDg5MDQzOTEwMjA3LCJoZWlnaHQiOjAuMDIxMDE5NTMxMDE4ODA2MjI1LCJwYWdlIjoxLCJvcmlnaW5hbF9wYWdlIjoxfSwiY29uZmlkZW5jZSI6MS4wLCJjaHVua19pbmRleCI6bnVsbCwicm90YXRpb24iOjM1OX0seyJ0ZXh0IjoiUERGIiwiYmJveCI6eyJsZWZ0IjowLjIwNTQ4MTk1OTMyNDI1NzA2LCJ0b3AiOjAuMDk1MTQ5OTA3MTk4ODE5MjQsIndpZHRoIjowLjAzOTM5OTI5MDM5NjQ5NzE0LCJoZWlnaHQiOjAuMDIxMDI1MjQzNDMxNjMwNzYsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb25maWRlbmNlIjoxLjAsImNodW5rX2luZGV4IjpudWxsLCJyb3RhdGlvbiI6MzU5fSx7InRleHQiOiJGaWxlIiwiYmJveCI6eyJsZWZ0IjowLjI1MDE0NzU3NjQyNTU4OTc3LCJ0b3AiOjAuMDk1Mzg2MzAyODMyNDAxMTUsIndpZHRoIjowLjAzMTc3MjAxNTE0MTUwNTgyLCJoZWlnaHQiOjAuMDIwOTg0ODcxMjE4OTAyODk1LCJwYWdlIjoxLCJvcmlnaW5hbF9wYWdlIjoxfSwiY29uZmlkZW5jZSI6MS4wLCJjaHVua19pbmRleCI6bnVsbCwicm90YXRpb24iOjM1OX1dLCJsaW5lcyI6W3sidGV4dCI6IlRlc3QgUERGIEZpbGUiLCJiYm94Ijp7ImxlZnQiOjAuMTYxODk0NzU0NjQ2NjUwMzIsInRvcCI6MC4wOTQ5MTkyMTQzNDQ5ODc5Nywid2lkdGgiOjAuMTIwMDI0ODM2OTIwNDQ1MjYsImhlaWdodCI6MC4wMjE0NTE5NTk3MDYzMTYwOTIsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb25maWRlbmNlIjoxLjAsImNodW5rX2luZGV4IjpudWxsLCJyb3RhdGlvbiI6MzU5fV19LCJjdXN0b20iOm51bGx9LCJwYXJzZV9tb2RlIjpudWxsLCJkb2N1bWVudF9wcm9wZXJ0aWVzIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:43 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:43.968997Z",
"schema_version": 1
}

View file

@ -0,0 +1,45 @@
{
"case": {
"litellm_input": {
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"model": "reducto/parse-v3"
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovLzAxMGIwMWIyLTgzYmQtNDQ2ZC1hZjExLWQxMjBjNWFjMmMwMi5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:27 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiI4MTM4YTZjNi1iNzI2LTQzMDAtOTBiNC1lNmQ1ZDQzZjZmNzAiLCJkdXJhdGlvbiI6MS4yNjQ4MjcyNTE0MzQzMjYyLCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvMDEwYjAxYjItODNiZC00NDZkLWFmMTEtZDEyMGM1YWMyYzAyLnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQyOFomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPTNjZGE5MDIwZmVmZmY4NWQ5ZGQxM2YxMjQ5MDhkN2IwMGU1NWI2ZTgyYTcyMDM5NTQ2ZDI2MjA5OWNmY2YzYjQiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iLzgxMzhhNmM2LWI3MjYtNDMwMC05MGI0LWU2ZDVkNDNmNmY3MCIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MTg5NTc0NTI3MTgyNzQ4LCJ0b3AiOjAuMDk0NzkyOTU1NTM4MTQxMjEsIndpZHRoIjowLjExOTQ2MjkwNjUwODIwODc1LCJoZWlnaHQiOjAuMDIxNDY0MTQzMDE3MTA1MDcsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiVGVzdCBQREYgRmlsZSIsImltYWdlX3VybCI6bnVsbCwiY2hhcnRfZGF0YSI6bnVsbCwiY29uZmlkZW5jZSI6ImhpZ2giLCJncmFudWxhcl9jb25maWRlbmNlIjp7ImV4dHJhY3RfY29uZmlkZW5jZSI6bnVsbCwicGFyc2VfY29uZmlkZW5jZSI6MC43MTg1MzU1NjkzMTAxODgyfSwiZXh0cmEiOm51bGx9XX1dLCJvY3IiOm51bGwsImN1c3RvbSI6bnVsbH0sInBhcnNlX21vZGUiOm51bGwsImRvY3VtZW50X3Byb3BlcnRpZXMiOm51bGx9",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:29 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:30.255600Z",
"schema_version": 1
}

View file

@ -0,0 +1,82 @@
{
"case": {
"litellm_input": {
"custom_llm_provider": null,
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"formatting": {
"add_page_markers": false,
"include": [
"signatures",
"ignore_watermarks"
],
"merge_tables": true,
"table_output_format": "json"
},
"model": "reducto/parse-v3",
"retrieval": {
"chunking": {
"chunk_mode": "variable",
"chunk_overlap": 32,
"chunk_size": 1500
},
"embedding_optimized": false,
"filter_blocks": [
"Figure",
"Table",
"Key Value"
]
},
"settings": {
"embed_pdf_metadata": false,
"embed_pdf_metadata_dpi": 100,
"extraction_mode": "hybrid",
"force_url_result": false,
"ocr_system": "legacy",
"page_range": [
1
],
"persist_results": false,
"return_images": [],
"return_ocr_data": false,
"timeout": 900.0
}
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovLzVkZjc0NWY0LTI4NzctNGNmNi05YmRlLWEyZjgyOWM5M2VlYS5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:48 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiI3Y2E2NzI0Mi0xNjc3LTQ4NGUtYjJlZi1iMjU2ZGNkZDFlYTQiLCJkdXJhdGlvbiI6MS4zMzU5MzA4MjQyNzk3ODUyLCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvNWRmNzQ1ZjQtMjg3Ny00Y2Y2LTliZGUtYTJmODI5YzkzZWVhLnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ0OVomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPTA3N2E2NTUxMjAxZDI1NjJmZDE1NDNmZDVjMzkxNTQxY2ZmZTYwNmQ2NDMwN2EzZjZkMTAzY2YxODIxOWY4MGEiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iLzdjYTY3MjQyLTE2NzctNDg0ZS1iMmVmLWIyNTZkY2RkMWVhNCIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MTg5NTc0NTI3MTgyNzQ4LCJ0b3AiOjAuMDk0NzkyOTU1NTM4MTQxMjEsIndpZHRoIjowLjExOTQ2MjkwNjUwODIwODc1LCJoZWlnaHQiOjAuMDIxNDY0MTQzMDE3MTA1MDcsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiVGVzdCBQREYgRmlsZSIsImltYWdlX3VybCI6bnVsbCwiY2hhcnRfZGF0YSI6bnVsbCwiY29uZmlkZW5jZSI6ImhpZ2giLCJncmFudWxhcl9jb25maWRlbmNlIjp7ImV4dHJhY3RfY29uZmlkZW5jZSI6bnVsbCwicGFyc2VfY29uZmlkZW5jZSI6MC43MTg1MzU1NjkzMTAxODgyfSwiZXh0cmEiOm51bGx9XX1dLCJvY3IiOm51bGwsImN1c3RvbSI6bnVsbH0sInBhcnNlX21vZGUiOm51bGwsImRvY3VtZW50X3Byb3BlcnRpZXMiOm51bGx9",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:50 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:50.708637Z",
"schema_version": 1
}

View file

@ -0,0 +1,50 @@
{
"case": {
"litellm_input": {
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"model": "reducto/parse-v3",
"retrieval": {
"chunking": {
"chunk_mode": "page"
}
}
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovLzY3MWQ2ZTAwLTZkZjUtNDkzYS1iZDllLThiYmYzZDc3ZDNhYi5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:36 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiI4ZjE1YjZkZC1mMmQyLTQ4ZTItYTRjOS0wMjMwYTlhY2U3MDQiLCJkdXJhdGlvbiI6My43NTEwNjQzMDA1MzcxMDk0LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvNjcxZDZlMDAtNmRmNS00OTNhLWJkOWUtOGJiZjNkNzdkM2FiLnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ0MFomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPTk2YTRmOTA0ZThkM2NhNThkMzk5YWIyMWFkZTY5ZWMwZWE2MmZmNWRmZWUzMjBjNWVlYWE2ZWFhMGM0ZTA1NjEiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iLzhmMTViNmRkLWYyZDItNDhlMi1hNGM5LTAyMzBhOWFjZTcwNCIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MTg5NTc0NTI3MTgyNzQ4LCJ0b3AiOjAuMDk0NzkyOTU1NTM4MTQxMjEsIndpZHRoIjowLjExOTQ2MjkwNjUwODIwODc1LCJoZWlnaHQiOjAuMDIxNDY0MTQzMDE3MTA1MDcsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiVGVzdCBQREYgRmlsZSIsImltYWdlX3VybCI6bnVsbCwiY2hhcnRfZGF0YSI6bnVsbCwiY29uZmlkZW5jZSI6ImhpZ2giLCJncmFudWxhcl9jb25maWRlbmNlIjp7ImV4dHJhY3RfY29uZmlkZW5jZSI6bnVsbCwicGFyc2VfY29uZmlkZW5jZSI6MC43MTg1MzU1NjkzMTAxODgyfSwiZXh0cmEiOm51bGx9XX1dLCJvY3IiOm51bGwsImN1c3RvbSI6bnVsbH0sInBhcnNlX21vZGUiOm51bGwsImRvY3VtZW50X3Byb3BlcnRpZXMiOm51bGx9",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:40 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:41.233002Z",
"schema_version": 1
}

View file

@ -0,0 +1,48 @@
{
"case": {
"litellm_input": {
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"formatting": {
"table_output_format": "md"
},
"model": "reducto/parse-v3"
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovL2YzY2MzYzcyLTYxNGEtNDEwNC1iYjcwLWE1MTZmMzcxNDhlMC5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:30 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiJhYmE5MjRkYy03NzczLTRkZmYtYTRhOC0zNTQ5MzY2ZWE5ZmEiLCJkdXJhdGlvbiI6NC41NzY0MTcyMDc3MTc4OTU1LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvZjNjYzNjNzItNjE0YS00MTA0LWJiNzAtYTUxNmYzNzE0OGUwLnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQzNVomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPTdkNGM5NDY0YmIzYTFhOWQ1YzdhNWY5NTdiYzEwODEzZmIxNmZjYzZkZTNmZWEyMDU0YTQwYjQ2OWZmYTc5YjMiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iL2FiYTkyNGRjLTc3NzMtNGRmZi1hNGE4LTM1NDkzNjZlYTlmYSIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MTg5NTc0NTI3MTgyNzQ4LCJ0b3AiOjAuMDk0NzkyOTU1NTM4MTQxMjEsIndpZHRoIjowLjExOTQ2MjkwNjUwODIwODc1LCJoZWlnaHQiOjAuMDIxNDY0MTQzMDE3MTA1MDcsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiVGVzdCBQREYgRmlsZSIsImltYWdlX3VybCI6bnVsbCwiY2hhcnRfZGF0YSI6bnVsbCwiY29uZmlkZW5jZSI6ImhpZ2giLCJncmFudWxhcl9jb25maWRlbmNlIjp7ImV4dHJhY3RfY29uZmlkZW5jZSI6bnVsbCwicGFyc2VfY29uZmlkZW5jZSI6MC43MTg1MzU1NjkzMTAxODgyfSwiZXh0cmEiOm51bGx9XX1dLCJvY3IiOm51bGwsImN1c3RvbSI6bnVsbH0sInBhcnNlX21vZGUiOm51bGwsImRvY3VtZW50X3Byb3BlcnRpZXMiOm51bGx9",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:35 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:35.993066Z",
"schema_version": 1
}

View file

@ -0,0 +1,46 @@
{
"case": {
"litellm_input": {
"custom_llm_provider": null,
"document": {
"document_url": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDYxMiA3OTJdIC9Db250ZW50cyA1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvRm9udCA8PCAvRjEgPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhID4+ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggNDQgPj4Kc3RyZWFtCkJUCi9GMSAxMiBUZgoxMDAgNzAwIFRkCihUZXN0IFBERiBGaWxlKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMDU4IDAwMDAwIG4gCjAwMDAwMDAxMTUgMDAwMDAgbiAKMDAwMDAwMDIxNCAwMDAwMCBuIAowMDAwMDAwMjkzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNiAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMzg1CiUlRU9GCg==",
"type": "document_url"
},
"model": "reducto/parse-v3"
},
"provider_responses": [
{
"body_b64": "eyJmaWxlX2lkIjoicmVkdWN0bzovLzZhYjliY2Y0LTE4OTMtNGIzNy1iZGRiLWFjZGE4Y2U0NWRmYi5wZGYiLCJwcmVzaWduZWRfdXJsIjpudWxsfQ==",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:44 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
},
{
"body_b64": "eyJyZXNwb25zZV90eXBlIjoicGFyc2UiLCJqb2JfaWQiOiJkMzQ1NTQ5Zi01ZDk4LTRjNjItYjNlMy0yNGM5OTAxNDRkZjQiLCJkdXJhdGlvbiI6MS4xMTk0NDI3MDEzMzk3MjE3LCJwZGZfdXJsIjoiaHR0cHM6Ly9wcm9kLXN0b3JhZ2UyMDI0MTAxMDE0NDc0NTE0MDkwMDAwMDAwMS5zMy5hbWF6b25hd3MuY29tL29yZy84YmQ5ODNkMS00N2RkLTQ0MGItOTNkOC00MzE0MTJkODE5YmYvNmFiOWJjZjQtMTg5My00YjM3LWJkZGItYWNkYThjZTQ1ZGZiLnBkZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUEyVU9LNk9WQk9VWUw3V1lBJTJGMjAyNjA5MDElMkZ1cy13ZXN0LTIlMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwOTAxVDE5NTQ0NVomWC1BbXotRXhwaXJlcz00MzIwMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmWC1BbXotU2lnbmF0dXJlPTFkYjYxZDMzZjYwMmYxM2U4NDQ0ZTg1ODBiNzM3YjMxZjM0NmU0NzQ5M2NjN2VjYTdjNGZjZGRiZGUwZGZiNDMiLCJzdHVkaW9fbGluayI6Imh0dHBzOi8vc3R1ZGlvLnJlZHVjdG8uYWkvam9iL2QzNDU1NDlmLTVkOTgtNGM2Mi1iM2UzLTI0Yzk5MDE0NGRmNCIsInVzYWdlIjp7Im51bV9wYWdlcyI6MSwiY3JlZGl0cyI6MS4wLCJjcmVkaXRfYnJlYWtkb3duIjp7InBhZ2UiOjEuMH0sInBhZ2VfYmlsbGluZ19icmVha2Rvd24iOnsiMSI6WyJwYWdlIl19LCJub25fZW1wdHlfY2VsbF9jb3VudCI6bnVsbH0sInJlc3VsdCI6eyJ0eXBlIjoiZnVsbCIsImNodW5rcyI6W3siY29udGVudCI6IiMgVGVzdCBQREYgRmlsZSIsImVtYmVkIjoiIyBUZXN0IFBERiBGaWxlIiwiZW5yaWNoZWQiOm51bGwsImVucmljaG1lbnRfc3VjY2VzcyI6ZmFsc2UsImJsb2NrcyI6W3sidHlwZSI6IlRpdGxlIiwiYmJveCI6eyJsZWZ0IjowLjE2MTg5NTc0NTI3MTgyNzQ4LCJ0b3AiOjAuMDk0NzkyOTU1NTM4MTQxMjEsIndpZHRoIjowLjExOTQ2MjkwNjUwODIwODc1LCJoZWlnaHQiOjAuMDIxNDY0MTQzMDE3MTA1MDcsInBhZ2UiOjEsIm9yaWdpbmFsX3BhZ2UiOjF9LCJjb250ZW50IjoiVGVzdCBQREYgRmlsZSIsImltYWdlX3VybCI6bnVsbCwiY2hhcnRfZGF0YSI6bnVsbCwiY29uZmlkZW5jZSI6ImhpZ2giLCJncmFudWxhcl9jb25maWRlbmNlIjp7ImV4dHJhY3RfY29uZmlkZW5jZSI6bnVsbCwicGFyc2VfY29uZmlkZW5jZSI6MC43MTg1MzU1NjkzMTAxODgyfSwiZXh0cmEiOm51bGx9XX1dLCJvY3IiOm51bGwsImN1c3RvbSI6bnVsbH0sInBhcnNlX21vZGUiOm51bGwsImRvY3VtZW50X3Byb3BlcnRpZXMiOm51bGx9",
"headers": [
{
"name": "Date",
"value": "Tue, 01 Sep 2026 19:54:46 GMT"
},
{
"name": "Content-Type",
"value": "application/json"
}
],
"kind": "http",
"status_code": 200
}
]
},
"recorded_at": "2026-09-01T19:54:46.694836Z",
"schema_version": 1
}

View file

@ -1,5 +1,6 @@
from __future__ import annotations
import base64
import logging
import os
from collections.abc import Callable, Mapping
@ -26,8 +27,11 @@ from tests.route_parity.fixture_recorder import (
fixture_directory,
)
from tests.test_litellm.ocr.fixture_models import (
AzureDocumentIntelligenceOcrSdkInput,
AzureMistralOcrSdkInput,
JsonSchemaDefinition,
JsonSchemaResponseFormat,
MistralDocumentUrlDocument,
MistralImageUrlDocument,
MistralOcrSdkInput,
OcrParityCase,
@ -39,13 +43,14 @@ from tests.test_litellm.ocr.fixture_models import (
ReductoParseV3SdkInput,
ReductoRetrieval,
ReductoSettings,
VertexDeepSeekOcrSdkInput,
VertexMistralOcrSdkInput,
)
FIXTURE_DIR_ENV: Final = "LITELLM_OCR_FIXTURE_DIR"
_TEXT: Final = st.just("invoice 123")
_VALUE_TEXT: Final = st.just("case-1")
_FONT_SIZE: Final = st.just(24)
_MISTRAL_MODEL: Final = "mistral/mistral-ocr-latest"
_REDUCTO_API_BASE: Final = "https://platform.reducto.ai"
OcrFixtureTarget = FixtureTarget[OcrSdkInputBase]
@ -56,6 +61,20 @@ def _image_document(text: str, font_size: int) -> MistralImageUrlDocument:
return MistralImageUrlDocument(type="image_url", image_url=url)
def _fixture_pdf_data_uri() -> str:
fixture: Final = Path(__file__).resolve().parents[2] / "llm_translation" / "fixtures" / "dummy.pdf"
encoded: Final = base64.b64encode(fixture.read_bytes()).decode("ascii")
return f"data:application/pdf;base64,{encoded}"
def _pdf_document() -> MistralDocumentUrlDocument:
return MistralDocumentUrlDocument(type="document_url", document_url=_fixture_pdf_data_uri())
def _public_document_strategy() -> SearchStrategy[MistralImageUrlDocument | MistralDocumentUrlDocument]:
return st.sampled_from((_image_document("invoice 123", 24), _pdf_document()))
def _annotation_format(name: str) -> JsonSchemaResponseFormat:
return JsonSchemaResponseFormat(
type="json_schema",
@ -79,7 +98,7 @@ def _mistral_confidence_strategy() -> SearchStrategy[str]:
@st.composite
def mistral_input_strategy(draw: DrawFn, model: str) -> MistralOcrSdkInput:
document: Final = draw(st.builds(_image_document, _TEXT, _FONT_SIZE))
document: Final = draw(_public_document_strategy())
annotation: Final = draw(
st.sampled_from(
(
@ -120,6 +139,145 @@ def mistral_input_strategy(draw: DrawFn, model: str) -> MistralOcrSdkInput:
)
def _as_azure_mistral(case_input: MistralOcrSdkInput, model: str) -> AzureMistralOcrSdkInput:
values: Final = case_input.model_dump(mode="python", exclude={"boundary", "model", "custom_llm_provider"})
return AzureMistralOcrSdkInput.model_validate({**values, "model": model})
def _as_vertex_mistral(case_input: MistralOcrSdkInput, project: str, location: str) -> VertexMistralOcrSdkInput:
values: Final = case_input.model_dump(mode="python", exclude={"boundary", "model", "custom_llm_provider"})
return VertexMistralOcrSdkInput.model_validate(
{**values, "vertex_project": project, "vertex_location": location}
)
def _required_mistral_inputs(model: str) -> tuple[MistralOcrSdkInput, ...]:
document: Final = _image_document("invoice 123", 24)
annotation: Final = _annotation_format("document_title")
bbox_annotation: Final = _annotation_format("bounding_boxes")
cases: Final[tuple[dict[str, object], ...]] = (
{},
{"pages": [0]},
{"include_image_base64": True},
{"image_limit": 1},
{"image_min_size": 300},
{"bbox_annotation_format": bbox_annotation},
{"document_annotation_format": annotation},
{"document_annotation_format": annotation, "document_annotation_prompt": "Extract the visible title"},
{"extract_header": True},
{"extract_footer": True},
{"table_format": "markdown"},
{"confidence_scores_granularity": "page"},
{"include_blocks": False},
{"id": "case-1"},
)
return tuple(
MistralOcrSdkInput.model_validate({"model": model, "document": document, **case}) for case in cases
)
def _required_azure_document_intelligence_inputs() -> tuple[AzureDocumentIntelligenceOcrSdkInput, ...]:
document: Final = _pdf_document()
model: Final = "azure_ai/doc-intelligence/prebuilt-layout"
return (
AzureDocumentIntelligenceOcrSdkInput(model=model, document=document),
AzureDocumentIntelligenceOcrSdkInput(model=model, document=document, pages=[0, 1]),
AzureDocumentIntelligenceOcrSdkInput(model=model, document=document, features=["languages"]),
AzureDocumentIntelligenceOcrSdkInput(model=model, document=document, req_format="litellm"),
)
def _required_vertex_deepseek_inputs(project: str, location: str) -> tuple[VertexDeepSeekOcrSdkInput, ...]:
document: Final = _image_document("invoice 123", 24)
common: Final = {"document": document, "vertex_project": project, "vertex_location": location}
cases: Final[tuple[dict[str, object], ...]] = (
{},
{"stream": False},
{"temperature": 0.5},
{"max_tokens": 256},
{"top_p": 0.9},
{"n": 1},
{"stop": ["END", "STOP"]},
)
return tuple(VertexDeepSeekOcrSdkInput.model_validate({**common, **case}) for case in cases)
def _required_reducto_v3_inputs(
document: ReductoDocumentUrlDocument,
) -> tuple[ReductoParseV3SdkInput, ...]:
return (
ReductoParseV3SdkInput(model="reducto/parse-v3", document=document),
ReductoParseV3SdkInput(
model="reducto/parse-v3",
document=document,
formatting=ReductoFormatting(table_output_format="md"),
),
ReductoParseV3SdkInput(
model="reducto/parse-v3",
document=document,
retrieval=ReductoRetrieval(chunking=ReductoChunking(chunk_mode="page")),
),
ReductoParseV3SdkInput(
model="reducto/parse-v3",
document=document,
settings=ReductoSettings(return_ocr_data=True),
),
)
@st.composite
def azure_document_intelligence_input_strategy(draw: DrawFn) -> AzureDocumentIntelligenceOcrSdkInput:
optional_params: Final = draw(
st.fixed_dictionaries(
{},
optional={
"pages": st.sampled_from(([0], [0, 1], "1-2")),
"features": st.sampled_from((["languages"], ["keyValuePairs"], "languages,keyValuePairs")),
},
)
)
return AzureDocumentIntelligenceOcrSdkInput.model_validate(
{
"model": draw(
st.sampled_from(
(
"azure_ai/doc-intelligence/prebuilt-read",
"azure_ai/doc-intelligence/prebuilt-layout",
"azure_ai/doc-intelligence/prebuilt-document",
)
)
),
"document": draw(_public_document_strategy()),
**optional_params,
}
)
@st.composite
def vertex_deepseek_input_strategy(draw: DrawFn, project: str, location: str) -> VertexDeepSeekOcrSdkInput:
optional_params: Final = draw(
st.fixed_dictionaries(
{},
optional={
"stream": st.just(False),
"temperature": st.sampled_from((0.0, 0.5, 1.0)),
"max_tokens": st.sampled_from((1, 256, 1024)),
"top_p": st.sampled_from((0.1, 0.9, 1.0)),
"n": st.just(1),
"stop": st.sampled_from(("END", ["END", "STOP"])),
},
)
)
return VertexDeepSeekOcrSdkInput.model_validate(
{
"document": draw(_public_document_strategy()),
"vertex_project": project,
"vertex_location": location,
**optional_params,
}
)
def _reducto_formatting_strategy() -> SearchStrategy[ReductoFormatting]:
return st.builds(
ReductoFormatting,
@ -175,7 +333,7 @@ def _reducto_settings_strategy() -> SearchStrategy[ReductoSettings]:
ReductoSettings,
ocr_system=st.sampled_from(("standard", "legacy")),
extraction_mode=st.sampled_from(("ocr", "hybrid")),
force_url_result=st.booleans(),
force_url_result=st.just(False),
return_ocr_data=st.booleans(),
return_images=st.sampled_from(([], ["figure"], ["table"], ["page"], ["figure", "table", "page"])),
embed_pdf_metadata=st.booleans(),
@ -187,7 +345,9 @@ def _reducto_settings_strategy() -> SearchStrategy[ReductoSettings]:
@st.composite
def reducto_v3_input_strategy(draw: DrawFn) -> ReductoParseV3SdkInput:
def reducto_v3_input_strategy(
draw: DrawFn, document: ReductoDocumentUrlDocument | None = None
) -> ReductoParseV3SdkInput:
model, custom_llm_provider = draw(st.sampled_from((("reducto/parse-v3", None), ("parse-v3", "reducto"))))
options: Final = draw(
st.fixed_dictionaries(
@ -203,32 +363,34 @@ def reducto_v3_input_strategy(draw: DrawFn) -> ReductoParseV3SdkInput:
{
"model": model,
"custom_llm_provider": custom_llm_provider,
"document": ReductoDocumentUrlDocument(
type="document_url",
document_url="reducto://fixture-document.pdf",
),
"document": document
or ReductoDocumentUrlDocument(type="document_url", document_url="reducto://fixture-document.pdf"),
**options,
}
)
def reducto_legacy_input_strategy() -> SearchStrategy[ReductoParseLegacySdkInput]:
def reducto_legacy_input_strategy(
document: ReductoDocumentUrlDocument | None = None,
) -> SearchStrategy[ReductoParseLegacySdkInput]:
selected_document: Final = document or ReductoDocumentUrlDocument(
type="document_url", document_url="reducto://fixture-document.pdf"
)
return st.sampled_from(
(
ReductoParseLegacySdkInput(
model="reducto/parse-legacy",
document=ReductoDocumentUrlDocument(
type="document_url",
document_url="reducto://fixture-document.pdf",
),
document=selected_document,
),
ReductoParseLegacySdkInput(
model="parse-legacy",
custom_llm_provider="reducto",
document=ReductoDocumentUrlDocument(
type="document_url",
document_url="reducto://fixture-document.pdf",
),
document=selected_document,
),
ReductoParseLegacySdkInput(
model="reducto/parse-legacy",
document=selected_document,
enhance={},
),
)
)
@ -264,6 +426,142 @@ def _mistral_target(
provider_spec=ProviderSpec(upstream_base=_mistral_upstream_base(environ)),
strategy=cast(SearchStrategy[OcrSdkInputBase], mistral_input_strategy(_MISTRAL_MODEL)),
invoke=invoke,
required_inputs=cast(tuple[OcrSdkInputBase, ...], _required_mistral_inputs(_MISTRAL_MODEL)),
)
def _azure_mistral_target(
environ: Mapping[str, str], sdk_call: Callable[..., object]
) -> OcrFixtureTarget | None:
api_key: Final = environ.get("AZURE_AI_API_KEY")
upstream_base: Final = environ.get("AZURE_AI_API_BASE")
configured_model: Final = environ.get("AZURE_AI_OCR_MODEL")
if not api_key or not upstream_base or not configured_model:
return None
model: Final = configured_model if configured_model.startswith("azure_ai/") else f"azure_ai/{configured_model}"
def invoke(api_base: str, case_input: OcrSdkInputBase) -> object:
return sdk_call(api_base=api_base, api_key=api_key, **case_input.as_sdk_kwargs())
return OcrFixtureTarget(
name="azure-mistral",
provider_spec=ProviderSpec(upstream_base=upstream_base.rstrip("/")),
strategy=cast(
SearchStrategy[OcrSdkInputBase],
mistral_input_strategy(_MISTRAL_MODEL).map(lambda case_input: _as_azure_mistral(case_input, model)),
),
invoke=invoke,
required_inputs=cast(
tuple[OcrSdkInputBase, ...],
tuple(_as_azure_mistral(case_input, model) for case_input in _required_mistral_inputs(_MISTRAL_MODEL)),
),
)
def _azure_document_intelligence_target(
environ: Mapping[str, str], sdk_call: Callable[..., object]
) -> OcrFixtureTarget | None:
api_key: Final = environ.get("AZURE_DOCUMENT_INTELLIGENCE_API_KEY")
upstream_base: Final = environ.get("AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT")
if not api_key or not upstream_base:
return None
def invoke(api_base: str, case_input: OcrSdkInputBase) -> object:
return sdk_call(api_base=api_base, api_key=api_key, **case_input.as_sdk_kwargs())
return OcrFixtureTarget(
name="azure-document-intelligence",
provider_spec=ProviderSpec(upstream_base=upstream_base.rstrip("/")),
strategy=cast(SearchStrategy[OcrSdkInputBase], azure_document_intelligence_input_strategy()),
invoke=invoke,
required_inputs=cast(
tuple[OcrSdkInputBase, ...], _required_azure_document_intelligence_inputs()
),
)
def _vertex_targets(
environ: Mapping[str, str], sdk_call: Callable[..., object]
) -> tuple[OcrFixtureTarget, ...]:
api_key: Final = environ.get("VERTEX_AI_API_KEY")
project: Final = environ.get("VERTEXAI_PROJECT") or environ.get("VERTEX_PROJECT")
location: Final = environ.get("VERTEXAI_LOCATION") or environ.get("VERTEX_LOCATION") or "us-central1"
if not api_key or not project:
return ()
upstream_base: Final = environ.get("VERTEX_AI_API_BASE") or f"https://{location}-aiplatform.googleapis.com"
def invoke(api_base: str, case_input: OcrSdkInputBase) -> object:
return sdk_call(api_base=api_base, api_key=api_key, **case_input.as_sdk_kwargs())
return (
OcrFixtureTarget(
name="vertex-mistral",
provider_spec=ProviderSpec(upstream_base=upstream_base.rstrip("/")),
strategy=cast(
SearchStrategy[OcrSdkInputBase],
st.builds(
_as_vertex_mistral,
case_input=mistral_input_strategy(_MISTRAL_MODEL),
project=st.just(project),
location=st.just(location),
),
),
invoke=invoke,
required_inputs=cast(
tuple[OcrSdkInputBase, ...],
tuple(
_as_vertex_mistral(case_input, project, location)
for case_input in _required_mistral_inputs(_MISTRAL_MODEL)
),
),
),
OcrFixtureTarget(
name="vertex-deepseek",
provider_spec=ProviderSpec(upstream_base=upstream_base.rstrip("/")),
strategy=cast(
SearchStrategy[OcrSdkInputBase], vertex_deepseek_input_strategy(project, location)
),
invoke=invoke,
required_inputs=cast(
tuple[OcrSdkInputBase, ...], _required_vertex_deepseek_inputs(project, location)
),
),
)
def _reducto_targets(
environ: Mapping[str, str], sdk_call: Callable[..., object]
) -> tuple[OcrFixtureTarget, ...]:
api_key: Final = environ.get("REDUCTO_API_KEY")
if not api_key:
return ()
upstream_base: Final = environ.get("REDUCTO_API_BASE", _REDUCTO_API_BASE).rstrip("/")
document: Final = ReductoDocumentUrlDocument(type="document_url", document_url=_fixture_pdf_data_uri())
def invoke(api_base: str, case_input: OcrSdkInputBase) -> object:
return sdk_call(api_base=api_base, api_key=api_key, **case_input.as_sdk_kwargs())
return (
OcrFixtureTarget(
name="reducto-v3",
provider_spec=ProviderSpec(upstream_base=upstream_base),
strategy=cast(SearchStrategy[OcrSdkInputBase], reducto_v3_input_strategy(document)),
invoke=invoke,
required_inputs=cast(tuple[OcrSdkInputBase, ...], _required_reducto_v3_inputs(document)),
),
OcrFixtureTarget(
name="reducto-legacy",
provider_spec=ProviderSpec(upstream_base=upstream_base),
strategy=cast(SearchStrategy[OcrSdkInputBase], reducto_legacy_input_strategy(document)),
invoke=invoke,
required_inputs=cast(
tuple[OcrSdkInputBase, ...],
(
ReductoParseLegacySdkInput(model="reducto/parse-legacy", document=document),
ReductoParseLegacySdkInput(model="reducto/parse-legacy", document=document, enhance={}),
),
),
),
)
@ -271,14 +569,19 @@ def discover_targets(
environ: Mapping[str, str],
sdk_call: Callable[..., object],
) -> tuple[OcrFixtureTarget, ...]:
candidates: Final = (_mistral_target(environ, sdk_call),)
return tuple(target for target in candidates if target is not None)
optional_targets: Final = (
_mistral_target(environ, sdk_call),
_azure_mistral_target(environ, sdk_call),
_azure_document_intelligence_target(environ, sdk_call),
)
direct_targets: Final = tuple(target for target in optional_targets if target is not None)
return (*direct_targets, *_vertex_targets(environ, sdk_call), *_reducto_targets(environ, sdk_call))
def require_targets(targets: tuple[OcrFixtureTarget, ...]) -> tuple[OcrFixtureTarget, ...]:
return require_fixture_targets(
targets,
"No OCR fixture providers are configured. Set MISTRAL_API_KEY",
"No OCR fixture providers are configured. Set a supported provider API key and endpoint",
)

View file

@ -10,7 +10,10 @@ from pydantic import ValidationError
from litellm.llms.base_llm.ocr.transformation import BaseOCRConfig
from litellm.llms.mistral.ocr.transformation import MistralOCRConfig
from litellm.llms.reducto.ocr.transformation import ReductoParseLegacyConfig, ReductoParseV3Config
from litellm.llms.vertex_ai.ocr.deepseek_transformation import VertexAIDeepSeekOCRConfig
from tests.test_litellm.ocr.fixture_models import (
AzureDocumentIntelligenceOcrSdkInput,
AzureMistralOcrSdkInput,
JsonSchemaDefinition,
JsonSchemaResponseFormat,
MistralDocumentUrlDocument,
@ -26,14 +29,20 @@ from tests.test_litellm.ocr.fixture_models import (
ReductoParseV3SdkInput,
ReductoRetrieval,
ReductoSettings,
VertexDeepSeekOcrSdkInput,
VertexMistralOcrSdkInput,
)
from tests.test_litellm.ocr.generate_fixtures import (
azure_document_intelligence_input_strategy,
mistral_input_strategy,
reducto_legacy_input_strategy,
reducto_v3_input_strategy,
vertex_deepseek_input_strategy,
)
COMMON_FIELDS: Final = frozenset({"model", "document", "custom_llm_provider"})
COMMON_FIELDS: Final = frozenset(
{"boundary", "model", "document", "custom_llm_provider", "vertex_project", "vertex_location"}
)
def _provider_fields(model: type[OcrSdkInputBase]) -> set[str]:
@ -71,6 +80,65 @@ def test_reducto_fixture_fields_match_provider_configs() -> None:
assert _provider_fields(ReductoParseLegacySdkInput) == _supported_params(ReductoParseLegacyConfig(), "parse-legacy")
def test_deepseek_fixture_fields_match_provider_config() -> None:
assert _provider_fields(VertexDeepSeekOcrSdkInput) == _supported_params(
VertexAIDeepSeekOCRConfig(), "deepseek-ai/deepseek-ocr-maas"
)
def test_deepseek_maps_litellm_params_without_duplicating_model_namespace() -> None:
config: Final = VertexAIDeepSeekOCRConfig()
optional_params: Final = config.map_ocr_params(
non_default_params={"temperature": 0.5, "max_tokens": 256},
optional_params={},
model="deepseek-ai/deepseek-ocr-maas",
)
request: Final = config.transform_ocr_request(
model="deepseek-ai/deepseek-ocr-maas",
document={"type": "image_url", "image_url": "gs://bucket/document.png"},
optional_params=optional_params,
headers={},
)
assert request.data == {
"model": "deepseek-ai/deepseek-ocr-maas",
"messages": [
{
"role": "user",
"content": [{"type": "image_url", "image_url": "gs://bucket/document.png"}],
}
],
"temperature": 0.5,
"max_tokens": 256,
}
@pytest.mark.parametrize(
"sdk_input",
(
AzureMistralOcrSdkInput(
model="azure_ai/mistral-ocr-deployment",
document=MistralImageUrlDocument(type="image_url", image_url="data:image/png;base64,AA=="),
),
VertexMistralOcrSdkInput(
document=MistralImageUrlDocument(type="image_url", image_url="data:image/png;base64,AA=="),
vertex_project="project-1",
),
AzureDocumentIntelligenceOcrSdkInput(
model="azure_ai/doc-intelligence/prebuilt-layout",
document=MistralImageUrlDocument(type="image_url", image_url="data:image/png;base64,AA=="),
),
VertexDeepSeekOcrSdkInput(
document=MistralImageUrlDocument(type="image_url", image_url="data:image/png;base64,AA=="),
vertex_project="project-1",
),
),
)
def test_provider_boundary_is_explicit_but_not_forwarded(sdk_input: OcrSdkInputBase) -> None:
assert sdk_input.canonical_input()["boundary"] == sdk_input.boundary
assert "boundary" not in sdk_input.as_sdk_kwargs()
def test_mistral_input_preserves_omission_and_explicit_boolean_values() -> None:
omitted: Final = _mistral_input().as_sdk_kwargs()
explicit: Final = _mistral_input(extract_header=False, include_blocks=True).as_sdk_kwargs()
@ -227,5 +295,23 @@ def test_reducto_v3_strategy_only_generates_valid_sdk_inputs(sdk_input: ReductoP
@settings(max_examples=10, deadline=None)
@given(sdk_input=reducto_legacy_input_strategy())
def test_reducto_legacy_strategy_omits_undocumented_enhance(sdk_input: ReductoParseLegacySdkInput) -> None:
assert "enhance" not in sdk_input.as_sdk_kwargs()
def test_reducto_legacy_strategy_generates_valid_litellm_inputs(sdk_input: ReductoParseLegacySdkInput) -> None:
assert ReductoParseLegacySdkInput.model_validate(sdk_input.canonical_input()) == sdk_input
@settings(max_examples=30, deadline=None)
@given(sdk_input=azure_document_intelligence_input_strategy())
def test_azure_document_intelligence_strategy_only_generates_litellm_inputs(
sdk_input: AzureDocumentIntelligenceOcrSdkInput,
) -> None:
assert sdk_input.req_format == "litellm"
assert "boundary" not in sdk_input.as_sdk_kwargs()
@settings(max_examples=30, deadline=None)
@given(sdk_input=vertex_deepseek_input_strategy("project-1", "us-central1"))
def test_vertex_deepseek_strategy_only_generates_litellm_inputs(
sdk_input: VertexDeepSeekOcrSdkInput,
) -> None:
assert sdk_input.vertex_project == "project-1"
assert "boundary" not in sdk_input.as_sdk_kwargs()

View file

@ -41,10 +41,51 @@ def test_discovery_requires_provider_specific_key(environ: dict[str, str]) -> No
def test_no_discovered_targets_has_actionable_error() -> None:
with pytest.raises(SystemExit, match="Set MISTRAL_API_KEY"):
with pytest.raises(SystemExit, match="supported provider API key"):
require_targets(())
def test_discovery_is_explicit_per_available_provider_boundary() -> None:
targets: Final = discover_targets(
{
"MISTRAL_API_KEY": "mistral-secret",
"REDUCTO_API_KEY": "reducto-secret",
"AZURE_AI_API_KEY": "azure-secret",
"AZURE_AI_API_BASE": "https://azure.example",
"AZURE_AI_OCR_MODEL": "mistral-ocr-deployment",
"AZURE_DOCUMENT_INTELLIGENCE_API_KEY": "document-secret",
"AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT": "https://document.example",
"VERTEX_AI_API_KEY": "vertex-secret",
"VERTEXAI_PROJECT": "project-1",
},
_unused_sdk_call,
)
assert tuple(target.name for target in targets) == (
"mistral-ocr",
"azure-mistral",
"azure-document-intelligence",
"vertex-mistral",
"vertex-deepseek",
"reducto-v3",
"reducto-legacy",
)
assert all("secret" not in repr(target) for target in targets)
def test_azure_mistral_discovery_requires_and_normalizes_deployment_model() -> None:
incomplete: Final = {
"AZURE_AI_API_KEY": "azure-secret",
"AZURE_AI_API_BASE": "https://azure.example",
}
assert discover_targets(incomplete, _unused_sdk_call) == ()
target: Final = discover_targets(
{**incomplete, "AZURE_AI_OCR_MODEL": "mistral-ocr-deployment"}, _unused_sdk_call
)[0]
assert target.required_inputs[0].model == "azure_ai/mistral-ocr-deployment"
@pytest.mark.parametrize(
("configured", "expected"),
(
@ -71,6 +112,28 @@ def test_mistral_target_uses_canonical_model_and_normalized_base(
case_inputs: Final = generate_case_inputs(target.strategy, examples=1)
assert len(case_inputs) == 1
assert case_inputs[0].canonical_input()["model"] == "mistral/mistral-ocr-latest"
assert len(target.required_inputs) == 14
covered_params: Final = {
key
for case_input in target.required_inputs
for key in case_input.as_sdk_kwargs()
if key not in {"model", "document", "custom_llm_provider"}
}
assert covered_params == {
"pages",
"include_image_base64",
"image_limit",
"image_min_size",
"bbox_annotation_format",
"document_annotation_format",
"document_annotation_prompt",
"extract_header",
"extract_footer",
"table_format",
"confidence_scores_granularity",
"include_blocks",
"id",
}
def test_mistral_target_invocation_forwards_discovered_credentials() -> None:

View file

@ -28,7 +28,7 @@ from tests.route_parity.runner import (
parity_worker_main,
run_execution,
)
from tests.test_litellm.ocr.fixture_models import MistralOcrSdkInput, OcrParityCase
from tests.test_litellm.ocr.fixture_models import OcrParityCase, OcrSdkInput
API_KEY: Final = "test-key"
PYTHON_HTTP_SENTINEL: Final = "python-ocr-parity-fallback"
@ -40,7 +40,7 @@ class SDKRoute(str, Enum):
AOCR = "aocr"
def _call_kwargs(sdk_input: MistralOcrSdkInput, mock_url: str, route: SDKRoute) -> dict[str, object]:
def _call_kwargs(sdk_input: OcrSdkInput, mock_url: str, route: SDKRoute) -> dict[str, object]:
return {
**sdk_input.as_sdk_kwargs(),
"api_base": mock_url,
@ -50,7 +50,7 @@ def _call_kwargs(sdk_input: MistralOcrSdkInput, mock_url: str, route: SDKRoute)
def _execute_sdk_case(
sdk_input: MistralOcrSdkInput,
sdk_input: OcrSdkInput,
route: SDKRoute,
mock_url: str,
event_loop: asyncio.AbstractEventLoop,
@ -67,7 +67,7 @@ def _execute_sdk_case(
return SDKReport(response=async_response.model_dump(mode="json"))
def _call_sdk_case(sdk_input: MistralOcrSdkInput, route: SDKRoute, mock_url: str) -> OCRResponse:
def _call_sdk_case(sdk_input: OcrSdkInput, route: SDKRoute, mock_url: str) -> OCRResponse:
import litellm
call_kwargs: Final = _call_kwargs(sdk_input, mock_url, route)
@ -185,12 +185,14 @@ def test_recorded_ocr_sdk_parity(
ocr_fixture: OcrParityCase,
route: SDKRoute,
) -> None:
if ocr_fixture.litellm_input.boundary in {"reducto_v3", "reducto_legacy"}:
pytest.skip("Reducto does not have a Rust OCR boundary")
sync_spy, async_spy = _native_spies()
with _restore_rust_ocr_state(), replay_server() as provider:
rust_ocr_bridge.use_litellm_rust(False, ocr=sync_spy, aocr=async_spy)
python: Final = run_in_process(
provider,
ocr_fixture.provider_response,
ocr_fixture.provider_responses,
lambda mock_url: _call_sdk_case(ocr_fixture.litellm_input, route, mock_url),
)
assert sync_spy.calls == 0
@ -199,13 +201,13 @@ def test_recorded_ocr_sdk_parity(
rust_ocr_bridge.use_litellm_rust(True, ocr=sync_spy, aocr=async_spy)
rust: Final = run_in_process(
provider,
ocr_fixture.provider_response,
ocr_fixture.provider_responses,
lambda mock_url: _call_sdk_case(ocr_fixture.litellm_input, route, mock_url),
)
assert sync_spy.calls == (1 if route is SDKRoute.OCR else 0)
assert async_spy.calls == (1 if route is SDKRoute.AOCR else 0)
assert_request_parity(python.request, rust.request)
assert_request_parity(python.requests, rust.requests)
assert_model_parity(python.response, rust.response)
@ -221,13 +223,13 @@ def test_ocr_subprocess_startup_smoke(
python_worker,
case_file,
SDKRoute.OCR.value,
startup_ocr_fixture.provider_response,
startup_ocr_fixture.provider_responses,
)
rust: Final = run_execution(
rust_worker,
case_file,
SDKRoute.OCR.value,
startup_ocr_fixture.provider_response,
startup_ocr_fixture.provider_responses,
)
assert_parity(python, rust, PYTHON_HTTP_SENTINEL)