retab 0.0.42__py3-none-any.whl → 0.0.44__py3-none-any.whl

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 (76) hide show
  1. retab/__init__.py +2 -1
  2. retab/client.py +26 -51
  3. retab/generate_types.py +180 -0
  4. retab/resources/consensus/client.py +1 -1
  5. retab/resources/consensus/responses.py +1 -1
  6. retab/resources/deployments/__init__.py +3 -0
  7. retab/resources/deployments/automations/__init__.py +9 -0
  8. retab/resources/deployments/automations/client.py +244 -0
  9. retab/resources/deployments/automations/endpoints.py +290 -0
  10. retab/resources/deployments/automations/links.py +303 -0
  11. retab/resources/deployments/automations/logs.py +222 -0
  12. retab/resources/deployments/automations/mailboxes.py +423 -0
  13. retab/resources/deployments/automations/outlook.py +377 -0
  14. retab/resources/deployments/automations/tests.py +161 -0
  15. retab/resources/deployments/client.py +148 -0
  16. retab/resources/documents/client.py +94 -68
  17. retab/resources/documents/extractions.py +55 -46
  18. retab/resources/evaluations/__init__.py +2 -2
  19. retab/resources/evaluations/client.py +61 -77
  20. retab/resources/evaluations/documents.py +48 -37
  21. retab/resources/evaluations/iterations.py +58 -40
  22. retab/resources/jsonlUtils.py +3 -4
  23. retab/resources/processors/automations/endpoints.py +49 -39
  24. retab/resources/processors/automations/links.py +52 -43
  25. retab/resources/processors/automations/mailboxes.py +74 -59
  26. retab/resources/processors/automations/outlook.py +104 -82
  27. retab/resources/processors/client.py +35 -30
  28. retab/resources/projects/__init__.py +3 -0
  29. retab/resources/projects/client.py +285 -0
  30. retab/resources/projects/documents.py +244 -0
  31. retab/resources/projects/iterations.py +470 -0
  32. retab/resources/usage.py +2 -0
  33. retab/types/ai_models.py +2 -1
  34. retab/types/deprecated_evals.py +195 -0
  35. retab/types/evaluations/__init__.py +5 -2
  36. retab/types/evaluations/iterations.py +9 -43
  37. retab/types/evaluations/model.py +19 -24
  38. retab/types/extractions.py +1 -0
  39. retab/types/jobs/base.py +1 -1
  40. retab/types/jobs/evaluation.py +1 -1
  41. retab/types/logs.py +5 -6
  42. retab/types/mime.py +1 -10
  43. retab/types/projects/__init__.py +34 -0
  44. retab/types/projects/documents.py +30 -0
  45. retab/types/projects/iterations.py +78 -0
  46. retab/types/projects/model.py +68 -0
  47. retab/types/schemas/enhance.py +22 -5
  48. retab/types/schemas/evaluate.py +2 -2
  49. retab/types/schemas/object.py +27 -25
  50. retab/types/standards.py +2 -2
  51. retab/utils/__init__.py +3 -0
  52. retab/utils/ai_models.py +127 -12
  53. retab/utils/hashing.py +24 -0
  54. retab/utils/json_schema.py +1 -26
  55. retab/utils/mime.py +0 -17
  56. retab/utils/usage/usage.py +0 -1
  57. {retab-0.0.42.dist-info → retab-0.0.44.dist-info}/METADATA +4 -6
  58. {retab-0.0.42.dist-info → retab-0.0.44.dist-info}/RECORD +60 -55
  59. retab/_utils/__init__.py +0 -0
  60. retab/_utils/_model_cards/anthropic.yaml +0 -59
  61. retab/_utils/_model_cards/auto.yaml +0 -43
  62. retab/_utils/_model_cards/gemini.yaml +0 -117
  63. retab/_utils/_model_cards/openai.yaml +0 -301
  64. retab/_utils/_model_cards/xai.yaml +0 -28
  65. retab/_utils/ai_models.py +0 -138
  66. retab/_utils/benchmarking.py +0 -484
  67. retab/_utils/chat.py +0 -327
  68. retab/_utils/display.py +0 -440
  69. retab/_utils/json_schema.py +0 -2156
  70. retab/_utils/mime.py +0 -165
  71. retab/_utils/responses.py +0 -169
  72. retab/_utils/stream_context_managers.py +0 -52
  73. retab/_utils/usage/__init__.py +0 -0
  74. retab/_utils/usage/usage.py +0 -301
  75. {retab-0.0.42.dist-info → retab-0.0.44.dist-info}/WHEEL +0 -0
  76. {retab-0.0.42.dist-info → retab-0.0.44.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,222 @@
1
+ from typing import Literal, Optional
2
+
3
+ from ...._resource import AsyncAPIResource, SyncAPIResource
4
+ from ....types.logs import AutomationLog, ExternalRequestLog, ListLogs
5
+ from ....types.standards import PreparedRequest
6
+
7
+
8
+ class LogsMixin:
9
+ def prepare_get(self, processor_id: str, log_id: str) -> PreparedRequest:
10
+ """Get a specific automation log by ID.
11
+
12
+ Args:
13
+ processor_id: ID of the processor
14
+ log_id: ID of the log to retrieve
15
+
16
+ Returns:
17
+ PreparedRequest: The prepared request
18
+ """
19
+ return PreparedRequest(method="GET", url=f"/v1/processors/{processor_id}/automations/logs/{log_id}")
20
+
21
+ def prepare_list(
22
+ self,
23
+ processor_id: str,
24
+ before: Optional[str] = None,
25
+ after: Optional[str] = None,
26
+ limit: Optional[int] = 10,
27
+ order: Optional[Literal["asc", "desc"]] = "desc",
28
+ # Filtering parameters
29
+ status_code: Optional[int] = None,
30
+ status_class: Optional[Literal["2xx", "3xx", "4xx", "5xx"]] = None,
31
+ automation_id: Optional[str] = None,
32
+ webhook_url: Optional[str] = None,
33
+ schema_id: Optional[str] = None,
34
+ schema_data_id: Optional[str] = None,
35
+ ) -> PreparedRequest:
36
+ """List automation logs with pagination support.
37
+
38
+ Args:
39
+ processor_id: ID of the processor
40
+ before: Optional cursor for pagination before a specific log ID
41
+ after: Optional cursor for pagination after a specific log ID
42
+ limit: Optional limit on number of results (max 100)
43
+ order: Optional sort order ("asc" or "desc")
44
+ status_code: Optional filter by status code
45
+ status_class: Optional filter by status_class
46
+ automation_id: Optional filter by deployment ID
47
+ webhook_url: Optional filter by webhook URL
48
+ schema_id: Optional filter by schema ID
49
+ schema_data_id: Optional filter by schema data ID
50
+
51
+ Returns:
52
+ PreparedRequest: The prepared request
53
+ """
54
+ params = {
55
+ "before": before,
56
+ "after": after,
57
+ "limit": limit,
58
+ "order": order,
59
+ "status_code": status_code,
60
+ "status_class": status_class,
61
+ "automation_id": automation_id,
62
+ "webhook_url": webhook_url,
63
+ "schema_id": schema_id,
64
+ "schema_data_id": schema_data_id,
65
+ }
66
+ # Remove None values
67
+ params = {k: v for k, v in params.items() if v is not None}
68
+
69
+ return PreparedRequest(method="GET", url=f"/v1/processors/{processor_id}/automations/logs", params=params)
70
+
71
+ def prepare_rerun(self, processor_id: str, log_id: str) -> PreparedRequest:
72
+ """Rerun a webhook from an existing AutomationLog.
73
+
74
+ Args:
75
+ processor_id: ID of the processor
76
+ log_id: ID of the log to rerun
77
+
78
+ Returns:
79
+ PreparedRequest: The prepared request
80
+ """
81
+ return PreparedRequest(method="POST", url=f"/v1/processors/{processor_id}/automations/logs/{log_id}/rerun")
82
+
83
+
84
+ class Logs(SyncAPIResource, LogsMixin):
85
+ """Logs API wrapper for managing automation logs"""
86
+
87
+ def get(self, processor_id: str, log_id: str) -> AutomationLog:
88
+ """Get a specific automation log by ID.
89
+
90
+ Args:
91
+ processor_id: ID of the processor
92
+ log_id: ID of the log to retrieve
93
+
94
+ Returns:
95
+ AutomationLog: The automation log
96
+ """
97
+ request = self.prepare_get(processor_id, log_id)
98
+ response = self._client._prepared_request(request)
99
+ return AutomationLog.model_validate(response)
100
+
101
+ def list(
102
+ self,
103
+ processor_id: str,
104
+ before: Optional[str] = None,
105
+ after: Optional[str] = None,
106
+ limit: Optional[int] = 10,
107
+ order: Optional[Literal["asc", "desc"]] = "desc",
108
+ status_code: Optional[int] = None,
109
+ status_class: Optional[Literal["2xx", "3xx", "4xx", "5xx"]] = None,
110
+ automation_id: Optional[str] = None,
111
+ webhook_url: Optional[str] = None,
112
+ schema_id: Optional[str] = None,
113
+ schema_data_id: Optional[str] = None,
114
+ ) -> ListLogs:
115
+ """List automation logs with pagination support.
116
+
117
+ Args:
118
+ processor_id: ID of the processor
119
+ before: Optional cursor for pagination before a specific log ID
120
+ after: Optional cursor for pagination after a specific log ID
121
+ limit: Optional limit on number of results (max 100)
122
+ order: Optional sort order ("asc" or "desc")
123
+ status_code: Optional filter by status code
124
+ status_class: Optional filter by status_class
125
+ automation_id: Optional filter by deployment ID
126
+ webhook_url: Optional filter by webhook URL
127
+ schema_id: Optional filter by schema ID
128
+ schema_data_id: Optional filter by schema data ID
129
+
130
+ Returns:
131
+ ListLogs: Paginated list of automation logs with metadata
132
+ """
133
+ request = self.prepare_list(processor_id, before, after, limit, order, status_code, status_class, automation_id, webhook_url, schema_id, schema_data_id)
134
+ response = self._client._prepared_request(request)
135
+ return ListLogs.model_validate(response)
136
+
137
+ def rerun(self, processor_id: str, log_id: str) -> ExternalRequestLog:
138
+ """Rerun a webhook from an existing AutomationLog.
139
+
140
+ Args:
141
+ processor_id: ID of the processor
142
+ log_id: ID of the log to rerun
143
+
144
+ Returns:
145
+ ExternalRequestLog: The result of the rerun webhook call
146
+ """
147
+ request = self.prepare_rerun(processor_id, log_id)
148
+ response = self._client._prepared_request(request)
149
+
150
+ print(f"Webhook call run successfully. Log available at https://www.retab.com/dashboard/processors/{processor_id}/logs/{log_id}")
151
+
152
+ return ExternalRequestLog.model_validate(response)
153
+
154
+
155
+ class AsyncLogs(AsyncAPIResource, LogsMixin):
156
+ """Async Logs API wrapper for managing automation logs"""
157
+
158
+ async def get(self, processor_id: str, log_id: str) -> AutomationLog:
159
+ """Get a specific automation log by ID.
160
+
161
+ Args:
162
+ processor_id: ID of the processor
163
+ log_id: ID of the log to retrieve
164
+
165
+ Returns:
166
+ AutomationLog: The automation log
167
+ """
168
+ request = self.prepare_get(processor_id, log_id)
169
+ response = await self._client._prepared_request(request)
170
+ return AutomationLog.model_validate(response)
171
+
172
+ async def list(
173
+ self,
174
+ processor_id: str,
175
+ before: Optional[str] = None,
176
+ after: Optional[str] = None,
177
+ limit: Optional[int] = 10,
178
+ order: Optional[Literal["asc", "desc"]] = "desc",
179
+ status_code: Optional[int] = None,
180
+ status_class: Optional[Literal["2xx", "3xx", "4xx", "5xx"]] = None,
181
+ automation_id: Optional[str] = None,
182
+ webhook_url: Optional[str] = None,
183
+ schema_id: Optional[str] = None,
184
+ schema_data_id: Optional[str] = None,
185
+ ) -> ListLogs:
186
+ """List automation logs with pagination support.
187
+
188
+ Args:
189
+ before: Optional cursor for pagination before a specific log ID
190
+ after: Optional cursor for pagination after a specific log ID
191
+ limit: Optional limit on number of results (max 100)
192
+ order: Optional sort order ("asc" or "desc")
193
+ status_code: Optional filter by status code
194
+ status_class: Optional filter by status_class
195
+ automation_id: Optional filter by deployment ID
196
+ webhook_url: Optional filter by webhook URL
197
+ schema_id: Optional filter by schema ID
198
+ schema_data_id: Optional filter by schema data ID
199
+
200
+ Returns:
201
+ ListLogs: Paginated list of automation logs with metadata
202
+ """
203
+ request = self.prepare_list(processor_id, before, after, limit, order, status_code, status_class, automation_id, webhook_url, schema_id, schema_data_id)
204
+ response = await self._client._prepared_request(request)
205
+ return ListLogs.model_validate(response)
206
+
207
+ async def rerun(self, processor_id: str, log_id: str) -> ExternalRequestLog:
208
+ """Rerun a webhook from an existing AutomationLog.
209
+
210
+ Args:
211
+ processor_id: ID of the processor
212
+ log_id: ID of the log to rerun
213
+
214
+ Returns:
215
+ ExternalRequestLog: The result of the rerun webhook call
216
+ """
217
+ request = self.prepare_rerun(processor_id, log_id)
218
+ response = await self._client._prepared_request(request)
219
+
220
+ print(f"Webhook call run successfully. Log available at https://www.retab.com/dashboard/processors/{processor_id}/logs/{log_id}")
221
+
222
+ return ExternalRequestLog.model_validate(response)
@@ -0,0 +1,423 @@
1
+ from io import IOBase
2
+ from pathlib import Path
3
+ from typing import Any, List, Literal, Optional
4
+
5
+ from pydantic import EmailStr, HttpUrl
6
+ from ....types.standards import FieldUnset
7
+
8
+ from ...._resource import AsyncAPIResource, SyncAPIResource
9
+ from ....utils.mime import prepare_mime_document
10
+ from ....types.automations.mailboxes import ListMailboxes, Mailbox, UpdateMailboxRequest
11
+ from ....types.mime import EmailData, MIMEData
12
+ from ....types.standards import PreparedRequest
13
+
14
+
15
+ class MailBoxesMixin:
16
+ mailboxes_base_url: str = "/v1/processors/automations/mailboxes"
17
+
18
+ def prepare_create(
19
+ self,
20
+ email: str,
21
+ name: str,
22
+ processor_id: str,
23
+ webhook_url: str,
24
+ authorized_domains: list[str] = FieldUnset,
25
+ authorized_emails: list[EmailStr] = FieldUnset,
26
+ webhook_headers: dict[str, str] = FieldUnset,
27
+ default_language: str = FieldUnset,
28
+ need_validation: bool = FieldUnset,
29
+ ) -> PreparedRequest:
30
+ """Create a new email automation configuration."""
31
+ mailbox_dict: dict[str, Any] = {
32
+ 'name': name,
33
+ 'processor_id': processor_id,
34
+ 'webhook_url': webhook_url,
35
+ 'email': email,
36
+ }
37
+ if default_language is not FieldUnset:
38
+ mailbox_dict['default_language'] = default_language
39
+ if webhook_headers is not FieldUnset:
40
+ mailbox_dict['webhook_headers'] = webhook_headers
41
+ if need_validation is not FieldUnset:
42
+ mailbox_dict['need_validation'] = need_validation
43
+ if authorized_domains is not FieldUnset:
44
+ mailbox_dict['authorized_domains'] = authorized_domains
45
+ if authorized_emails is not FieldUnset:
46
+ mailbox_dict['authorized_emails'] = authorized_emails
47
+
48
+ mailbox = Mailbox(**mailbox_dict)
49
+ return PreparedRequest(method="POST", url=self.mailboxes_base_url, data=mailbox.model_dump(mode="json", exclude_unset=True))
50
+
51
+ def prepare_list(
52
+ self,
53
+ processor_id: str,
54
+ before: str | None = None,
55
+ after: str | None = None,
56
+ limit: int = 10,
57
+ order: Literal["asc", "desc"] | None = "desc",
58
+ email: Optional[str] = None,
59
+ name: Optional[str] = None,
60
+ webhook_url: Optional[str] = None,
61
+ ) -> PreparedRequest:
62
+ params = {
63
+ "processor_id": processor_id,
64
+ "before": before,
65
+ "after": after,
66
+ "limit": limit,
67
+ "order": order,
68
+ "email": email,
69
+ "name": name,
70
+ "webhook_url": webhook_url,
71
+ }
72
+ # Remove None values
73
+ params = {k: v for k, v in params.items() if v is not None}
74
+
75
+ return PreparedRequest(method="GET", url=self.mailboxes_base_url, params=params)
76
+
77
+ def prepare_get(self, mailbox_id: str) -> PreparedRequest:
78
+ return PreparedRequest(method="GET", url=f"{self.mailboxes_base_url}/{mailbox_id}")
79
+
80
+ def prepare_update(
81
+ self,
82
+ mailbox_id: str,
83
+ name: str = FieldUnset,
84
+ default_language: str = FieldUnset,
85
+ webhook_url: str = FieldUnset,
86
+ webhook_headers: dict[str, str] = FieldUnset,
87
+ need_validation: bool = FieldUnset,
88
+ authorized_domains: list[str] = FieldUnset,
89
+ authorized_emails: list[str] = FieldUnset,
90
+ ) -> PreparedRequest:
91
+ update_dict: dict[str, Any] = {}
92
+ if name is not FieldUnset:
93
+ update_dict['name'] = name
94
+ if default_language is not FieldUnset:
95
+ update_dict['default_language'] = default_language
96
+ if webhook_url is not FieldUnset:
97
+ update_dict['webhook_url'] = webhook_url
98
+ if webhook_headers is not FieldUnset:
99
+ update_dict['webhook_headers'] = webhook_headers
100
+ if need_validation is not FieldUnset:
101
+ update_dict['need_validation'] = need_validation
102
+ if authorized_domains is not FieldUnset:
103
+ update_dict['authorized_domains'] = authorized_domains
104
+ if authorized_emails is not FieldUnset:
105
+ update_dict['authorized_emails'] = authorized_emails
106
+
107
+ update_mailbox_request = UpdateMailboxRequest(**update_dict)
108
+ return PreparedRequest(method="PUT", url=f"/v1/processors/automations/mailboxes/{mailbox_id}", data=update_mailbox_request.model_dump(mode="json", exclude_unset=True))
109
+
110
+ def prepare_delete(self, mailbox_id: str) -> PreparedRequest:
111
+ return PreparedRequest(method="DELETE", url=f"/v1/processors/automations/mailboxes/{mailbox_id}", raise_for_status=True)
112
+
113
+
114
+ class Mailboxes(SyncAPIResource, MailBoxesMixin):
115
+ """Emails API wrapper for managing email automation configurations"""
116
+
117
+ def __init__(self, client: Any) -> None:
118
+ super().__init__(client=client)
119
+ self.tests = TestMailboxes(client=client)
120
+
121
+ def create(
122
+ self,
123
+ email: str,
124
+ name: str,
125
+ webhook_url: str,
126
+ processor_id: str,
127
+ authorized_domains: list[str] = FieldUnset,
128
+ authorized_emails: list[EmailStr] = FieldUnset,
129
+ webhook_headers: dict[str, str] = FieldUnset,
130
+ default_language: str = FieldUnset,
131
+ need_validation: bool = FieldUnset,
132
+ ) -> Mailbox:
133
+ """Create a new email automation configuration.
134
+
135
+ Args:
136
+ email: Email address for the mailbox
137
+ name: Name of the mailbox
138
+ webhook_url: Webhook URL to receive processed emails
139
+ processor_id: ID of the processor to use for the mailbox
140
+ authorized_domains: List of authorized domains for the mailbox
141
+ authorized_emails: List of authorized emails for the mailbox
142
+ webhook_headers: Webhook headers to send with processed emails
143
+
144
+ Returns:
145
+ Mailbox: The created mailbox configuration
146
+ """
147
+
148
+ request = self.prepare_create(
149
+ email=email,
150
+ name=name,
151
+ processor_id=processor_id,
152
+ webhook_url=webhook_url,
153
+ authorized_domains=authorized_domains,
154
+ authorized_emails=authorized_emails,
155
+ webhook_headers=webhook_headers,
156
+ default_language=default_language,
157
+ need_validation=need_validation,
158
+ )
159
+ response = self._client._prepared_request(request)
160
+
161
+ print(f"Mailbox {response['email']} created. Url: https://www.retab.com/dashboard/processors/automations/{response['id']}")
162
+
163
+ return Mailbox.model_validate(response)
164
+
165
+ def list(
166
+ self,
167
+ processor_id: str,
168
+ before: str | None = None,
169
+ after: str | None = None,
170
+ limit: int = 10,
171
+ order: Literal["asc", "desc"] | None = "desc",
172
+ name: Optional[str] = None,
173
+ email: Optional[str] = None,
174
+ webhook_url: Optional[str] = None,
175
+ ) -> ListMailboxes:
176
+ """List all email automation configurations.
177
+
178
+ Args:
179
+ before: Optional cursor for pagination - get results before this log ID
180
+ after: Optional cursor for pagination - get results after this log ID
181
+ limit: Maximum number of logs to return (1-100, default 10)
182
+ order: Sort order by creation time - "asc" or "desc" (default "desc")
183
+ email: Optional email address filter
184
+ webhook_url: Optional webhook URL filter
185
+ schema_id: Optional schema ID filter
186
+ schema_data_id: Optional schema data ID filter
187
+
188
+ Returns:
189
+ ListMailboxes: List of mailbox configurations
190
+ """
191
+ request = self.prepare_list(
192
+ processor_id=processor_id,
193
+ before=before,
194
+ after=after,
195
+ limit=limit,
196
+ order=order,
197
+ email=email,
198
+ name=name,
199
+ webhook_url=webhook_url,
200
+ )
201
+ response = self._client._prepared_request(request)
202
+ return ListMailboxes.model_validate(response)
203
+
204
+ def get(self, mailbox_id: str) -> Mailbox:
205
+ """Get a specific email automation configuration.
206
+
207
+ Args:
208
+ mailbox_id: ID of the mailbox
209
+
210
+ Returns:
211
+ Mailbox: The mailbox configuration
212
+ """
213
+ request = self.prepare_get(mailbox_id)
214
+ response = self._client._prepared_request(request)
215
+ return Mailbox.model_validate(response)
216
+
217
+ def update(
218
+ self,
219
+ mailbox_id: str,
220
+ name: str = FieldUnset,
221
+ default_language: str = FieldUnset,
222
+ webhook_url: str = FieldUnset,
223
+ webhook_headers: dict[str, str] = FieldUnset,
224
+ need_validation: bool = FieldUnset,
225
+ authorized_domains: List[str] = FieldUnset,
226
+ authorized_emails: List[str] = FieldUnset,
227
+ ) -> Mailbox:
228
+ """Update an email automation configuration.
229
+
230
+ Args:
231
+ email: Email address of the mailbox to update
232
+ name: New name
233
+ default_language: New default language
234
+ webhook_url: New webhook configuration
235
+ webhook_headers: New webhook configuration
236
+ need_validation: New webhook configuration
237
+ authorized_domains: New webhook configuration
238
+ authorized_emails: New webhook configuration
239
+
240
+ Returns:
241
+ Mailbox: The updated mailbox configuration
242
+ """
243
+ request = self.prepare_update(
244
+ mailbox_id=mailbox_id,
245
+ name=name,
246
+ default_language=default_language,
247
+ webhook_url=webhook_url,
248
+ webhook_headers=webhook_headers,
249
+ need_validation=need_validation,
250
+ authorized_domains=authorized_domains,
251
+ authorized_emails=authorized_emails,
252
+ )
253
+ response = self._client._prepared_request(request)
254
+ return Mailbox.model_validate(response)
255
+
256
+ def delete(self, mailbox_id: str) -> None:
257
+ """Delete an email automation configuration.
258
+
259
+ Args:
260
+ email: Email address of the mailbox to delete
261
+ """
262
+ request = self.prepare_delete(mailbox_id)
263
+ self._client._prepared_request(request)
264
+ return None
265
+
266
+
267
+ class AsyncMailboxes(AsyncAPIResource, MailBoxesMixin):
268
+ def __init__(self, client: Any) -> None:
269
+ super().__init__(client=client)
270
+ self.tests = AsyncTestMailboxes(client=client)
271
+
272
+ async def create(
273
+ self,
274
+ email: str,
275
+ name: str,
276
+ webhook_url: str,
277
+ processor_id: str,
278
+ authorized_domains: List[str] = FieldUnset,
279
+ authorized_emails: List[EmailStr] = FieldUnset,
280
+ webhook_headers: dict[str, str] = FieldUnset,
281
+ default_language: str = FieldUnset,
282
+ need_validation: bool = FieldUnset,
283
+ ) -> Mailbox:
284
+ request = self.prepare_create(
285
+ email=email,
286
+ name=name,
287
+ processor_id=processor_id,
288
+ webhook_url=webhook_url,
289
+ authorized_domains=authorized_domains,
290
+ authorized_emails=authorized_emails,
291
+ webhook_headers=webhook_headers,
292
+ default_language=default_language,
293
+ need_validation=need_validation,
294
+ )
295
+ response = await self._client._prepared_request(request)
296
+
297
+ print(f"Mailbox {response['email']} created. Url: https://www.retab.com/dashboard/processors/automations/{response['id']}")
298
+
299
+ return Mailbox.model_validate(response)
300
+
301
+ async def list(
302
+ self,
303
+ processor_id: str,
304
+ before: str | None = None,
305
+ after: str | None = None,
306
+ limit: int = 10,
307
+ order: Literal["asc", "desc"] | None = "desc",
308
+ email: str | None = None,
309
+ name: str | None = None,
310
+ webhook_url: str | None = None,
311
+ ) -> ListMailboxes:
312
+ request = self.prepare_list(
313
+ processor_id=processor_id,
314
+ before=before,
315
+ after=after,
316
+ limit=limit,
317
+ order=order,
318
+ email=email,
319
+ name=name,
320
+ webhook_url=webhook_url,
321
+ )
322
+ response = await self._client._prepared_request(request)
323
+ return ListMailboxes.model_validate(response)
324
+
325
+ async def get(self, mailbox_id: str) -> Mailbox:
326
+ request = self.prepare_get(mailbox_id)
327
+ response = await self._client._prepared_request(request)
328
+ return Mailbox.model_validate(response)
329
+
330
+ async def update(
331
+ self,
332
+ mailbox_id: str,
333
+ name: str = FieldUnset,
334
+ default_language: str = FieldUnset,
335
+ webhook_url: str = FieldUnset,
336
+ webhook_headers: dict[str, str] = FieldUnset,
337
+ need_validation: bool = FieldUnset,
338
+ authorized_domains: List[str] = FieldUnset,
339
+ authorized_emails: List[str] = FieldUnset,
340
+ ) -> Mailbox:
341
+ request = self.prepare_update(
342
+ mailbox_id=mailbox_id,
343
+ name=name,
344
+ default_language=default_language,
345
+ webhook_url=webhook_url,
346
+ webhook_headers=webhook_headers,
347
+ need_validation=need_validation,
348
+ authorized_domains=authorized_domains,
349
+ authorized_emails=authorized_emails,
350
+ )
351
+ response = await self._client._prepared_request(request)
352
+ return Mailbox.model_validate(response)
353
+
354
+ async def delete(self, mailbox_id: str) -> None:
355
+ request = self.prepare_delete(mailbox_id)
356
+ await self._client._prepared_request(request)
357
+ return None
358
+
359
+
360
+ class TestMailboxesMixin:
361
+ def prepare_forward(
362
+ self,
363
+ mailbox_id: str,
364
+ document: Path | str | IOBase | HttpUrl | MIMEData,
365
+ verbose: bool = True,
366
+ ) -> PreparedRequest:
367
+ mime_document = prepare_mime_document(document)
368
+ return PreparedRequest(method="POST", url=f"/v1/processors/automations/mailboxes/tests/forward/{mailbox_id}", data={"document": mime_document.model_dump()})
369
+
370
+ def print_forward_verbose(self, email_data: EmailData) -> None:
371
+ print("\nTEST EMAIL FORWARDING RESULTS:")
372
+ print("\n#########################")
373
+ print(f"Email ID: {email_data.id}")
374
+ print(f"Subject: {email_data.subject}")
375
+ print(f"From: {email_data.sender}")
376
+ print(f"To: {', '.join(str(r) for r in email_data.recipients_to)}")
377
+ if email_data.recipients_cc:
378
+ print(f"CC: {', '.join(str(r) for r in email_data.recipients_cc)}")
379
+ print(f"Sent at: {email_data.sent_at}")
380
+ print(f"Attachments: {len(email_data.attachments)}")
381
+ if email_data.body_plain:
382
+ print("\nBody Preview:")
383
+ print(email_data.body_plain[:500] + "..." if len(email_data.body_plain) > 500 else email_data.body_plain)
384
+
385
+
386
+ class TestMailboxes(SyncAPIResource, TestMailboxesMixin):
387
+ def forward(
388
+ self,
389
+ mailbox_id: str,
390
+ document: Path | str | IOBase | HttpUrl | MIMEData,
391
+ verbose: bool = True,
392
+ ) -> EmailData:
393
+ """Mock endpoint that simulates the complete email forwarding process with sample data.
394
+
395
+ Args:
396
+ email: Email address of the mailbox to mock
397
+
398
+ Returns:
399
+ DocumentExtractResponse: The simulated extraction response
400
+ """
401
+ request = self.prepare_forward(mailbox_id, document, verbose)
402
+ response = self._client._prepared_request(request)
403
+
404
+ email_data = EmailData.model_validate(response)
405
+
406
+ if verbose:
407
+ self.print_forward_verbose(email_data)
408
+ return email_data
409
+
410
+
411
+ class AsyncTestMailboxes(AsyncAPIResource, TestMailboxesMixin):
412
+ async def forward(
413
+ self,
414
+ mailbox_id: str,
415
+ document: Path | str | IOBase | HttpUrl | MIMEData,
416
+ verbose: bool = True,
417
+ ) -> EmailData:
418
+ request = self.prepare_forward(mailbox_id, document, verbose)
419
+ response = await self._client._prepared_request(request)
420
+ email_data = EmailData.model_validate(response)
421
+ if verbose:
422
+ self.print_forward_verbose(email_data)
423
+ return email_data