Skip to content

Commit 6f080ec

Browse files
Translate PDF: Improve response types
- `TranslatePdfTextResponse` gets missing `source_languages` and `output_language` fields. - Adds `TranslatePdfTextFileResponse`. This inherits from `PdfRestFileBasedResponse` with additional Translate PDF fields. - Add `FileBasedResponse` TypeVar bound to `PdfRestFileBasedResponse` so classes like `TranslatePdfTextFileResponse` can reuse file fetch logic. Assisted-by: Codex
1 parent 97821c4 commit 6f080ec

5 files changed

Lines changed: 103 additions & 29 deletions

File tree

src/pdfrest/client.py

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,10 @@
6969
PdfRestFileID,
7070
PdfRestInfoResponse,
7171
SummarizePdfTextResponse,
72+
TranslatePdfTextFileResponse,
7273
TranslatePdfTextResponse,
7374
UpResponse,
7475
)
75-
76-
__all__ = ("AsyncPdfRestClient", "PdfRestClient")
77-
7876
from .models._internal import (
7977
BasePdfRestGraphicPayload,
8078
BmpPdfRestPayload,
@@ -122,6 +120,9 @@
122120
TranslateOutputFormat,
123121
)
124122

123+
__all__ = ("AsyncPdfRestClient", "PdfRestClient")
124+
FileResponseModel = TypeVar("FileResponseModel", bound=PdfRestFileBasedResponse)
125+
125126
DEFAULT_BASE_URL = "https://api.pdfrest.com"
126127
API_KEY_ENV_VAR = "PDFREST_API_KEY"
127128
API_KEY_HEADER_NAME = "Api-Key"
@@ -986,11 +987,12 @@ def _post_file_operation(
986987
endpoint: str,
987988
payload: dict[str, Any],
988989
payload_model: type[BaseModel],
990+
response_model: type[FileResponseModel] = PdfRestFileBasedResponse,
989991
extra_query: Query | None = None,
990992
extra_headers: AnyMapping | None = None,
991993
extra_body: Body | None = None,
992994
timeout: TimeoutTypes | None = None,
993-
) -> PdfRestFileBasedResponse:
995+
) -> FileResponseModel:
994996
job_options = payload_model.model_validate(payload)
995997
json_body = job_options.model_dump(
996998
mode="json", by_alias=True, exclude_none=True, exclude_unset=True
@@ -1018,15 +1020,17 @@ def _post_file_operation(
10181020
for file_id in output_ids
10191021
]
10201022

1021-
return PdfRestFileBasedResponse.model_validate(
1022-
{
1023-
"input_id": [str(file_id) for file_id in raw_response.input_id],
1024-
"output_file": [
1025-
file.model_dump(mode="json", by_alias=True) for file in output_files
1026-
],
1027-
"warning": raw_response.warning,
1028-
}
1029-
)
1023+
response_payload: dict[str, Any] = {
1024+
"input_id": [str(file_id) for file_id in raw_response.input_id],
1025+
"output_file": [
1026+
file.model_dump(mode="json", by_alias=True) for file in output_files
1027+
],
1028+
"warning": raw_response.warning,
1029+
}
1030+
if raw_response.model_extra:
1031+
response_payload.update(raw_response.model_extra)
1032+
1033+
return response_model.model_validate(response_payload)
10301034

10311035
def send_request(self, request: _RequestModel) -> Any:
10321036
return self._send_request(request)
@@ -1250,11 +1254,12 @@ async def _post_file_operation(
12501254
endpoint: str,
12511255
payload: dict[str, Any],
12521256
payload_model: type[BaseModel],
1257+
response_model: type[FileResponseModel] = PdfRestFileBasedResponse,
12531258
extra_query: Query | None = None,
12541259
extra_headers: AnyMapping | None = None,
12551260
extra_body: Body | None = None,
12561261
timeout: TimeoutTypes | None = None,
1257-
) -> PdfRestFileBasedResponse:
1262+
) -> FileResponseModel:
12581263
job_options = payload_model.model_validate(payload)
12591264
request = self.prepare_request(
12601265
"POST",
@@ -1290,15 +1295,17 @@ async def throttled_fetch_file_info(file_id: str) -> PdfRestFile:
12901295
)
12911296
)
12921297

1293-
return PdfRestFileBasedResponse.model_validate(
1294-
{
1295-
"input_id": [str(file_id) for file_id in raw_response.input_id],
1296-
"output_file": [
1297-
file.model_dump(mode="json", by_alias=True) for file in output_files
1298-
],
1299-
"warning": raw_response.warning,
1300-
}
1301-
)
1298+
response_payload: dict[str, Any] = {
1299+
"input_id": [str(file_id) for file_id in raw_response.input_id],
1300+
"output_file": [
1301+
file.model_dump(mode="json", by_alias=True) for file in output_files
1302+
],
1303+
"warning": raw_response.warning,
1304+
}
1305+
if raw_response.model_extra:
1306+
response_payload.update(raw_response.model_extra)
1307+
1308+
return response_model.model_validate(response_payload)
13021309

13031310
async def send_request(self, request: _RequestModel) -> Any:
13041311
return await self._send_request(request)
@@ -2334,7 +2341,7 @@ def translate_pdf_text_to_file(
23342341
extra_headers: AnyMapping | None = None,
23352342
extra_body: Body | None = None,
23362343
timeout: TimeoutTypes | None = None,
2337-
) -> PdfRestFileBasedResponse:
2344+
) -> TranslatePdfTextFileResponse:
23382345
"""Translate textual content and receive a file-based response."""
23392346

23402347
payload: dict[str, Any] = {
@@ -2356,6 +2363,7 @@ def translate_pdf_text_to_file(
23562363
extra_headers=extra_headers,
23572364
extra_body=extra_body,
23582365
timeout=timeout,
2366+
response_model=TranslatePdfTextFileResponse,
23592367
)
23602368

23612369
def extract_images(
@@ -3323,7 +3331,7 @@ async def translate_pdf_text_to_file(
33233331
extra_headers: AnyMapping | None = None,
33243332
extra_body: Body | None = None,
33253333
timeout: TimeoutTypes | None = None,
3326-
) -> PdfRestFileBasedResponse:
3334+
) -> TranslatePdfTextFileResponse:
33273335
"""Translate textual content and receive a file-based response."""
33283336

33293337
payload: dict[str, Any] = {
@@ -3345,6 +3353,7 @@ async def translate_pdf_text_to_file(
33453353
extra_headers=extra_headers,
33463354
extra_body=extra_body,
33473355
timeout=timeout,
3356+
response_model=TranslatePdfTextFileResponse,
33483357
)
33493358

33503359
async def extract_images(

src/pdfrest/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
PdfRestFileID,
99
PdfRestInfoResponse,
1010
SummarizePdfTextResponse,
11+
TranslatePdfTextFileResponse,
1112
TranslatePdfTextResponse,
1213
UpResponse,
1314
)
@@ -22,6 +23,7 @@
2223
"PdfRestFileID",
2324
"PdfRestInfoResponse",
2425
"SummarizePdfTextResponse",
26+
"TranslatePdfTextFileResponse",
2527
"TranslatePdfTextResponse",
2628
"UpResponse",
2729
]

src/pdfrest/models/public.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"PdfRestFileID",
3030
"PdfRestInfoResponse",
3131
"SummarizePdfTextResponse",
32+
"TranslatePdfTextFileResponse",
3233
"TranslatePdfTextResponse",
3334
"UpResponse",
3435
)
@@ -360,6 +361,24 @@ class TranslatePdfTextResponse(BaseModel):
360361

361362
model_config = ConfigDict(extra="allow")
362363

364+
source_languages: Annotated[
365+
list[str] | None,
366+
Field(
367+
alias="source_languages",
368+
validation_alias=AliasChoices("source_languages", "sourceLanguages"),
369+
description="Languages detected in the source content.",
370+
default=None,
371+
),
372+
] = None
373+
output_language: Annotated[
374+
str | None,
375+
Field(
376+
alias="output_language",
377+
validation_alias=AliasChoices("output_language", "outputLanguage"),
378+
description="Target language used for the translation.",
379+
default=None,
380+
),
381+
] = None
363382
translated_text: Annotated[
364383
str | None,
365384
Field(
@@ -396,6 +415,31 @@ class TranslatePdfTextResponse(BaseModel):
396415
] = None
397416

398417

418+
class TranslatePdfTextFileResponse(PdfRestFileBasedResponse):
419+
"""File-based response returned by the translated-pdf-text tool."""
420+
421+
model_config = ConfigDict(extra="allow")
422+
423+
source_languages: Annotated[
424+
list[str] | None,
425+
Field(
426+
alias="source_languages",
427+
validation_alias=AliasChoices("source_languages", "sourceLanguages"),
428+
description="Languages detected in the source content.",
429+
default=None,
430+
),
431+
] = None
432+
output_language: Annotated[
433+
str | None,
434+
Field(
435+
alias="output_language",
436+
validation_alias=AliasChoices("output_language", "outputLanguage"),
437+
description="Target language used for the translation.",
438+
default=None,
439+
),
440+
] = None
441+
442+
399443
class ExtractTextResponse(BaseModel):
400444
"""Response returned by the extracted-text tool."""
401445

tests/live/test_live_translate_pdf_text.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import pytest
44

55
from pdfrest import PdfRestApiError, PdfRestClient
6-
from pdfrest.models import PdfRestFileBasedResponse, TranslatePdfTextResponse
6+
from pdfrest.models import (
7+
TranslatePdfTextFileResponse,
8+
TranslatePdfTextResponse,
9+
)
710

811
from ..resources import get_test_resource_path
912

@@ -26,6 +29,8 @@ def test_live_translate_pdf_text_success(
2629

2730
assert isinstance(response, TranslatePdfTextResponse)
2831
assert response.translated_text
32+
assert response.output_language == "fr"
33+
assert response.source_languages
2934
assert response.input_id == uploaded.id
3035

3136

@@ -63,6 +68,8 @@ def test_live_translate_pdf_text_file_success(
6368
output_format="plaintext",
6469
)
6570

66-
assert isinstance(response, PdfRestFileBasedResponse)
71+
assert isinstance(response, TranslatePdfTextFileResponse)
6772
assert response.output_files
73+
assert response.output_language == "fr"
74+
assert response.source_languages
6875
assert response.input_id == uploaded.id

tests/test_translate_pdf_text.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from pdfrest import AsyncPdfRestClient, PdfRestClient
1010
from pdfrest.models import (
1111
PdfRestFile,
12-
PdfRestFileBasedResponse,
1312
PdfRestFileID,
13+
TranslatePdfTextFileResponse,
1414
TranslatePdfTextResponse,
1515
)
1616
from pdfrest.models._internal import TranslatePdfTextPayload
@@ -86,6 +86,8 @@ def handler(request: httpx.Request) -> httpx.Response:
8686
json={
8787
"translated_text": "Bonjour",
8888
"inputId": str(input_file.id),
89+
"source_languages": ["en"],
90+
"output_language": "fr",
8991
},
9092
)
9193
msg = f"Unexpected request {request.method} {request.url}"
@@ -104,6 +106,8 @@ def handler(request: httpx.Request) -> httpx.Response:
104106
assert seen == {"post": 1}
105107
assert isinstance(response, TranslatePdfTextResponse)
106108
assert response.translated_text == "Bonjour"
109+
assert response.source_languages == ["en"]
110+
assert response.output_language == "fr"
107111
assert response.input_id == input_file.id
108112
assert response.output_id is None
109113
assert response.output_url is None
@@ -140,6 +144,8 @@ def handler(request: httpx.Request) -> httpx.Response:
140144
"outputUrl": f"https://api.pdfrest.com/resource/{output_id}?format=file",
141145
"outputId": output_id,
142146
"inputId": str(input_file.id),
147+
"source_languages": ["en"],
148+
"output_language": "es",
143149
},
144150
)
145151
if request.method == "GET" and request.url.path == f"/resource/{output_id}":
@@ -166,8 +172,10 @@ def handler(request: httpx.Request) -> httpx.Response:
166172
timeout=0.3,
167173
)
168174

169-
assert isinstance(response, PdfRestFileBasedResponse)
175+
assert isinstance(response, TranslatePdfTextFileResponse)
170176
assert response.output_file.id == output_id
177+
assert response.output_language == "es"
178+
assert response.source_languages == ["en"]
171179
timeout_value = captured_timeout["value"]
172180
assert timeout_value is not None
173181
if isinstance(timeout_value, dict):
@@ -201,6 +209,8 @@ def handler(request: httpx.Request) -> httpx.Response:
201209
json={
202210
"translated_text": "Hallo",
203211
"inputId": str(input_file.id),
212+
"source_languages": ["en"],
213+
"output_language": "de",
204214
},
205215
)
206216
msg = f"Unexpected request {request.method} {request.url}"
@@ -216,4 +226,6 @@ def handler(request: httpx.Request) -> httpx.Response:
216226
assert seen == {"post": 1}
217227
assert isinstance(response, TranslatePdfTextResponse)
218228
assert response.translated_text == "Hallo"
229+
assert response.source_languages == ["en"]
230+
assert response.output_language == "de"
219231
assert response.input_id == input_file.id

0 commit comments

Comments
 (0)