diff --git a/litellm/llms/base_llm/ocr/transformation.py b/litellm/llms/base_llm/ocr/transformation.py index d1c77186ea8..fff76c9f7b5 100644 --- a/litellm/llms/base_llm/ocr/transformation.py +++ b/litellm/llms/base_llm/ocr/transformation.py @@ -92,8 +92,12 @@ class OCRResponse(LiteLLMPydanticObjectBase): document_annotation: Any | None = None usage_info: OCRUsageInfo | None = None content: str | None = None - tables: list[dict[str, object]] | None = None - keyValuePairs: list[dict[str, object]] | None = None + # `Any`, not `object`: the `object` field below shadows the builtin in this + # class namespace, and pydantic resolves the deferred annotations against + # that namespace — so `dict[str, object]` resolves to the string "ocr" and + # the model can never be built. + tables: list[dict[str, Any]] | None = None + keyValuePairs: list[dict[str, Any]] | None = None object: str = "ocr" model_config = {"extra": "allow"} diff --git a/tests/test_litellm/ocr/test_ocr_response_model_build.py b/tests/test_litellm/ocr/test_ocr_response_model_build.py new file mode 100644 index 00000000000..94773595465 --- /dev/null +++ b/tests/test_litellm/ocr/test_ocr_response_model_build.py @@ -0,0 +1,32 @@ +"""`OCRResponse` must be constructible. + +The model declares a field literally named `object`, which shadows the builtin +inside the class namespace. Pydantic resolves the deferred annotations against +that namespace, so any *other* field annotated with the builtin `object` +resolves to the string "ocr" and pydantic treats it as an unresolved forward +reference — the model then cannot be built at all. +""" + +from litellm.llms.base_llm.ocr.transformation import OCRPage, OCRResponse + + +def test_ocr_response_can_be_constructed(): + response = OCRResponse( + pages=[OCRPage(index=0, markdown="# hello")], + model="mistral-ocr-latest", + ) + + assert response.object == "ocr" + assert response.pages[0].markdown == "# hello" + + +def test_ocr_response_accepts_tables_and_key_value_pairs(): + response = OCRResponse( + pages=[OCRPage(index=0, markdown="# hello")], + model="mistral-ocr-latest", + tables=[{"rows": [["a", "b"]]}], + keyValuePairs=[{"key": "invoice_no", "value": "42"}], + ) + + assert response.tables == [{"rows": [["a", "b"]]}] + assert response.keyValuePairs == [{"key": "invoice_no", "value": "42"}]