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.
- retab/__init__.py +2 -1
- retab/client.py +26 -51
- retab/generate_types.py +180 -0
- retab/resources/consensus/client.py +1 -1
- retab/resources/consensus/responses.py +1 -1
- retab/resources/deployments/__init__.py +3 -0
- retab/resources/deployments/automations/__init__.py +9 -0
- retab/resources/deployments/automations/client.py +244 -0
- retab/resources/deployments/automations/endpoints.py +290 -0
- retab/resources/deployments/automations/links.py +303 -0
- retab/resources/deployments/automations/logs.py +222 -0
- retab/resources/deployments/automations/mailboxes.py +423 -0
- retab/resources/deployments/automations/outlook.py +377 -0
- retab/resources/deployments/automations/tests.py +161 -0
- retab/resources/deployments/client.py +148 -0
- retab/resources/documents/client.py +94 -68
- retab/resources/documents/extractions.py +55 -46
- retab/resources/evaluations/__init__.py +2 -2
- retab/resources/evaluations/client.py +61 -77
- retab/resources/evaluations/documents.py +48 -37
- retab/resources/evaluations/iterations.py +58 -40
- retab/resources/jsonlUtils.py +3 -4
- retab/resources/processors/automations/endpoints.py +49 -39
- retab/resources/processors/automations/links.py +52 -43
- retab/resources/processors/automations/mailboxes.py +74 -59
- retab/resources/processors/automations/outlook.py +104 -82
- retab/resources/processors/client.py +35 -30
- retab/resources/projects/__init__.py +3 -0
- retab/resources/projects/client.py +285 -0
- retab/resources/projects/documents.py +244 -0
- retab/resources/projects/iterations.py +470 -0
- retab/resources/usage.py +2 -0
- retab/types/ai_models.py +2 -1
- retab/types/deprecated_evals.py +195 -0
- retab/types/evaluations/__init__.py +5 -2
- retab/types/evaluations/iterations.py +9 -43
- retab/types/evaluations/model.py +19 -24
- retab/types/extractions.py +1 -0
- retab/types/jobs/base.py +1 -1
- retab/types/jobs/evaluation.py +1 -1
- retab/types/logs.py +5 -6
- retab/types/mime.py +1 -10
- retab/types/projects/__init__.py +34 -0
- retab/types/projects/documents.py +30 -0
- retab/types/projects/iterations.py +78 -0
- retab/types/projects/model.py +68 -0
- retab/types/schemas/enhance.py +22 -5
- retab/types/schemas/evaluate.py +2 -2
- retab/types/schemas/object.py +27 -25
- retab/types/standards.py +2 -2
- retab/utils/__init__.py +3 -0
- retab/utils/ai_models.py +127 -12
- retab/utils/hashing.py +24 -0
- retab/utils/json_schema.py +1 -26
- retab/utils/mime.py +0 -17
- retab/utils/usage/usage.py +0 -1
- {retab-0.0.42.dist-info → retab-0.0.44.dist-info}/METADATA +4 -6
- {retab-0.0.42.dist-info → retab-0.0.44.dist-info}/RECORD +60 -55
- retab/_utils/__init__.py +0 -0
- retab/_utils/_model_cards/anthropic.yaml +0 -59
- retab/_utils/_model_cards/auto.yaml +0 -43
- retab/_utils/_model_cards/gemini.yaml +0 -117
- retab/_utils/_model_cards/openai.yaml +0 -301
- retab/_utils/_model_cards/xai.yaml +0 -28
- retab/_utils/ai_models.py +0 -138
- retab/_utils/benchmarking.py +0 -484
- retab/_utils/chat.py +0 -327
- retab/_utils/display.py +0 -440
- retab/_utils/json_schema.py +0 -2156
- retab/_utils/mime.py +0 -165
- retab/_utils/responses.py +0 -169
- retab/_utils/stream_context_managers.py +0 -52
- retab/_utils/usage/__init__.py +0 -0
- retab/_utils/usage/usage.py +0 -301
- {retab-0.0.42.dist-info → retab-0.0.44.dist-info}/WHEEL +0 -0
- {retab-0.0.42.dist-info → retab-0.0.44.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,377 @@
|
|
1
|
+
from typing import Any, Literal, List
|
2
|
+
|
3
|
+
from ....types.standards import FieldUnset
|
4
|
+
|
5
|
+
from ...._resource import AsyncAPIResource, SyncAPIResource
|
6
|
+
from ....types.automations.outlook import (
|
7
|
+
FetchParams,
|
8
|
+
ListOutlooks,
|
9
|
+
MatchParams,
|
10
|
+
Outlook,
|
11
|
+
UpdateOutlookRequest,
|
12
|
+
)
|
13
|
+
from ....types.standards import PreparedRequest
|
14
|
+
|
15
|
+
|
16
|
+
class OutlooksMixin:
|
17
|
+
outlooks_base_url: str = "/v1/processors/automations/outlook"
|
18
|
+
|
19
|
+
def prepare_create(
|
20
|
+
self,
|
21
|
+
name: str,
|
22
|
+
processor_id: str,
|
23
|
+
webhook_url: str,
|
24
|
+
default_language: str = FieldUnset,
|
25
|
+
webhook_headers: dict[str, str] = FieldUnset,
|
26
|
+
need_validation: bool = FieldUnset,
|
27
|
+
authorized_domains: list[str] = FieldUnset,
|
28
|
+
authorized_emails: list[str] = FieldUnset,
|
29
|
+
layout_schema: dict[str, Any] = FieldUnset,
|
30
|
+
match_params: list[MatchParams] = FieldUnset,
|
31
|
+
fetch_params: list[FetchParams] = FieldUnset,
|
32
|
+
) -> PreparedRequest:
|
33
|
+
# Build outlook dictionary with only provided fields
|
34
|
+
outlook_dict: dict[str, Any] = {
|
35
|
+
'processor_id': processor_id,
|
36
|
+
'name': name,
|
37
|
+
'webhook_url': webhook_url,
|
38
|
+
}
|
39
|
+
if default_language is not FieldUnset:
|
40
|
+
outlook_dict['default_language'] = default_language
|
41
|
+
if webhook_headers is not FieldUnset:
|
42
|
+
outlook_dict['webhook_headers'] = webhook_headers
|
43
|
+
if need_validation is not FieldUnset:
|
44
|
+
outlook_dict['need_validation'] = need_validation
|
45
|
+
if authorized_domains is not FieldUnset:
|
46
|
+
outlook_dict['authorized_domains'] = authorized_domains
|
47
|
+
if authorized_emails is not FieldUnset:
|
48
|
+
outlook_dict['authorized_emails'] = authorized_emails
|
49
|
+
if layout_schema is not FieldUnset:
|
50
|
+
outlook_dict['layout_schema'] = layout_schema
|
51
|
+
if match_params is not FieldUnset:
|
52
|
+
outlook_dict['match_params'] = match_params
|
53
|
+
if fetch_params is not FieldUnset:
|
54
|
+
outlook_dict['fetch_params'] = fetch_params
|
55
|
+
|
56
|
+
# Validate the data
|
57
|
+
outlook_data = Outlook(**outlook_dict)
|
58
|
+
return PreparedRequest(
|
59
|
+
method="POST",
|
60
|
+
url=self.outlooks_base_url,
|
61
|
+
data=outlook_data.model_dump(mode="json", exclude_unset=True),
|
62
|
+
)
|
63
|
+
|
64
|
+
def prepare_list(
|
65
|
+
self,
|
66
|
+
processor_id: str,
|
67
|
+
before: str | None = None,
|
68
|
+
after: str | None = None,
|
69
|
+
limit: int = 10,
|
70
|
+
order: Literal["asc", "desc"] | None = "desc",
|
71
|
+
name: str | None = None,
|
72
|
+
webhook_url: str | None = None,
|
73
|
+
) -> PreparedRequest:
|
74
|
+
params = {
|
75
|
+
"processor_id": processor_id,
|
76
|
+
"before": before,
|
77
|
+
"after": after,
|
78
|
+
"limit": limit,
|
79
|
+
"order": order,
|
80
|
+
"name": name,
|
81
|
+
"webhook_url": webhook_url,
|
82
|
+
}
|
83
|
+
# Remove None values
|
84
|
+
params = {k: v for k, v in params.items() if v is not None}
|
85
|
+
|
86
|
+
return PreparedRequest(method="GET", url=self.outlooks_base_url, params=params)
|
87
|
+
|
88
|
+
def prepare_get(self, outlook_id: str) -> PreparedRequest:
|
89
|
+
return PreparedRequest(method="GET", url=f"{self.outlooks_base_url}/{outlook_id}")
|
90
|
+
|
91
|
+
def prepare_update(
|
92
|
+
self,
|
93
|
+
outlook_id: str,
|
94
|
+
name: str = FieldUnset,
|
95
|
+
default_language: str = FieldUnset,
|
96
|
+
webhook_url: str = FieldUnset,
|
97
|
+
webhook_headers: dict[str, str] = FieldUnset,
|
98
|
+
need_validation: bool = FieldUnset,
|
99
|
+
authorized_domains: list[str] = FieldUnset,
|
100
|
+
authorized_emails: list[str] = FieldUnset,
|
101
|
+
match_params: list[MatchParams] = FieldUnset,
|
102
|
+
fetch_params: list[FetchParams] = FieldUnset,
|
103
|
+
layout_schema: dict[str, Any] = FieldUnset,
|
104
|
+
) -> PreparedRequest:
|
105
|
+
update_dict: dict[str, Any] = {}
|
106
|
+
if name is not FieldUnset:
|
107
|
+
update_dict['name'] = name
|
108
|
+
if default_language is not FieldUnset:
|
109
|
+
update_dict['default_language'] = default_language
|
110
|
+
if webhook_url is not FieldUnset:
|
111
|
+
update_dict['webhook_url'] = webhook_url
|
112
|
+
if webhook_headers is not FieldUnset:
|
113
|
+
update_dict['webhook_headers'] = webhook_headers
|
114
|
+
if need_validation is not FieldUnset:
|
115
|
+
update_dict['need_validation'] = need_validation
|
116
|
+
if authorized_domains is not FieldUnset:
|
117
|
+
update_dict['authorized_domains'] = authorized_domains
|
118
|
+
if authorized_emails is not FieldUnset:
|
119
|
+
update_dict['authorized_emails'] = authorized_emails
|
120
|
+
if layout_schema is not FieldUnset:
|
121
|
+
update_dict['layout_schema'] = layout_schema
|
122
|
+
if match_params is not FieldUnset:
|
123
|
+
update_dict['match_params'] = match_params
|
124
|
+
if fetch_params is not FieldUnset:
|
125
|
+
update_dict['fetch_params'] = fetch_params
|
126
|
+
|
127
|
+
update_outlook_request = UpdateOutlookRequest(**update_dict)
|
128
|
+
|
129
|
+
return PreparedRequest(
|
130
|
+
method="PUT",
|
131
|
+
url=f"{self.outlooks_base_url}/{outlook_id}",
|
132
|
+
data=update_outlook_request.model_dump(mode="json", exclude_unset=True),
|
133
|
+
)
|
134
|
+
|
135
|
+
def prepare_delete(self, outlook_id: str) -> PreparedRequest:
|
136
|
+
return PreparedRequest(method="DELETE", url=f"{self.outlooks_base_url}/{outlook_id}")
|
137
|
+
|
138
|
+
|
139
|
+
class Outlooks(SyncAPIResource, OutlooksMixin):
|
140
|
+
"""Outlook API wrapper for managing outlook automation configurations"""
|
141
|
+
|
142
|
+
def __init__(self, client: Any) -> None:
|
143
|
+
super().__init__(client=client)
|
144
|
+
|
145
|
+
def create(
|
146
|
+
self,
|
147
|
+
name: str,
|
148
|
+
processor_id: str,
|
149
|
+
webhook_url: str,
|
150
|
+
default_language: str = FieldUnset,
|
151
|
+
webhook_headers: dict[str, str] = FieldUnset,
|
152
|
+
need_validation: bool = FieldUnset,
|
153
|
+
authorized_domains: list[str] = FieldUnset,
|
154
|
+
authorized_emails: list[str] = FieldUnset,
|
155
|
+
layout_schema: dict[str, Any] = FieldUnset,
|
156
|
+
match_params: list[MatchParams] = FieldUnset,
|
157
|
+
fetch_params: list[FetchParams] = FieldUnset,
|
158
|
+
) -> Outlook:
|
159
|
+
"""Create a new outlook automation configuration.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
name: Name of the outlook plugin
|
163
|
+
processor_id: ID of the processor to use for the automation
|
164
|
+
webhook_url: Webhook URL to receive processed data
|
165
|
+
webhook_headers: Webhook headers to send with processed data
|
166
|
+
authorized_domains: List of authorized domains
|
167
|
+
authorized_emails: List of authorized emails
|
168
|
+
layout_schema: Layout schema to display the data
|
169
|
+
match_params: List of match parameters for the outlook automation
|
170
|
+
fetch_params: List of fetch parameters for the outlook automation
|
171
|
+
Returns:
|
172
|
+
Outlook: The created outlook plugin configuration
|
173
|
+
"""
|
174
|
+
|
175
|
+
request = self.prepare_create(
|
176
|
+
name=name,
|
177
|
+
processor_id=processor_id,
|
178
|
+
webhook_url=webhook_url,
|
179
|
+
default_language=default_language,
|
180
|
+
webhook_headers=webhook_headers,
|
181
|
+
need_validation=need_validation,
|
182
|
+
authorized_domains=authorized_domains,
|
183
|
+
authorized_emails=authorized_emails,
|
184
|
+
layout_schema=layout_schema,
|
185
|
+
match_params=match_params,
|
186
|
+
fetch_params=fetch_params,
|
187
|
+
)
|
188
|
+
response = self._client._prepared_request(request)
|
189
|
+
|
190
|
+
print(f"Outlook plugin created. Url: https://www.retab.com/dashboard/processors/automations/{response['id']}")
|
191
|
+
|
192
|
+
return Outlook.model_validate(response)
|
193
|
+
|
194
|
+
def list(
|
195
|
+
self,
|
196
|
+
processor_id: str,
|
197
|
+
before: str | None = None,
|
198
|
+
after: str | None = None,
|
199
|
+
limit: int = 10,
|
200
|
+
order: Literal["asc", "desc"] | None = "desc",
|
201
|
+
name: str | None = None,
|
202
|
+
webhook_url: str | None = None,
|
203
|
+
) -> ListOutlooks:
|
204
|
+
"""List all outlook automation configurations.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
before: Optional cursor for pagination - get results before this log ID
|
208
|
+
after: Optional cursor for pagination - get results after this log ID
|
209
|
+
limit: Maximum number of logs to return (1-100, default 10)
|
210
|
+
order: Sort order by creation time - "asc" or "desc" (default "desc")
|
211
|
+
name: Optional name filter
|
212
|
+
webhook_url: Optional webhook URL filter
|
213
|
+
Returns:
|
214
|
+
List[Outlook]: List of outlook plugin configurations
|
215
|
+
"""
|
216
|
+
request = self.prepare_list(processor_id, before, after, limit, order, name, webhook_url)
|
217
|
+
response = self._client._prepared_request(request)
|
218
|
+
return ListOutlooks.model_validate(response)
|
219
|
+
|
220
|
+
def get(self, outlook_id: str) -> Outlook:
|
221
|
+
"""Get a specific outlook automation configuration.
|
222
|
+
|
223
|
+
Args:
|
224
|
+
id: ID of the outlook plugin
|
225
|
+
|
226
|
+
Returns:
|
227
|
+
Outlook: The outlook plugin configuration
|
228
|
+
"""
|
229
|
+
request = self.prepare_get(outlook_id)
|
230
|
+
response = self._client._prepared_request(request)
|
231
|
+
return Outlook.model_validate(response)
|
232
|
+
|
233
|
+
def update(
|
234
|
+
self,
|
235
|
+
outlook_id: str,
|
236
|
+
name: str = FieldUnset,
|
237
|
+
default_language: str = FieldUnset,
|
238
|
+
webhook_url: str = FieldUnset,
|
239
|
+
webhook_headers: dict[str, str] = FieldUnset,
|
240
|
+
need_validation: bool = FieldUnset,
|
241
|
+
authorized_domains: List[str] = FieldUnset,
|
242
|
+
authorized_emails: List[str] = FieldUnset,
|
243
|
+
layout_schema: dict[str, Any] = FieldUnset,
|
244
|
+
match_params: List[MatchParams] = FieldUnset,
|
245
|
+
fetch_params: List[FetchParams] = FieldUnset,
|
246
|
+
) -> Outlook:
|
247
|
+
"""Update an outlook automation configuration.
|
248
|
+
|
249
|
+
Args:
|
250
|
+
outlook_id: ID of the outlook plugin to update
|
251
|
+
name: New name for the outlook plugin
|
252
|
+
webhook_url: New webhook URL
|
253
|
+
webhook_headers: New webhook headers
|
254
|
+
authorized_domains: New authorized domains
|
255
|
+
authorized_emails: New authorized emails
|
256
|
+
match_params: New match parameters for the outlook automation
|
257
|
+
fetch_params: New fetch parameters for the outlook automation
|
258
|
+
layout_schema: New layout schema for the outlook automation
|
259
|
+
|
260
|
+
Returns:
|
261
|
+
Outlook: The updated outlook plugin configuration
|
262
|
+
"""
|
263
|
+
request = self.prepare_update(
|
264
|
+
outlook_id,
|
265
|
+
name=name,
|
266
|
+
default_language=default_language,
|
267
|
+
webhook_url=webhook_url,
|
268
|
+
webhook_headers=webhook_headers,
|
269
|
+
need_validation=need_validation,
|
270
|
+
authorized_domains=authorized_domains,
|
271
|
+
authorized_emails=authorized_emails,
|
272
|
+
layout_schema=layout_schema,
|
273
|
+
match_params=match_params,
|
274
|
+
fetch_params=fetch_params,
|
275
|
+
)
|
276
|
+
response = self._client._prepared_request(request)
|
277
|
+
return Outlook.model_validate(response)
|
278
|
+
|
279
|
+
def delete(self, outlook_id: str) -> None:
|
280
|
+
"""Delete an outlook automation configuration.
|
281
|
+
|
282
|
+
Args:
|
283
|
+
outlook_id: ID of the outlook plugin to delete
|
284
|
+
"""
|
285
|
+
request = self.prepare_delete(outlook_id)
|
286
|
+
self._client._prepared_request(request)
|
287
|
+
return None
|
288
|
+
|
289
|
+
|
290
|
+
class AsyncOutlooks(AsyncAPIResource, OutlooksMixin):
|
291
|
+
def __init__(self, client: Any) -> None:
|
292
|
+
super().__init__(client=client)
|
293
|
+
|
294
|
+
async def create(
|
295
|
+
self,
|
296
|
+
name: str,
|
297
|
+
processor_id: str,
|
298
|
+
webhook_url: str,
|
299
|
+
default_language: str = FieldUnset,
|
300
|
+
webhook_headers: dict[str, str] = FieldUnset,
|
301
|
+
need_validation: bool = FieldUnset,
|
302
|
+
authorized_domains: list[str] = FieldUnset,
|
303
|
+
authorized_emails: list[str] = FieldUnset,
|
304
|
+
layout_schema: dict[str, Any] = FieldUnset,
|
305
|
+
match_params: list[MatchParams] = FieldUnset,
|
306
|
+
fetch_params: list[FetchParams] = FieldUnset,
|
307
|
+
) -> Outlook:
|
308
|
+
request = self.prepare_create(
|
309
|
+
name=name,
|
310
|
+
processor_id=processor_id,
|
311
|
+
webhook_url=webhook_url,
|
312
|
+
default_language=default_language,
|
313
|
+
webhook_headers=webhook_headers,
|
314
|
+
need_validation=need_validation,
|
315
|
+
authorized_domains=authorized_domains,
|
316
|
+
authorized_emails=authorized_emails,
|
317
|
+
layout_schema=layout_schema,
|
318
|
+
match_params=match_params,
|
319
|
+
fetch_params=fetch_params,
|
320
|
+
)
|
321
|
+
response = await self._client._prepared_request(request)
|
322
|
+
print(f"Outlook plugin created. Url: https://www.retab.com/dashboard/processors/automations/{response['id']}")
|
323
|
+
return Outlook.model_validate(response)
|
324
|
+
|
325
|
+
async def list(
|
326
|
+
self,
|
327
|
+
processor_id: str,
|
328
|
+
before: str | None = None,
|
329
|
+
after: str | None = None,
|
330
|
+
limit: int = 10,
|
331
|
+
order: Literal["asc", "desc"] | None = "desc",
|
332
|
+
name: str | None = None,
|
333
|
+
webhook_url: str | None = None,
|
334
|
+
) -> ListOutlooks:
|
335
|
+
request = self.prepare_list(processor_id, before, after, limit, order, name, webhook_url)
|
336
|
+
response = await self._client._prepared_request(request)
|
337
|
+
return ListOutlooks.model_validate(response)
|
338
|
+
|
339
|
+
async def get(self, outlook_id: str) -> Outlook:
|
340
|
+
request = self.prepare_get(outlook_id)
|
341
|
+
response = await self._client._prepared_request(request)
|
342
|
+
return Outlook.model_validate(response)
|
343
|
+
|
344
|
+
async def update(
|
345
|
+
self,
|
346
|
+
outlook_id: str,
|
347
|
+
name: str = FieldUnset,
|
348
|
+
default_language: str = FieldUnset,
|
349
|
+
webhook_url: str = FieldUnset,
|
350
|
+
webhook_headers: dict[str, str] = FieldUnset,
|
351
|
+
need_validation: bool = FieldUnset,
|
352
|
+
authorized_domains: List[str] = FieldUnset,
|
353
|
+
authorized_emails: List[str] = FieldUnset,
|
354
|
+
layout_schema: dict[str, Any] = FieldUnset,
|
355
|
+
match_params: List[MatchParams] = FieldUnset,
|
356
|
+
fetch_params: List[FetchParams] = FieldUnset,
|
357
|
+
) -> Outlook:
|
358
|
+
request = self.prepare_update(
|
359
|
+
outlook_id=outlook_id,
|
360
|
+
name=name,
|
361
|
+
default_language=default_language,
|
362
|
+
webhook_url=webhook_url,
|
363
|
+
webhook_headers=webhook_headers,
|
364
|
+
need_validation=need_validation,
|
365
|
+
authorized_domains=authorized_domains,
|
366
|
+
authorized_emails=authorized_emails,
|
367
|
+
layout_schema=layout_schema,
|
368
|
+
match_params=match_params,
|
369
|
+
fetch_params=fetch_params,
|
370
|
+
)
|
371
|
+
response = await self._client._prepared_request(request)
|
372
|
+
return Outlook.model_validate(response)
|
373
|
+
|
374
|
+
async def delete(self, outlook_id: str) -> None:
|
375
|
+
request = self.prepare_delete(outlook_id)
|
376
|
+
await self._client._prepared_request(request)
|
377
|
+
return None
|
@@ -0,0 +1,161 @@
|
|
1
|
+
import json
|
2
|
+
import base64
|
3
|
+
from io import IOBase
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
from PIL.Image import Image
|
7
|
+
from pydantic import HttpUrl
|
8
|
+
|
9
|
+
from ...._resource import AsyncAPIResource, SyncAPIResource
|
10
|
+
from ....utils.mime import prepare_mime_document
|
11
|
+
from ....types.logs import AutomationLog
|
12
|
+
from ....types.mime import MIMEData
|
13
|
+
from ....types.standards import PreparedRequest
|
14
|
+
|
15
|
+
|
16
|
+
class TestsMixin:
|
17
|
+
def prepare_upload(self, automation_id: str, document: Path | str | IOBase | HttpUrl | Image | MIMEData) -> PreparedRequest:
|
18
|
+
mime_document = prepare_mime_document(document)
|
19
|
+
|
20
|
+
# Convert MIME document to file upload format (similar to processors client)
|
21
|
+
files = {"file": (mime_document.filename, base64.b64decode(mime_document.content), mime_document.mime_type)}
|
22
|
+
|
23
|
+
# Send as multipart form data with file upload
|
24
|
+
return PreparedRequest(method="POST", url=f"/v1/processors/automations/tests/upload/{automation_id}", files=files)
|
25
|
+
|
26
|
+
def prepare_webhook(self, automation_id: str) -> PreparedRequest:
|
27
|
+
return PreparedRequest(method="POST", url=f"/v1/processors/automations/tests/webhook/{automation_id}", data=None)
|
28
|
+
|
29
|
+
def print_upload_verbose(self, log: AutomationLog) -> None:
|
30
|
+
if log.external_request_log:
|
31
|
+
print("\nTEST FILE UPLOAD RESULTS:")
|
32
|
+
print("\n#########################")
|
33
|
+
print(f"Status Code: {log.external_request_log.status_code}")
|
34
|
+
print(f"Duration: {log.external_request_log.duration_ms:.2f}ms")
|
35
|
+
|
36
|
+
if log.external_request_log.error:
|
37
|
+
print(f"\nERROR: {log.external_request_log.error}")
|
38
|
+
|
39
|
+
if log.external_request_log.response_body:
|
40
|
+
print("\n--------------")
|
41
|
+
print("RESPONSE BODY:")
|
42
|
+
print("--------------")
|
43
|
+
print(json.dumps(log.external_request_log.response_body, indent=2))
|
44
|
+
|
45
|
+
if log.external_request_log.response_headers:
|
46
|
+
print("\n--------------")
|
47
|
+
print("RESPONSE HEADERS:")
|
48
|
+
print("--------------")
|
49
|
+
print(json.dumps(log.external_request_log.response_headers, indent=2))
|
50
|
+
|
51
|
+
def print_webhook_verbose(self, log: AutomationLog) -> None:
|
52
|
+
if log.external_request_log:
|
53
|
+
print("\nTEST WEBHOOK RESULTS:")
|
54
|
+
print("\n#########################")
|
55
|
+
print(f"Status Code: {log.external_request_log.status_code}")
|
56
|
+
print(f"Duration: {log.external_request_log.duration_ms:.2f}ms")
|
57
|
+
|
58
|
+
if log.external_request_log.error:
|
59
|
+
print(f"\nERROR: {log.external_request_log.error}")
|
60
|
+
|
61
|
+
if log.external_request_log.response_body:
|
62
|
+
print("\n--------------")
|
63
|
+
print("RESPONSE BODY:")
|
64
|
+
print("--------------")
|
65
|
+
print(json.dumps(log.external_request_log.response_body, indent=2))
|
66
|
+
|
67
|
+
if log.external_request_log.response_headers:
|
68
|
+
print("\n--------------")
|
69
|
+
print("RESPONSE HEADERS:")
|
70
|
+
print("--------------")
|
71
|
+
print(json.dumps(log.external_request_log.response_headers, indent=2))
|
72
|
+
|
73
|
+
|
74
|
+
class Tests(SyncAPIResource, TestsMixin):
|
75
|
+
"""Test API wrapper for testing automation configurations"""
|
76
|
+
|
77
|
+
def upload(self, automation_id: str, document: Path | str | IOBase | HttpUrl | Image | MIMEData, verbose: bool = True) -> AutomationLog:
|
78
|
+
"""Test endpoint that simulates the complete extraction process with the provided document.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
automation_id: ID of the automation to test
|
82
|
+
document: Document to process
|
83
|
+
verbose: Whether to print verbose output
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
AutomationLog: The automation log with extraction results
|
87
|
+
"""
|
88
|
+
request = self.prepare_upload(automation_id, document)
|
89
|
+
response = self._client._prepared_request(request)
|
90
|
+
|
91
|
+
log = AutomationLog.model_validate(response)
|
92
|
+
|
93
|
+
if verbose:
|
94
|
+
self.print_upload_verbose(log)
|
95
|
+
|
96
|
+
return log
|
97
|
+
|
98
|
+
def webhook(self, automation_id: str, verbose: bool = True) -> AutomationLog:
|
99
|
+
"""Test endpoint that simulates the complete webhook process with sample data.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
automation_id: ID of the automation to test
|
103
|
+
verbose: Whether to print verbose output
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
AutomationLog: The automation log with webhook results
|
107
|
+
"""
|
108
|
+
request = self.prepare_webhook(automation_id)
|
109
|
+
response = self._client._prepared_request(request)
|
110
|
+
|
111
|
+
log = AutomationLog.model_validate(response)
|
112
|
+
|
113
|
+
if verbose:
|
114
|
+
self.print_webhook_verbose(log)
|
115
|
+
|
116
|
+
return log
|
117
|
+
|
118
|
+
|
119
|
+
class AsyncTests(AsyncAPIResource, TestsMixin):
|
120
|
+
"""Async Test API wrapper for testing deployment configurations"""
|
121
|
+
|
122
|
+
async def upload(self, automation_id: str, document: Path | str | IOBase | HttpUrl | Image | MIMEData, verbose: bool = True) -> AutomationLog:
|
123
|
+
"""Test endpoint that simulates the complete extraction process with the provided document.
|
124
|
+
|
125
|
+
Args:
|
126
|
+
automation_id: ID of the automation to test
|
127
|
+
document: Document to process
|
128
|
+
verbose: Whether to print verbose output
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
AutomationLog: The automation log with extraction results
|
132
|
+
"""
|
133
|
+
request = self.prepare_upload(automation_id, document)
|
134
|
+
response = await self._client._prepared_request(request)
|
135
|
+
|
136
|
+
log = AutomationLog.model_validate(response)
|
137
|
+
|
138
|
+
if verbose:
|
139
|
+
self.print_upload_verbose(log)
|
140
|
+
|
141
|
+
return log
|
142
|
+
|
143
|
+
async def webhook(self, automation_id: str, verbose: bool = True) -> AutomationLog:
|
144
|
+
"""Test endpoint that simulates the complete webhook process with sample data.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
automation_id: ID of the automation to test
|
148
|
+
verbose: Whether to print verbose output
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
AutomationLog: The automation log with webhook results
|
152
|
+
"""
|
153
|
+
request = self.prepare_webhook(automation_id)
|
154
|
+
response = await self._client._prepared_request(request)
|
155
|
+
|
156
|
+
log = AutomationLog.model_validate(response)
|
157
|
+
|
158
|
+
if verbose:
|
159
|
+
self.print_webhook_verbose(log)
|
160
|
+
|
161
|
+
return log
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import base64
|
2
|
+
from io import IOBase
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Any, List
|
5
|
+
|
6
|
+
import PIL.Image
|
7
|
+
from pydantic import HttpUrl
|
8
|
+
from ..._resource import AsyncAPIResource, SyncAPIResource
|
9
|
+
from ...utils.mime import MIMEData, prepare_mime_document
|
10
|
+
from ...types.documents.extractions import RetabParsedChatCompletion
|
11
|
+
from ...types.standards import PreparedRequest
|
12
|
+
|
13
|
+
|
14
|
+
class DeploymentsMixin:
|
15
|
+
def prepare_submit(
|
16
|
+
self,
|
17
|
+
project_id: str,
|
18
|
+
iteration_id: str,
|
19
|
+
document: Path | str | bytes | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
|
20
|
+
documents: list[Path | str | bytes | IOBase | MIMEData | PIL.Image.Image | HttpUrl] | None = None,
|
21
|
+
temperature: float | None = None,
|
22
|
+
seed: int | None = None,
|
23
|
+
store: bool = True,
|
24
|
+
) -> PreparedRequest:
|
25
|
+
"""Prepare a request to submit documents to a processor.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
project_id: ID of the project
|
29
|
+
iteration_id: ID of the iteration
|
30
|
+
document: Single document to process (mutually exclusive with documents)
|
31
|
+
documents: List of documents to process (mutually exclusive with document)
|
32
|
+
temperature: Optional temperature override
|
33
|
+
seed: Optional seed for reproducibility
|
34
|
+
store: Whether to store the results
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
PreparedRequest: The prepared request
|
38
|
+
"""
|
39
|
+
# Validate that either document or documents is provided, but not both
|
40
|
+
if not document and not documents:
|
41
|
+
raise ValueError("Either 'document' or 'documents' must be provided")
|
42
|
+
|
43
|
+
if document and documents:
|
44
|
+
raise ValueError("Provide either 'document' (single) or 'documents' (multiple), not both")
|
45
|
+
|
46
|
+
# Prepare form data parameters
|
47
|
+
form_data = {
|
48
|
+
"temperature": temperature,
|
49
|
+
"seed": seed,
|
50
|
+
"store": store,
|
51
|
+
}
|
52
|
+
# Remove None values
|
53
|
+
form_data = {k: v for k, v in form_data.items() if v is not None}
|
54
|
+
|
55
|
+
# Prepare files for upload
|
56
|
+
files = {}
|
57
|
+
if document:
|
58
|
+
# Convert document to MIMEData if needed
|
59
|
+
mime_document = prepare_mime_document(document)
|
60
|
+
# Single document upload
|
61
|
+
files["document"] = (mime_document.filename, base64.b64decode(mime_document.content), mime_document.mime_type)
|
62
|
+
elif documents:
|
63
|
+
# Multiple documents upload - httpx supports multiple files with same field name using a list
|
64
|
+
files_list = []
|
65
|
+
for doc in documents:
|
66
|
+
# Convert each document to MIMEData if needed
|
67
|
+
mime_doc = prepare_mime_document(doc)
|
68
|
+
files_list.append(
|
69
|
+
(
|
70
|
+
"documents", # field name
|
71
|
+
(mime_doc.filename, base64.b64decode(mime_doc.content), mime_doc.mime_type),
|
72
|
+
)
|
73
|
+
)
|
74
|
+
files = files_list
|
75
|
+
|
76
|
+
url = f"/v1/deployments/{project_id}/{iteration_id}/submit"
|
77
|
+
|
78
|
+
return PreparedRequest(method="POST", url=url, form_data=form_data, files=files)
|
79
|
+
|
80
|
+
|
81
|
+
class Deployments(SyncAPIResource, DeploymentsMixin):
|
82
|
+
"""Deployments API wrapper for managing deployment configurations"""
|
83
|
+
|
84
|
+
def __init__(self, client: Any) -> None:
|
85
|
+
super().__init__(client=client)
|
86
|
+
|
87
|
+
def submit(
|
88
|
+
self,
|
89
|
+
project_id: str,
|
90
|
+
iteration_id: str,
|
91
|
+
document: Path | str | bytes | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
|
92
|
+
documents: List[Path | str | bytes | IOBase | MIMEData | PIL.Image.Image | HttpUrl] | None = None,
|
93
|
+
temperature: float | None = None,
|
94
|
+
seed: int | None = None,
|
95
|
+
store: bool = True,
|
96
|
+
) -> RetabParsedChatCompletion:
|
97
|
+
"""Submit documents to a deployment for processing.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
project_id: ID of the project
|
101
|
+
iteration_id: ID of the iteration
|
102
|
+
document: Single document to process (mutually exclusive with documents)
|
103
|
+
documents: List of documents to process (mutually exclusive with document)
|
104
|
+
temperature: Optional temperature override
|
105
|
+
seed: Optional seed for reproducibility
|
106
|
+
store: Whether to store the results
|
107
|
+
|
108
|
+
Returns:
|
109
|
+
RetabParsedChatCompletion: The processing result
|
110
|
+
"""
|
111
|
+
request = self.prepare_submit(project_id=project_id, iteration_id=iteration_id, document=document, documents=documents, temperature=temperature, seed=seed, store=store)
|
112
|
+
response = self._client._prepared_request(request)
|
113
|
+
return RetabParsedChatCompletion.model_validate(response)
|
114
|
+
|
115
|
+
|
116
|
+
class AsyncDeployments(AsyncAPIResource, DeploymentsMixin):
|
117
|
+
"""Async Deployments API wrapper for managing deployment configurations"""
|
118
|
+
|
119
|
+
def __init__(self, client: Any) -> None:
|
120
|
+
super().__init__(client=client)
|
121
|
+
|
122
|
+
async def submit(
|
123
|
+
self,
|
124
|
+
project_id: str,
|
125
|
+
iteration_id: str,
|
126
|
+
document: Path | str | bytes | IOBase | MIMEData | PIL.Image.Image | HttpUrl | None = None,
|
127
|
+
documents: List[Path | str | bytes | IOBase | MIMEData | PIL.Image.Image | HttpUrl] | None = None,
|
128
|
+
temperature: float | None = None,
|
129
|
+
seed: int | None = None,
|
130
|
+
store: bool = True,
|
131
|
+
) -> RetabParsedChatCompletion:
|
132
|
+
"""Submit documents to a deployment for processing.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
project_id: ID of the project
|
136
|
+
iteration_id: ID of the iteration
|
137
|
+
document: Single document to process (mutually exclusive with documents)
|
138
|
+
documents: List of documents to process (mutually exclusive with document)
|
139
|
+
temperature: Optional temperature override
|
140
|
+
seed: Optional seed for reproducibility
|
141
|
+
store: Whether to store the results
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
RetabParsedChatCompletion: The processing result
|
145
|
+
"""
|
146
|
+
request = self.prepare_submit(project_id=project_id, iteration_id=iteration_id, document=document, documents=documents, temperature=temperature, seed=seed, store=store)
|
147
|
+
response = await self._client._prepared_request(request)
|
148
|
+
return RetabParsedChatCompletion.model_validate(response)
|