retab 0.0.81__tar.gz → 0.0.83__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {retab-0.0.81 → retab-0.0.83}/PKG-INFO +1 -1
  2. {retab-0.0.81 → retab-0.0.83}/retab/client.py +3 -1
  3. {retab-0.0.81 → retab-0.0.83}/retab/resources/documents/client.py +8 -8
  4. retab-0.0.83/retab/resources/edit/__init__.py +3 -0
  5. retab-0.0.83/retab/resources/edit/client.py +176 -0
  6. retab-0.0.83/retab/resources/edit/templates/__init__.py +4 -0
  7. retab-0.0.83/retab/resources/edit/templates/client.py +620 -0
  8. {retab-0.0.81 → retab-0.0.83}/retab/types/documents/edit.py +13 -25
  9. retab-0.0.83/retab/types/edit/__init__.py +16 -0
  10. retab-0.0.83/retab/types/edit/templates.py +50 -0
  11. {retab-0.0.81 → retab-0.0.83}/retab.egg-info/PKG-INFO +1 -1
  12. {retab-0.0.81 → retab-0.0.83}/retab.egg-info/SOURCES.txt +6 -0
  13. {retab-0.0.81 → retab-0.0.83}/setup.py +1 -1
  14. {retab-0.0.81 → retab-0.0.83}/README.md +0 -0
  15. {retab-0.0.81 → retab-0.0.83}/pyproject.toml +0 -0
  16. {retab-0.0.81 → retab-0.0.83}/retab/__init__.py +0 -0
  17. {retab-0.0.81 → retab-0.0.83}/retab/_resource.py +0 -0
  18. {retab-0.0.81 → retab-0.0.83}/retab/generate_types.py +0 -0
  19. {retab-0.0.81 → retab-0.0.83}/retab/py.typed +0 -0
  20. {retab-0.0.81 → retab-0.0.83}/retab/resources/__init__.py +0 -0
  21. {retab-0.0.81 → retab-0.0.83}/retab/resources/documents/__init__.py +0 -0
  22. {retab-0.0.81 → retab-0.0.83}/retab/resources/extractions/__init__.py +0 -0
  23. {retab-0.0.81 → retab-0.0.83}/retab/resources/extractions/client.py +0 -0
  24. {retab-0.0.81 → retab-0.0.83}/retab/resources/models.py +0 -0
  25. {retab-0.0.81 → retab-0.0.83}/retab/resources/projects/__init__.py +0 -0
  26. {retab-0.0.81 → retab-0.0.83}/retab/resources/projects/client.py +0 -0
  27. {retab-0.0.81 → retab-0.0.83}/retab/resources/schemas.py +0 -0
  28. {retab-0.0.81 → retab-0.0.83}/retab/resources/workflows/__init__.py +0 -0
  29. {retab-0.0.81 → retab-0.0.83}/retab/resources/workflows/client.py +0 -0
  30. {retab-0.0.81 → retab-0.0.83}/retab/types/__init__.py +0 -0
  31. {retab-0.0.81 → retab-0.0.83}/retab/types/chat.py +0 -0
  32. {retab-0.0.81 → retab-0.0.83}/retab/types/documents/__init__.py +0 -0
  33. {retab-0.0.81 → retab-0.0.83}/retab/types/documents/classify.py +0 -0
  34. {retab-0.0.81 → retab-0.0.83}/retab/types/documents/correct_orientation.py +0 -0
  35. {retab-0.0.81 → retab-0.0.83}/retab/types/documents/create_messages.py +0 -0
  36. {retab-0.0.81 → retab-0.0.83}/retab/types/documents/extract.py +0 -0
  37. {retab-0.0.81 → retab-0.0.83}/retab/types/documents/parse.py +0 -0
  38. {retab-0.0.81 → retab-0.0.83}/retab/types/documents/split.py +0 -0
  39. {retab-0.0.81 → retab-0.0.83}/retab/types/extractions/__init__.py +0 -0
  40. {retab-0.0.81 → retab-0.0.83}/retab/types/extractions/types.py +0 -0
  41. {retab-0.0.81 → retab-0.0.83}/retab/types/inference_settings.py +0 -0
  42. {retab-0.0.81 → retab-0.0.83}/retab/types/mime.py +0 -0
  43. {retab-0.0.81 → retab-0.0.83}/retab/types/modality.py +0 -0
  44. {retab-0.0.81 → retab-0.0.83}/retab/types/pagination.py +0 -0
  45. {retab-0.0.81 → retab-0.0.83}/retab/types/projects/__init__.py +0 -0
  46. {retab-0.0.81 → retab-0.0.83}/retab/types/projects/metrics.py +0 -0
  47. {retab-0.0.81 → retab-0.0.83}/retab/types/projects/model.py +0 -0
  48. {retab-0.0.81 → retab-0.0.83}/retab/types/projects/predictions.py +0 -0
  49. {retab-0.0.81 → retab-0.0.83}/retab/types/schemas/__init__.py +0 -0
  50. {retab-0.0.81 → retab-0.0.83}/retab/types/schemas/chat.py +0 -0
  51. {retab-0.0.81 → retab-0.0.83}/retab/types/schemas/generate.py +0 -0
  52. {retab-0.0.81 → retab-0.0.83}/retab/types/schemas/layout.py +0 -0
  53. {retab-0.0.81 → retab-0.0.83}/retab/types/schemas/model.py +0 -0
  54. {retab-0.0.81 → retab-0.0.83}/retab/types/schemas/templates.py +0 -0
  55. {retab-0.0.81 → retab-0.0.83}/retab/types/standards.py +0 -0
  56. {retab-0.0.81 → retab-0.0.83}/retab/types/workflows/__init__.py +0 -0
  57. {retab-0.0.81 → retab-0.0.83}/retab/types/workflows/model.py +0 -0
  58. {retab-0.0.81 → retab-0.0.83}/retab/utils/__init__.py +0 -0
  59. {retab-0.0.81 → retab-0.0.83}/retab/utils/display.py +0 -0
  60. {retab-0.0.81 → retab-0.0.83}/retab/utils/hashing.py +0 -0
  61. {retab-0.0.81 → retab-0.0.83}/retab/utils/json_schema.py +0 -0
  62. {retab-0.0.81 → retab-0.0.83}/retab/utils/mime.py +0 -0
  63. {retab-0.0.81 → retab-0.0.83}/retab/utils/stream_context_managers.py +0 -0
  64. {retab-0.0.81 → retab-0.0.83}/retab.egg-info/dependency_links.txt +0 -0
  65. {retab-0.0.81 → retab-0.0.83}/retab.egg-info/requires.txt +0 -0
  66. {retab-0.0.81 → retab-0.0.83}/retab.egg-info/top_level.txt +0 -0
  67. {retab-0.0.81 → retab-0.0.83}/setup.cfg +0 -0
  68. {retab-0.0.81 → retab-0.0.83}/tests/test_projects.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: retab
3
- Version: 0.0.81
3
+ Version: 0.0.83
4
4
  Summary: Retab official python library
5
5
  Home-page: https://github.com/retab-dev/retab
6
6
  Author: Retab
@@ -10,7 +10,7 @@ import backoff.types
10
10
  import httpx
11
11
  import truststore
12
12
 
13
- from .resources import documents, models, schemas, projects, extractions
13
+ from .resources import documents, models, schemas, projects, extractions, edit
14
14
  from .types.standards import PreparedRequest, FieldUnset
15
15
 
16
16
 
@@ -188,6 +188,7 @@ class Retab(BaseRetab):
188
188
  self.documents = documents.Documents(client=self)
189
189
  self.models = models.Models(client=self)
190
190
  self.schemas = schemas.Schemas(client=self)
191
+ self.edit = edit.Edit(client=self)
191
192
 
192
193
  def _request(
193
194
  self,
@@ -485,6 +486,7 @@ class AsyncRetab(BaseRetab):
485
486
  self.documents = documents.AsyncDocuments(client=self)
486
487
  self.models = models.AsyncModels(client=self)
487
488
  self.schemas = schemas.AsyncSchemas(client=self)
489
+ self.edit = edit.AsyncEdit(client=self)
488
490
 
489
491
  def _parse_response(self, response: httpx.Response) -> Any:
490
492
  """Parse response based on content-type.
@@ -119,14 +119,14 @@ class BaseDocumentsMixin:
119
119
 
120
120
  def _prepare_edit(
121
121
  self,
122
- filling_instructions: str,
122
+ instructions: str,
123
123
  document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
124
124
  model: str = FieldUnset,
125
125
  template_id: str | None = FieldUnset,
126
126
  **extra_body: Any,
127
127
  ) -> PreparedRequest:
128
128
  request_dict: dict[str, Any] = {
129
- "filling_instructions": filling_instructions,
129
+ "instructions": instructions,
130
130
  }
131
131
 
132
132
  if document is not None:
@@ -598,7 +598,7 @@ class Documents(SyncAPIResource, BaseDocumentsMixin):
598
598
 
599
599
  def edit(
600
600
  self,
601
- filling_instructions: str,
601
+ instructions: str,
602
602
  document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
603
603
  model: str = FieldUnset,
604
604
  template_id: str | None = FieldUnset,
@@ -616,7 +616,7 @@ class Documents(SyncAPIResource, BaseDocumentsMixin):
616
616
  Either `document` OR `template_id` must be provided, but not both.
617
617
 
618
618
  Args:
619
- filling_instructions: Instructions describing how to fill the form fields.
619
+ instructions: Instructions describing how to fill the form fields.
620
620
  document: The document to edit. Can be a file path (Path or str), file-like object, MIMEData, PIL Image, or URL.
621
621
  Mutually exclusive with template_id.
622
622
  model: The LLM model to use for inference. Defaults to "retab-small".
@@ -632,7 +632,7 @@ class Documents(SyncAPIResource, BaseDocumentsMixin):
632
632
  HTTPException: If the request fails.
633
633
  """
634
634
  request = self._prepare_edit(
635
- filling_instructions=filling_instructions,
635
+ instructions=instructions,
636
636
  document=document,
637
637
  model=model,
638
638
  template_id=template_id,
@@ -993,7 +993,7 @@ class AsyncDocuments(AsyncAPIResource, BaseDocumentsMixin):
993
993
 
994
994
  async def edit(
995
995
  self,
996
- filling_instructions: str,
996
+ instructions: str,
997
997
  document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
998
998
  model: str = FieldUnset,
999
999
  template_id: str | None = FieldUnset,
@@ -1011,7 +1011,7 @@ class AsyncDocuments(AsyncAPIResource, BaseDocumentsMixin):
1011
1011
  Either `document` OR `template_id` must be provided, but not both.
1012
1012
 
1013
1013
  Args:
1014
- filling_instructions: Instructions describing how to fill the form fields.
1014
+ instructions: Instructions describing how to fill the form fields.
1015
1015
  document: The document to edit. Can be a file path (Path or str), file-like object, MIMEData, PIL Image, or URL.
1016
1016
  Mutually exclusive with template_id.
1017
1017
  model: The LLM model to use for inference. Defaults to "gemini-2.5-pro".
@@ -1027,7 +1027,7 @@ class AsyncDocuments(AsyncAPIResource, BaseDocumentsMixin):
1027
1027
  HTTPException: If the request fails.
1028
1028
  """
1029
1029
  request = self._prepare_edit(
1030
- filling_instructions=filling_instructions,
1030
+ instructions=instructions,
1031
1031
  document=document,
1032
1032
  model=model,
1033
1033
  template_id=template_id,
@@ -0,0 +1,3 @@
1
+ from .client import Edit, AsyncEdit
2
+
3
+ __all__ = ["Edit", "AsyncEdit"]
@@ -0,0 +1,176 @@
1
+ """
2
+ Edit SDK client - Wrapper for document editing functionality.
3
+ """
4
+
5
+ from io import IOBase
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import PIL.Image
10
+ from pydantic import HttpUrl
11
+
12
+ from ..._resource import AsyncAPIResource, SyncAPIResource
13
+ from ...utils.mime import prepare_mime_document
14
+ from ...types.documents.edit import (
15
+ EditRequest,
16
+ EditResponse,
17
+ )
18
+ from ...types.mime import MIMEData
19
+ from ...types.standards import PreparedRequest, FieldUnset
20
+ from .templates import Templates, AsyncTemplates
21
+
22
+
23
+ class BaseEditMixin:
24
+ """Shared methods for preparing edit API requests."""
25
+
26
+ def _prepare_fill_document(
27
+ self,
28
+ instructions: str,
29
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
30
+ model: str = FieldUnset,
31
+ template_id: str | None = FieldUnset,
32
+ **extra_body: Any,
33
+ ) -> PreparedRequest:
34
+ request_dict: dict[str, Any] = {
35
+ "instructions": instructions,
36
+ }
37
+
38
+ if document is not None:
39
+ mime_document = prepare_mime_document(document)
40
+ request_dict["document"] = mime_document
41
+
42
+ if model is not FieldUnset:
43
+ request_dict["model"] = model
44
+ if template_id is not FieldUnset:
45
+ request_dict["template_id"] = template_id
46
+
47
+ # Merge any extra fields provided by the caller
48
+ if extra_body:
49
+ request_dict.update(extra_body)
50
+
51
+ edit_request = EditRequest(**request_dict)
52
+ return PreparedRequest(
53
+ method="POST",
54
+ url="/v1/edit/fill-document",
55
+ data=edit_request.model_dump(mode="json", exclude_unset=True),
56
+ )
57
+
58
+
59
+ class Edit(SyncAPIResource, BaseEditMixin):
60
+ """Edit API wrapper for synchronous usage."""
61
+
62
+ def __init__(self, client: Any) -> None:
63
+ super().__init__(client=client)
64
+ self.templates = Templates(client=client)
65
+
66
+ def fill_document(
67
+ self,
68
+ instructions: str,
69
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
70
+ model: str = FieldUnset,
71
+ template_id: str | None = FieldUnset,
72
+ **extra_body: Any,
73
+ ) -> EditResponse:
74
+ """
75
+ Edit a document by inferring form fields and filling them with provided instructions.
76
+
77
+ This method performs:
78
+ 1. Detection to identify form field bounding boxes
79
+ 2. LLM inference to name and describe detected fields
80
+ 3. LLM-based form filling using the provided instructions
81
+ 4. Returns the filled document with form field values populated
82
+
83
+ Either `document` OR `template_id` must be provided, but not both.
84
+
85
+ Args:
86
+ instructions: Instructions describing how to fill the form fields.
87
+ document: The document to edit. Can be a file path (Path or str), file-like object,
88
+ MIMEData, PIL Image, or URL. Mutually exclusive with template_id.
89
+ model: The LLM model to use for inference. Defaults to "retab-small".
90
+ template_id: Template ID to use for filling. When provided, uses the template's
91
+ pre-defined form fields and empty PDF. Only works for PDF documents.
92
+ Mutually exclusive with document.
93
+
94
+ Returns:
95
+ EditResponse: Response containing:
96
+ - form_data: List of form fields with filled values
97
+ - filled_document: Document with filled form values (MIMEData)
98
+
99
+ Raises:
100
+ HTTPException: If the request fails.
101
+
102
+ Supported document formats:
103
+ - PDF: Native form field detection and filling
104
+ - DOCX/DOC: Native editing to preserve styles and formatting
105
+ - PPTX/PPT: Native editing for presentations
106
+ - XLSX/XLS: Native editing for spreadsheets
107
+ """
108
+ request = self._prepare_fill_document(
109
+ instructions=instructions,
110
+ document=document,
111
+ model=model,
112
+ template_id=template_id,
113
+ **extra_body,
114
+ )
115
+ response = self._client._prepared_request(request)
116
+ return EditResponse.model_validate(response)
117
+
118
+
119
+ class AsyncEdit(AsyncAPIResource, BaseEditMixin):
120
+ """Edit API wrapper for asynchronous usage."""
121
+
122
+ def __init__(self, client: Any) -> None:
123
+ super().__init__(client=client)
124
+ self.templates = AsyncTemplates(client=client)
125
+
126
+ async def fill_document(
127
+ self,
128
+ instructions: str,
129
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
130
+ model: str = FieldUnset,
131
+ template_id: str | None = FieldUnset,
132
+ **extra_body: Any,
133
+ ) -> EditResponse:
134
+ """
135
+ Edit a document by inferring form fields and filling them with provided instructions asynchronously.
136
+
137
+ This method performs:
138
+ 1. Detection to identify form field bounding boxes
139
+ 2. LLM inference to name and describe detected fields
140
+ 3. LLM-based form filling using the provided instructions
141
+ 4. Returns the filled document with form field values populated
142
+
143
+ Either `document` OR `template_id` must be provided, but not both.
144
+
145
+ Args:
146
+ instructions: Instructions describing how to fill the form fields.
147
+ document: The document to edit. Can be a file path (Path or str), file-like object,
148
+ MIMEData, PIL Image, or URL. Mutually exclusive with template_id.
149
+ model: The LLM model to use for inference. Defaults to "retab-small".
150
+ template_id: Template ID to use for filling. When provided, uses the template's
151
+ pre-defined form fields and empty PDF. Only works for PDF documents.
152
+ Mutually exclusive with document.
153
+
154
+ Returns:
155
+ EditResponse: Response containing:
156
+ - form_data: List of form fields with filled values
157
+ - filled_document: Document with filled form values (MIMEData)
158
+
159
+ Raises:
160
+ HTTPException: If the request fails.
161
+
162
+ Supported document formats:
163
+ - PDF: Native form field detection and filling
164
+ - DOCX/DOC: Native editing to preserve styles and formatting
165
+ - PPTX/PPT: Native editing for presentations
166
+ - XLSX/XLS: Native editing for spreadsheets
167
+ """
168
+ request = self._prepare_fill_document(
169
+ instructions=instructions,
170
+ document=document,
171
+ model=model,
172
+ template_id=template_id,
173
+ **extra_body,
174
+ )
175
+ response = await self._client._prepared_request(request)
176
+ return EditResponse.model_validate(response)
@@ -0,0 +1,4 @@
1
+ from .client import Templates, AsyncTemplates
2
+
3
+ __all__ = ["Templates", "AsyncTemplates"]
4
+
@@ -0,0 +1,620 @@
1
+ """
2
+ Edit Templates SDK client - Wrapper for edit template management.
3
+ """
4
+
5
+ from io import IOBase
6
+ from pathlib import Path
7
+ from typing import Any, Literal, List
8
+
9
+ import PIL.Image
10
+ from pydantic import HttpUrl
11
+
12
+ from ...._resource import AsyncAPIResource, SyncAPIResource
13
+ from ....utils.mime import prepare_mime_document
14
+ from ....types.documents.edit import (
15
+ FormField,
16
+ InferFormSchemaRequest,
17
+ InferFormSchemaResponse,
18
+ EditResponse,
19
+ )
20
+ from ....types.edit.templates import EditTemplate, FillTemplateRequest
21
+ from ....types.mime import MIMEData
22
+ from ....types.standards import PreparedRequest, FieldUnset
23
+ from ....types.pagination import PaginatedList
24
+
25
+
26
+ class BaseTemplatesMixin:
27
+ """Shared methods for preparing template API requests."""
28
+
29
+ def _prepare_list(
30
+ self,
31
+ before: str | None = None,
32
+ after: str | None = None,
33
+ limit: int = 10,
34
+ order: Literal["asc", "desc"] = "desc",
35
+ filename: str | None = None,
36
+ mime_type: str | None = None,
37
+ **extra_params: Any,
38
+ ) -> PreparedRequest:
39
+ params: dict[str, Any] = {
40
+ "limit": limit,
41
+ "order": order,
42
+ }
43
+ if before:
44
+ params["before"] = before
45
+ if after:
46
+ params["after"] = after
47
+ if filename:
48
+ params["filename"] = filename
49
+ if mime_type:
50
+ params["mime_type"] = mime_type
51
+ if extra_params:
52
+ params.update(extra_params)
53
+
54
+ return PreparedRequest(
55
+ method="GET",
56
+ url="/v1/edit/templates",
57
+ params=params,
58
+ )
59
+
60
+ def _prepare_get(self, template_id: str) -> PreparedRequest:
61
+ return PreparedRequest(
62
+ method="GET",
63
+ url=f"/v1/edit/templates/{template_id}",
64
+ )
65
+
66
+ def _prepare_create(
67
+ self,
68
+ name: str,
69
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl,
70
+ form_fields: list[FormField],
71
+ **extra_body: Any,
72
+ ) -> PreparedRequest:
73
+ mime_document = prepare_mime_document(document)
74
+
75
+ request_dict: dict[str, Any] = {
76
+ "name": name,
77
+ "document": mime_document,
78
+ "form_fields": [f.model_dump() if hasattr(f, 'model_dump') else f for f in form_fields],
79
+ }
80
+ if extra_body:
81
+ request_dict.update(extra_body)
82
+
83
+ return PreparedRequest(
84
+ method="POST",
85
+ url="/v1/edit/templates",
86
+ data=request_dict,
87
+ )
88
+
89
+ def _prepare_update(
90
+ self,
91
+ template_id: str,
92
+ name: str | None = None,
93
+ form_fields: list[FormField] | None = None,
94
+ **extra_body: Any,
95
+ ) -> PreparedRequest:
96
+ request_dict: dict[str, Any] = {}
97
+ if name is not None:
98
+ request_dict["name"] = name
99
+ if form_fields is not None:
100
+ request_dict["form_fields"] = [f.model_dump() if hasattr(f, 'model_dump') else f for f in form_fields]
101
+ if extra_body:
102
+ request_dict.update(extra_body)
103
+
104
+ return PreparedRequest(
105
+ method="PATCH",
106
+ url=f"/v1/edit/templates/{template_id}",
107
+ data=request_dict,
108
+ )
109
+
110
+ def _prepare_delete(self, template_id: str) -> PreparedRequest:
111
+ return PreparedRequest(
112
+ method="DELETE",
113
+ url=f"/v1/edit/templates/{template_id}",
114
+ )
115
+
116
+ def _prepare_duplicate(
117
+ self,
118
+ template_id: str,
119
+ name: str | None = None,
120
+ **extra_body: Any,
121
+ ) -> PreparedRequest:
122
+ request_dict: dict[str, Any] = {}
123
+ if name is not None:
124
+ request_dict["name"] = name
125
+ if extra_body:
126
+ request_dict.update(extra_body)
127
+
128
+ return PreparedRequest(
129
+ method="POST",
130
+ url=f"/v1/edit/templates/{template_id}/duplicate",
131
+ data=request_dict,
132
+ )
133
+
134
+ def _prepare_generate(
135
+ self,
136
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl,
137
+ model: str = FieldUnset,
138
+ instructions: str | None = FieldUnset,
139
+ **extra_body: Any,
140
+ ) -> PreparedRequest:
141
+ mime_document = prepare_mime_document(document)
142
+
143
+ request_dict: dict[str, Any] = {
144
+ "document": mime_document,
145
+ }
146
+
147
+ if model is not FieldUnset:
148
+ request_dict["model"] = model
149
+ if instructions is not FieldUnset:
150
+ request_dict["instructions"] = instructions
151
+ if extra_body:
152
+ request_dict.update(extra_body)
153
+
154
+ infer_request = InferFormSchemaRequest(**request_dict)
155
+ return PreparedRequest(
156
+ method="POST",
157
+ url="/v1/edit/templates/generate",
158
+ data=infer_request.model_dump(mode="json", exclude_unset=True),
159
+ )
160
+
161
+ def _prepare_fill(
162
+ self,
163
+ template_id: str,
164
+ instructions: str,
165
+ model: str = FieldUnset,
166
+ **extra_body: Any,
167
+ ) -> PreparedRequest:
168
+ request_dict: dict[str, Any] = {
169
+ "template_id": template_id,
170
+ "instructions": instructions,
171
+ }
172
+
173
+ if model is not FieldUnset:
174
+ request_dict["model"] = model
175
+ if extra_body:
176
+ request_dict.update(extra_body)
177
+
178
+ fill_request = FillTemplateRequest(**request_dict)
179
+ return PreparedRequest(
180
+ method="POST",
181
+ url="/v1/edit/templates/fill",
182
+ data=fill_request.model_dump(mode="json", exclude_unset=True),
183
+ )
184
+
185
+
186
+ class Templates(SyncAPIResource, BaseTemplatesMixin):
187
+ """Templates API wrapper for synchronous usage."""
188
+
189
+ def __init__(self, client: Any) -> None:
190
+ super().__init__(client=client)
191
+
192
+ def list(
193
+ self,
194
+ before: str | None = None,
195
+ after: str | None = None,
196
+ limit: int = 10,
197
+ order: Literal["asc", "desc"] = "desc",
198
+ filename: str | None = None,
199
+ mime_type: str | None = None,
200
+ **extra_params: Any,
201
+ ) -> PaginatedList:
202
+ """
203
+ List edit templates with pagination and optional filtering.
204
+
205
+ Args:
206
+ before: Cursor for backward pagination
207
+ after: Cursor for forward pagination
208
+ limit: Number of items per page (1-100, default 10)
209
+ order: Sort order ("asc" or "desc", default "desc")
210
+ filename: Filter by filename (partial match)
211
+ mime_type: Filter by MIME type
212
+
213
+ Returns:
214
+ PaginatedList: Paginated list of templates (data contains EditTemplate objects)
215
+ """
216
+ request = self._prepare_list(
217
+ before=before,
218
+ after=after,
219
+ limit=limit,
220
+ order=order,
221
+ filename=filename,
222
+ mime_type=mime_type,
223
+ **extra_params,
224
+ )
225
+ response = self._client._prepared_request(request)
226
+ return PaginatedList.model_validate(response)
227
+
228
+ def get(self, template_id: str) -> EditTemplate:
229
+ """
230
+ Get a specific edit template by ID.
231
+
232
+ Args:
233
+ template_id: The template ID to retrieve
234
+
235
+ Returns:
236
+ EditTemplate: The template details
237
+ """
238
+ request = self._prepare_get(template_id)
239
+ response = self._client._prepared_request(request)
240
+ return EditTemplate.model_validate(response)
241
+
242
+ def create(
243
+ self,
244
+ name: str,
245
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl,
246
+ form_fields: List[FormField],
247
+ **extra_body: Any,
248
+ ) -> EditTemplate:
249
+ """
250
+ Create a new edit template.
251
+
252
+ Args:
253
+ name: Name of the template
254
+ document: The PDF document to use as the template
255
+ form_fields: List of form fields in the template
256
+
257
+ Returns:
258
+ EditTemplate: The created template
259
+ """
260
+ request = self._prepare_create(
261
+ name=name,
262
+ document=document,
263
+ form_fields=form_fields,
264
+ **extra_body,
265
+ )
266
+ response = self._client._prepared_request(request)
267
+ return EditTemplate.model_validate(response)
268
+
269
+ def update(
270
+ self,
271
+ template_id: str,
272
+ name: str | None = None,
273
+ form_fields: List[FormField] | None = None,
274
+ **extra_body: Any,
275
+ ) -> EditTemplate:
276
+ """
277
+ Update an existing edit template.
278
+
279
+ Args:
280
+ template_id: The template ID to update
281
+ name: New name for the template (optional)
282
+ form_fields: New form fields (optional)
283
+
284
+ Returns:
285
+ EditTemplate: The updated template
286
+ """
287
+ request = self._prepare_update(
288
+ template_id=template_id,
289
+ name=name,
290
+ form_fields=form_fields,
291
+ **extra_body,
292
+ )
293
+ response = self._client._prepared_request(request)
294
+ return EditTemplate.model_validate(response)
295
+
296
+ def delete(self, template_id: str) -> None:
297
+ """
298
+ Delete an edit template.
299
+
300
+ Args:
301
+ template_id: The template ID to delete
302
+ """
303
+ request = self._prepare_delete(template_id)
304
+ self._client._prepared_request(request)
305
+
306
+ def duplicate(
307
+ self,
308
+ template_id: str,
309
+ name: str | None = None,
310
+ **extra_body: Any,
311
+ ) -> EditTemplate:
312
+ """
313
+ Duplicate an existing edit template.
314
+
315
+ Args:
316
+ template_id: The template ID to duplicate
317
+ name: Name for the duplicated template (defaults to "<original> (copy)")
318
+
319
+ Returns:
320
+ EditTemplate: The duplicated template
321
+ """
322
+ request = self._prepare_duplicate(
323
+ template_id=template_id,
324
+ name=name,
325
+ **extra_body,
326
+ )
327
+ response = self._client._prepared_request(request)
328
+ return EditTemplate.model_validate(response)
329
+
330
+ def generate(
331
+ self,
332
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl,
333
+ model: str = FieldUnset,
334
+ instructions: str | None = FieldUnset,
335
+ **extra_body: Any,
336
+ ) -> InferFormSchemaResponse:
337
+ """
338
+ Infer form schema from a PDF document.
339
+
340
+ This method combines computer vision for precise bounding box detection
341
+ with LLM for semantic field naming (key, description) and type classification.
342
+
343
+ Args:
344
+ document: The PDF document to analyze
345
+ model: The LLM model to use for field naming (default: "retab-small")
346
+ instructions: Optional instructions to guide form field detection
347
+
348
+ Returns:
349
+ InferFormSchemaResponse: Response containing:
350
+ - form_schema: The detected form schema
351
+ - annotated_pdf: PDF with bounding boxes for visual verification
352
+ - detection_count: Number of fields detected
353
+
354
+ Note:
355
+ Only PDF documents are supported for form schema inference.
356
+ """
357
+ request = self._prepare_generate(
358
+ document=document,
359
+ model=model,
360
+ instructions=instructions,
361
+ **extra_body,
362
+ )
363
+ response = self._client._prepared_request(request)
364
+ return InferFormSchemaResponse.model_validate(response)
365
+
366
+ def fill(
367
+ self,
368
+ template_id: str,
369
+ instructions: str,
370
+ model: str = FieldUnset,
371
+ **extra_body: Any,
372
+ ) -> EditResponse:
373
+ """
374
+ Fill a PDF form using a pre-defined template.
375
+
376
+ This method uses a template's pre-defined form fields to fill a PDF form,
377
+ skipping the field detection step for faster processing.
378
+
379
+ Args:
380
+ template_id: The template ID to use for filling
381
+ instructions: Instructions describing how to fill the form fields
382
+ model: The LLM model to use for inference (default: "retab-small")
383
+
384
+ Returns:
385
+ EditResponse: Response containing:
386
+ - form_data: List of form fields with filled values
387
+ - filled_document: The filled PDF document as MIMEData
388
+
389
+ Use cases:
390
+ - Batch processing of the same form with different data
391
+ - Faster form filling when field detection is already done
392
+ - Consistent field mapping across multiple fills
393
+ """
394
+ request = self._prepare_fill(
395
+ template_id=template_id,
396
+ instructions=instructions,
397
+ model=model,
398
+ **extra_body,
399
+ )
400
+ response = self._client._prepared_request(request)
401
+ return EditResponse.model_validate(response)
402
+
403
+
404
+ class AsyncTemplates(AsyncAPIResource, BaseTemplatesMixin):
405
+ """Templates API wrapper for asynchronous usage."""
406
+
407
+ def __init__(self, client: Any) -> None:
408
+ super().__init__(client=client)
409
+
410
+ async def list(
411
+ self,
412
+ before: str | None = None,
413
+ after: str | None = None,
414
+ limit: int = 10,
415
+ order: Literal["asc", "desc"] = "desc",
416
+ filename: str | None = None,
417
+ mime_type: str | None = None,
418
+ **extra_params: Any,
419
+ ) -> PaginatedList:
420
+ """
421
+ List edit templates with pagination and optional filtering.
422
+
423
+ Args:
424
+ before: Cursor for backward pagination
425
+ after: Cursor for forward pagination
426
+ limit: Number of items per page (1-100, default 10)
427
+ order: Sort order ("asc" or "desc", default "desc")
428
+ filename: Filter by filename (partial match)
429
+ mime_type: Filter by MIME type
430
+
431
+ Returns:
432
+ PaginatedList: Paginated list of templates (data contains EditTemplate objects)
433
+ """
434
+ request = self._prepare_list(
435
+ before=before,
436
+ after=after,
437
+ limit=limit,
438
+ order=order,
439
+ filename=filename,
440
+ mime_type=mime_type,
441
+ **extra_params,
442
+ )
443
+ response = await self._client._prepared_request(request)
444
+ return PaginatedList.model_validate(response)
445
+
446
+ async def get(self, template_id: str) -> EditTemplate:
447
+ """
448
+ Get a specific edit template by ID.
449
+
450
+ Args:
451
+ template_id: The template ID to retrieve
452
+
453
+ Returns:
454
+ EditTemplate: The template details
455
+ """
456
+ request = self._prepare_get(template_id)
457
+ response = await self._client._prepared_request(request)
458
+ return EditTemplate.model_validate(response)
459
+
460
+ async def create(
461
+ self,
462
+ name: str,
463
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl,
464
+ form_fields: List[FormField],
465
+ **extra_body: Any,
466
+ ) -> EditTemplate:
467
+ """
468
+ Create a new edit template.
469
+
470
+ Args:
471
+ name: Name of the template
472
+ document: The PDF document to use as the template
473
+ form_fields: List of form fields in the template
474
+
475
+ Returns:
476
+ EditTemplate: The created template
477
+ """
478
+ request = self._prepare_create(
479
+ name=name,
480
+ document=document,
481
+ form_fields=form_fields,
482
+ **extra_body,
483
+ )
484
+ response = await self._client._prepared_request(request)
485
+ return EditTemplate.model_validate(response)
486
+
487
+ async def update(
488
+ self,
489
+ template_id: str,
490
+ name: str | None = None,
491
+ form_fields: List[FormField] | None = None,
492
+ **extra_body: Any,
493
+ ) -> EditTemplate:
494
+ """
495
+ Update an existing edit template.
496
+
497
+ Args:
498
+ template_id: The template ID to update
499
+ name: New name for the template (optional)
500
+ form_fields: New form fields (optional)
501
+
502
+ Returns:
503
+ EditTemplate: The updated template
504
+ """
505
+ request = self._prepare_update(
506
+ template_id=template_id,
507
+ name=name,
508
+ form_fields=form_fields,
509
+ **extra_body,
510
+ )
511
+ response = await self._client._prepared_request(request)
512
+ return EditTemplate.model_validate(response)
513
+
514
+ async def delete(self, template_id: str) -> None:
515
+ """
516
+ Delete an edit template.
517
+
518
+ Args:
519
+ template_id: The template ID to delete
520
+ """
521
+ request = self._prepare_delete(template_id)
522
+ await self._client._prepared_request(request)
523
+
524
+ async def duplicate(
525
+ self,
526
+ template_id: str,
527
+ name: str | None = None,
528
+ **extra_body: Any,
529
+ ) -> EditTemplate:
530
+ """
531
+ Duplicate an existing edit template.
532
+
533
+ Args:
534
+ template_id: The template ID to duplicate
535
+ name: Name for the duplicated template (defaults to "<original> (copy)")
536
+
537
+ Returns:
538
+ EditTemplate: The duplicated template
539
+ """
540
+ request = self._prepare_duplicate(
541
+ template_id=template_id,
542
+ name=name,
543
+ **extra_body,
544
+ )
545
+ response = await self._client._prepared_request(request)
546
+ return EditTemplate.model_validate(response)
547
+
548
+ async def generate(
549
+ self,
550
+ document: Path | str | IOBase | MIMEData | PIL.Image.Image | HttpUrl,
551
+ model: str = FieldUnset,
552
+ instructions: str | None = FieldUnset,
553
+ **extra_body: Any,
554
+ ) -> InferFormSchemaResponse:
555
+ """
556
+ Infer form schema from a PDF document asynchronously.
557
+
558
+ This method combines computer vision for precise bounding box detection
559
+ with LLM for semantic field naming (key, description) and type classification.
560
+
561
+ Args:
562
+ document: The PDF document to analyze
563
+ model: The LLM model to use for field naming (default: "retab-small")
564
+ instructions: Optional instructions to guide form field detection
565
+
566
+ Returns:
567
+ InferFormSchemaResponse: Response containing:
568
+ - form_schema: The detected form schema
569
+ - annotated_pdf: PDF with bounding boxes for visual verification
570
+ - detection_count: Number of fields detected
571
+
572
+ Note:
573
+ Only PDF documents are supported for form schema inference.
574
+ """
575
+ request = self._prepare_generate(
576
+ document=document,
577
+ model=model,
578
+ instructions=instructions,
579
+ **extra_body,
580
+ )
581
+ response = await self._client._prepared_request(request)
582
+ return InferFormSchemaResponse.model_validate(response)
583
+
584
+ async def fill(
585
+ self,
586
+ template_id: str,
587
+ instructions: str,
588
+ model: str = FieldUnset,
589
+ **extra_body: Any,
590
+ ) -> EditResponse:
591
+ """
592
+ Fill a PDF form using a pre-defined template asynchronously.
593
+
594
+ This method uses a template's pre-defined form fields to fill a PDF form,
595
+ skipping the field detection step for faster processing.
596
+
597
+ Args:
598
+ template_id: The template ID to use for filling
599
+ instructions: Instructions describing how to fill the form fields
600
+ model: The LLM model to use for inference (default: "retab-small")
601
+
602
+ Returns:
603
+ EditResponse: Response containing:
604
+ - form_data: List of form fields with filled values
605
+ - filled_document: The filled PDF document as MIMEData
606
+
607
+ Use cases:
608
+ - Batch processing of the same form with different data
609
+ - Faster form filling when field detection is already done
610
+ - Consistent field mapping across multiple fills
611
+ """
612
+ request = self._prepare_fill(
613
+ template_id=template_id,
614
+ instructions=instructions,
615
+ model=model,
616
+ **extra_body,
617
+ )
618
+ response = await self._client._prepared_request(request)
619
+ return EditResponse.model_validate(response)
620
+
@@ -64,7 +64,7 @@ class BaseFormField(BaseModel):
64
64
  ...,
65
65
  description="Key of the field. This is used to identify the field in the form data.",
66
66
  )
67
-
67
+
68
68
 
69
69
  class FormField(BaseFormField):
70
70
  """Single field in the form schema (text input, checkbox, etc.)."""
@@ -72,8 +72,8 @@ class FormField(BaseFormField):
72
72
  value: Optional[str] = Field(
73
73
  None,
74
74
  description=(
75
- "Current or default value of the field as text. "
76
- "May be null/None if no value is set."
75
+ "Filled value of the field as text. "
76
+ "May be null/None if no filled value is set."
77
77
  ),
78
78
  )
79
79
 
@@ -88,18 +88,6 @@ class FormSchema(BaseModel):
88
88
  ),
89
89
  )
90
90
 
91
- class FilledFormField(BaseFormField):
92
- """Single field in the form schema (text input, checkbox, etc.)."""
93
-
94
- value: Optional[str] = Field(
95
- None,
96
- description=(
97
- "Filled value of the field as text. "
98
- "May be null/None if no filled value is set."
99
- ),
100
- )
101
-
102
-
103
91
  class OCRTextElement(BaseModel):
104
92
  """A single OCR-detected text element with its bounding box."""
105
93
 
@@ -124,14 +112,6 @@ class InferFormSchemaRequest(BaseModel):
124
112
  instructions: Optional[str] = Field(default=None, description="Optional instructions to guide form field detection (e.g., which fields to focus on, specific areas to look for)")
125
113
 
126
114
 
127
- class InferFormSchemaResponse(BaseModel):
128
- """Response containing the inferred form schema."""
129
-
130
- form_schema: FormSchema = Field(..., description="The inferred form schema")
131
- ocr_result: OCRResult = Field(..., description="The OCR results used for inference")
132
- form_fields_pdf: MIMEData = Field(..., description="PDF with form field bounding boxes")
133
-
134
-
135
115
  class EditRequest(BaseModel):
136
116
  """Request for the infer_and_fill_schema endpoint.
137
117
 
@@ -141,13 +121,13 @@ class EditRequest(BaseModel):
141
121
  """
142
122
  document: Optional[MIMEData] = Field(default=None, description="Input document (PDF or DOCX). DOCX files will be converted to PDF. Mutually exclusive with template_id.")
143
123
  model: str = Field(default="retab-small", description="LLM model to use for inference")
144
- filling_instructions: str = Field(..., description="Instructions to fill the form")
124
+ instructions: str = Field(..., description="Instructions to fill the form")
145
125
  template_id: Optional[str] = Field(default=None, description="Template ID to use for filling. When provided, uses the template's pre-defined form fields and empty PDF. Only works for PDF documents. Mutually exclusive with document.")
146
126
 
147
127
  class EditResponse(BaseModel):
148
128
  """Response from the fill_form endpoint.
149
129
  """
150
- form_data: list[FilledFormField] = Field(
130
+ form_data: list[FormField] = Field(
151
131
  ...,
152
132
  description=(
153
133
  "List of form fields (with positions, descriptions, and metadata) "
@@ -162,3 +142,11 @@ class ProcessOCRRequest(BaseModel):
162
142
 
163
143
  document: MIMEData = Field(..., description="Input document (PDF)")
164
144
 
145
+
146
+ class InferFormSchemaResponse(BaseModel):
147
+ """Response from form schema inference."""
148
+
149
+ form_schema: FormSchema = Field(..., description="Form schema with detected bounding boxes and field names")
150
+ annotated_pdf: MIMEData = Field(..., description="PDF with form field bounding boxes")
151
+ field_count: int = Field(..., description="Number of fields detected")
152
+
@@ -0,0 +1,16 @@
1
+ from .templates import (
2
+ EditTemplate,
3
+ CreateEditTemplateRequest,
4
+ UpdateEditTemplateRequest,
5
+ DuplicateEditTemplateRequest,
6
+ FillTemplateRequest,
7
+ )
8
+
9
+ __all__ = [
10
+ "EditTemplate",
11
+ "CreateEditTemplateRequest",
12
+ "UpdateEditTemplateRequest",
13
+ "DuplicateEditTemplateRequest",
14
+ "FillTemplateRequest",
15
+ ]
16
+
@@ -0,0 +1,50 @@
1
+ """Types for edit templates."""
2
+
3
+ from typing import Optional
4
+ from pydantic import BaseModel, Field
5
+ import datetime
6
+
7
+ from ..mime import BaseMIMEData, MIMEData
8
+ from ..documents.edit import FormField
9
+
10
+
11
+ class EditTemplate(BaseModel):
12
+ """An edit template with pre-defined form fields."""
13
+
14
+ id: str = Field(..., description="Unique identifier of the template")
15
+ name: str = Field(..., description="Name of the template")
16
+ file: BaseMIMEData = Field(..., description="File information for the empty PDF template")
17
+ form_fields: list[FormField] = Field(..., description="List of form fields in the template")
18
+ organization_id: Optional[str] = Field(default=None, description="Organization that owns this template")
19
+ created_at: datetime.datetime = Field(..., description="Timestamp of creation")
20
+ updated_at: datetime.datetime = Field(..., description="Timestamp of last update")
21
+
22
+
23
+ class CreateEditTemplateRequest(BaseModel):
24
+ """Request model for creating an edit template."""
25
+
26
+ name: str = Field(..., description="Name of the template")
27
+ document: MIMEData = Field(..., description="The PDF document to use as the template")
28
+ form_fields: list[FormField] = Field(..., description="List of form fields in the template")
29
+
30
+
31
+ class UpdateEditTemplateRequest(BaseModel):
32
+ """Request model for updating an edit template."""
33
+
34
+ name: Optional[str] = Field(default=None, description="Name of the template")
35
+ form_fields: Optional[list[FormField]] = Field(default=None, description="List of form fields")
36
+
37
+
38
+ class DuplicateEditTemplateRequest(BaseModel):
39
+ """Request model for duplicating an edit template."""
40
+
41
+ name: Optional[str] = Field(default=None, description="Name for the duplicated template")
42
+
43
+
44
+ class FillTemplateRequest(BaseModel):
45
+ """Request for the fill endpoint.
46
+ Uses pre-defined form fields from the template (PDF only)
47
+ """
48
+ model: str = Field(default="retab-small", description="LLM model to use for inference")
49
+ instructions: str = Field(..., description="Instructions to fill the form")
50
+ template_id: str = Field(..., description="Template ID to use for filling. When provided, uses the template's pre-defined form fields and empty PDF. Only works for PDF documents. Mutually exclusive with document.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: retab
3
- Version: 0.0.81
3
+ Version: 0.0.83
4
4
  Summary: Retab official python library
5
5
  Home-page: https://github.com/retab-dev/retab
6
6
  Author: Retab
@@ -16,6 +16,10 @@ retab/resources/models.py
16
16
  retab/resources/schemas.py
17
17
  retab/resources/documents/__init__.py
18
18
  retab/resources/documents/client.py
19
+ retab/resources/edit/__init__.py
20
+ retab/resources/edit/client.py
21
+ retab/resources/edit/templates/__init__.py
22
+ retab/resources/edit/templates/client.py
19
23
  retab/resources/extractions/__init__.py
20
24
  retab/resources/extractions/client.py
21
25
  retab/resources/projects/__init__.py
@@ -37,6 +41,8 @@ retab/types/documents/edit.py
37
41
  retab/types/documents/extract.py
38
42
  retab/types/documents/parse.py
39
43
  retab/types/documents/split.py
44
+ retab/types/edit/__init__.py
45
+ retab/types/edit/templates.py
40
46
  retab/types/extractions/__init__.py
41
47
  retab/types/extractions/types.py
42
48
  retab/types/projects/__init__.py
@@ -6,7 +6,7 @@ with open("requirements.txt") as f:
6
6
 
7
7
  setup(
8
8
  name="retab",
9
- version="0.0.81",
9
+ version="0.0.83",
10
10
  author="Retab",
11
11
  author_email="contact@retab.com",
12
12
  description="Retab official python library",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes