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
@@ -4,7 +4,7 @@ from openai.types.chat.chat_completion_reasoning_effort import ChatCompletionRea
4
4
 
5
5
  from ..._resource import AsyncAPIResource, SyncAPIResource
6
6
  from ...types.browser_canvas import BrowserCanvas
7
- from ...types.evaluations import CreateIterationRequest, Iteration, ProcessIterationRequest, IterationDocumentStatusResponse, PatchIterationRequest
7
+ from ...types.projects import CreateIterationRequest, Iteration, ProcessIterationRequest, IterationDocumentStatusResponse, PatchIterationRequest
8
8
  from ...types.inference_settings import InferenceSettings
9
9
  from ...types.metrics import DistancesResult
10
10
  from ...types.modalities import Modality
@@ -14,13 +14,13 @@ from ...types.documents.extractions import RetabParsedChatCompletion
14
14
 
15
15
  class IterationsMixin:
16
16
  def prepare_get(self, evaluation_id: str, iteration_id: str) -> PreparedRequest:
17
- return PreparedRequest(method="GET", url=f"/v1/evaluations/{evaluation_id}/iterations/{iteration_id}")
17
+ return PreparedRequest(method="GET", url=f"/v1/projects/{evaluation_id}/iterations/{iteration_id}")
18
18
 
19
19
  def prepare_list(self, evaluation_id: str, model: Optional[str] = None) -> PreparedRequest:
20
20
  params = {}
21
21
  if model:
22
22
  params["model"] = model
23
- return PreparedRequest(method="GET", url=f"/v1/evaluations/{evaluation_id}/iterations", params=params)
23
+ return PreparedRequest(method="GET", url=f"/v1/projects/{evaluation_id}/iterations", params=params)
24
24
 
25
25
  def prepare_create(
26
26
  self,
@@ -34,19 +34,27 @@ class IterationsMixin:
34
34
  browser_canvas: BrowserCanvas = FieldUnset,
35
35
  n_consensus: int = FieldUnset,
36
36
  ) -> PreparedRequest:
37
- inference_settings = InferenceSettings(
38
- model=model,
39
- temperature=temperature,
40
- modality=modality,
41
- reasoning_effort=reasoning_effort,
42
- image_resolution_dpi=image_resolution_dpi,
43
- browser_canvas=browser_canvas,
44
- n_consensus=n_consensus,
45
- )
37
+ inference_dict = {}
38
+ if model is not FieldUnset:
39
+ inference_dict["model"] = model
40
+ if temperature is not FieldUnset:
41
+ inference_dict["temperature"] = temperature
42
+ if modality is not FieldUnset:
43
+ inference_dict["modality"] = modality
44
+ if reasoning_effort is not FieldUnset:
45
+ inference_dict["reasoning_effort"] = reasoning_effort
46
+ if image_resolution_dpi is not FieldUnset:
47
+ inference_dict["image_resolution_dpi"] = image_resolution_dpi
48
+ if browser_canvas is not FieldUnset:
49
+ inference_dict["browser_canvas"] = browser_canvas
50
+ if n_consensus is not FieldUnset:
51
+ inference_dict["n_consensus"] = n_consensus
52
+
53
+ inference_settings = InferenceSettings(**inference_dict)
46
54
 
47
55
  request = CreateIterationRequest(inference_settings=inference_settings, json_schema=json_schema)
48
56
 
49
- return PreparedRequest(method="POST", url=f"/v1/evaluations/{evaluation_id}/iterations", data=request.model_dump(exclude_unset=True, exclude_defaults=True, mode="json"))
57
+ return PreparedRequest(method="POST", url=f"/v1/projects/{evaluation_id}/iterations", data=request.model_dump(exclude_unset=True, exclude_defaults=True, mode="json"))
50
58
 
51
59
  def prepare_update(
52
60
  self,
@@ -61,29 +69,39 @@ class IterationsMixin:
61
69
  browser_canvas: BrowserCanvas = FieldUnset,
62
70
  n_consensus: int = FieldUnset,
63
71
  ) -> PreparedRequest:
64
- inference_settings = InferenceSettings(
65
- model=model,
66
- temperature=temperature,
67
- modality=modality,
68
- reasoning_effort=reasoning_effort,
69
- image_resolution_dpi=image_resolution_dpi,
70
- browser_canvas=browser_canvas,
71
- n_consensus=n_consensus,
72
- )
73
- if not inference_settings.model_dump(exclude_unset=True, mode="json"):
74
- inference_settings = FieldUnset
75
-
76
- iteration_data = PatchIterationRequest(json_schema=json_schema, inference_settings=inference_settings)
72
+ inference_dict = {}
73
+ if model is not FieldUnset:
74
+ inference_dict["model"] = model
75
+ if temperature is not FieldUnset:
76
+ inference_dict["temperature"] = temperature
77
+ if modality is not FieldUnset:
78
+ inference_dict["modality"] = modality
79
+ if reasoning_effort is not FieldUnset:
80
+ inference_dict["reasoning_effort"] = reasoning_effort
81
+ if image_resolution_dpi is not FieldUnset:
82
+ inference_dict["image_resolution_dpi"] = image_resolution_dpi
83
+ if browser_canvas is not FieldUnset:
84
+ inference_dict["browser_canvas"] = browser_canvas
85
+ if n_consensus is not FieldUnset:
86
+ inference_dict["n_consensus"] = n_consensus
87
+
88
+ iteration_dict = {}
89
+ if json_schema is not FieldUnset:
90
+ iteration_dict["json_schema"] = json_schema
91
+ if inference_dict: # Only add inference_settings if we have at least one field
92
+ iteration_dict["inference_settings"] = InferenceSettings(**inference_dict)
93
+
94
+ iteration_data = PatchIterationRequest(**iteration_dict)
77
95
 
78
96
  return PreparedRequest(
79
- method="PATCH", url=f"/v1/evaluations/{evaluation_id}/iterations/{iteration_id}", data=iteration_data.model_dump(exclude_unset=True, exclude_defaults=True, mode="json")
97
+ method="PATCH", url=f"/v1/projects/{evaluation_id}/iterations/{iteration_id}", data=iteration_data.model_dump(exclude_unset=True, exclude_defaults=True, mode="json")
80
98
  )
81
99
 
82
100
  def prepare_delete(self, evaluation_id: str, iteration_id: str) -> PreparedRequest:
83
- return PreparedRequest(method="DELETE", url=f"/v1/evaluations/{evaluation_id}/iterations/{iteration_id}")
101
+ return PreparedRequest(method="DELETE", url=f"/v1/projects/{evaluation_id}/iterations/{iteration_id}")
84
102
 
85
103
  def prepare_compute_distances(self, evaluation_id: str, iteration_id: str, document_id: str) -> PreparedRequest:
86
- return PreparedRequest(method="GET", url=f"/v1/evaluations/{evaluation_id}/iterations/{iteration_id}/documents/{document_id}/distances")
104
+ return PreparedRequest(method="GET", url=f"/v1/projects/{evaluation_id}/iterations/{iteration_id}/documents/{document_id}/distances")
87
105
 
88
106
  def prepare_process(
89
107
  self,
@@ -96,13 +114,13 @@ class IterationsMixin:
96
114
  document_ids=document_ids,
97
115
  only_outdated=only_outdated,
98
116
  )
99
- return PreparedRequest(method="POST", url=f"/v1/evaluations/{evaluation_id}/iterations/{iteration_id}/process", data=request.model_dump(exclude_none=True, mode="json"))
117
+ return PreparedRequest(method="POST", url=f"/v1/projects/{evaluation_id}/iterations/{iteration_id}/process", data=request.model_dump(exclude_none=True, mode="json"))
100
118
 
101
119
  def prepare_process_document(self, evaluation_id: str, iteration_id: str, document_id: str) -> PreparedRequest:
102
- return PreparedRequest(method="POST", url=f"/v1/evaluations/{evaluation_id}/iterations/{iteration_id}/documents/{document_id}/process", data={"stream": False})
120
+ return PreparedRequest(method="POST", url=f"/v1/projects/{evaluation_id}/iterations/{iteration_id}/documents/{document_id}/process", data={"stream": False})
103
121
 
104
122
  def prepare_status(self, evaluation_id: str, iteration_id: str) -> PreparedRequest:
105
- return PreparedRequest(method="GET", url=f"/v1/evaluations/{evaluation_id}/iterations/{iteration_id}/status")
123
+ return PreparedRequest(method="GET", url=f"/v1/projects/{evaluation_id}/iterations/{iteration_id}/status")
106
124
 
107
125
 
108
126
  class Iterations(SyncAPIResource, IterationsMixin):
@@ -136,14 +154,14 @@ class Iterations(SyncAPIResource, IterationsMixin):
136
154
  def create(
137
155
  self,
138
156
  evaluation_id: str,
139
- model: str,
140
- temperature: float = 0.0,
141
- modality: Modality = "native",
142
- json_schema: Optional[Dict[str, Any]] = None,
143
- reasoning_effort: ChatCompletionReasoningEffort = "medium",
144
- image_resolution_dpi: int = 96,
145
- browser_canvas: BrowserCanvas = "A4",
146
- n_consensus: int = 1,
157
+ model: str = FieldUnset,
158
+ temperature: float = FieldUnset,
159
+ modality: Modality = FieldUnset,
160
+ json_schema: Optional[Dict[str, Any]] = FieldUnset,
161
+ reasoning_effort: ChatCompletionReasoningEffort = FieldUnset,
162
+ image_resolution_dpi: int = FieldUnset,
163
+ browser_canvas: BrowserCanvas = FieldUnset,
164
+ n_consensus: int = FieldUnset,
147
165
  ) -> Iteration:
148
166
  """
149
167
  Create a new iteration for an evaluation.
@@ -14,9 +14,8 @@ from anthropic import Anthropic
14
14
  from openai import OpenAI
15
15
  from openai.types.chat.chat_completion_message_param import ChatCompletionMessageParam
16
16
  from pydantic import BaseModel
17
- from pydantic_core import PydanticUndefined
18
17
  from tqdm import tqdm
19
-
18
+ from ..types.standards import FieldUnset
20
19
  from .._resource import AsyncAPIResource, SyncAPIResource
21
20
  from ..utils.ai_models import assert_valid_model_extraction, get_provider_for_model
22
21
  from ..utils.chat import convert_to_anthropic_format, convert_to_openai_format, separate_messages
@@ -139,8 +138,8 @@ class Datasets(SyncAPIResource, BaseDatasetsMixin):
139
138
  json_schema: dict[str, Any] | Path | str,
140
139
  document_annotation_pairs_paths: list[dict[str, Path | str]],
141
140
  dataset_path: Path | str,
142
- image_resolution_dpi: int = PydanticUndefined, # type: ignore[assignment]
143
- browser_canvas: BrowserCanvas = PydanticUndefined, # type: ignore[assignment]
141
+ image_resolution_dpi: int = FieldUnset,
142
+ browser_canvas: BrowserCanvas = FieldUnset,
144
143
  modality: Modality = "native",
145
144
  ) -> None:
146
145
  """Save document-annotation pairs to a JSONL training set.
@@ -1,6 +1,6 @@
1
- from typing import Literal, Optional
1
+ from typing import Any, Literal, Optional
2
2
 
3
- from pydantic_core import PydanticUndefined
3
+ from ....types.standards import FieldUnset
4
4
 
5
5
  from ...._resource import AsyncAPIResource, SyncAPIResource
6
6
  from ....utils.ai_models import assert_valid_model_extraction
@@ -15,19 +15,23 @@ class EndpointsMixin:
15
15
  name: str,
16
16
  webhook_url: str,
17
17
  model: str = "gpt-4o-mini",
18
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
19
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
18
+ webhook_headers: dict[str, str] = FieldUnset,
19
+ need_validation: bool = FieldUnset,
20
20
  ) -> PreparedRequest:
21
21
  assert_valid_model_extraction(model)
22
22
 
23
- request = Endpoint(
24
- processor_id=processor_id,
25
- name=name,
26
- webhook_url=webhook_url,
27
- webhook_headers=webhook_headers,
28
- need_validation=need_validation,
29
- )
30
- return PreparedRequest(method="POST", url="/v1/processors/automations/endpoints", data=request.model_dump(mode="json"))
23
+ endpoint_dict: dict[str, Any] = {
24
+ 'processor_id': processor_id,
25
+ 'name': name,
26
+ 'webhook_url': webhook_url,
27
+ }
28
+ if webhook_headers is not FieldUnset:
29
+ endpoint_dict['webhook_headers'] = webhook_headers
30
+ if need_validation is not FieldUnset:
31
+ endpoint_dict['need_validation'] = need_validation
32
+
33
+ request = Endpoint(**endpoint_dict)
34
+ return PreparedRequest(method="POST", url="/v1/processors/automations/endpoints", data=request.model_dump(mode="json", exclude_unset=True))
31
35
 
32
36
  def prepare_list(
33
37
  self,
@@ -68,20 +72,26 @@ class EndpointsMixin:
68
72
  def prepare_update(
69
73
  self,
70
74
  endpoint_id: str,
71
- name: str = PydanticUndefined, # type: ignore[assignment]
72
- default_language: str = PydanticUndefined, # type: ignore[assignment]
73
- webhook_url: str = PydanticUndefined, # type: ignore[assignment]
74
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
75
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
75
+ name: str = FieldUnset,
76
+ default_language: str = FieldUnset,
77
+ webhook_url: str = FieldUnset,
78
+ webhook_headers: dict[str, str] = FieldUnset,
79
+ need_validation: bool = FieldUnset,
76
80
  ) -> PreparedRequest:
77
- request = UpdateEndpointRequest(
78
- name=name,
79
- default_language=default_language,
80
- webhook_url=webhook_url,
81
- webhook_headers=webhook_headers,
82
- need_validation=need_validation,
83
- )
84
- return PreparedRequest(method="PUT", url=f"/v1/processors/automations/endpoints/{endpoint_id}", data=request.model_dump(mode="json"))
81
+ update_dict: dict[str, Any] = {}
82
+ if name is not FieldUnset:
83
+ update_dict['name'] = name
84
+ if default_language is not FieldUnset:
85
+ update_dict['default_language'] = default_language
86
+ if webhook_url is not FieldUnset:
87
+ update_dict['webhook_url'] = webhook_url
88
+ if webhook_headers is not FieldUnset:
89
+ update_dict['webhook_headers'] = webhook_headers
90
+ if need_validation is not FieldUnset:
91
+ update_dict['need_validation'] = need_validation
92
+
93
+ request = UpdateEndpointRequest(**update_dict)
94
+ return PreparedRequest(method="PUT", url=f"/v1/processors/automations/endpoints/{endpoint_id}", data=request.model_dump(mode="json", exclude_unset=True))
85
95
 
86
96
  def prepare_delete(self, endpoint_id: str) -> PreparedRequest:
87
97
  return PreparedRequest(method="DELETE", url=f"/v1/processors/automations/endpoints/{endpoint_id}")
@@ -95,8 +105,8 @@ class Endpoints(SyncAPIResource, EndpointsMixin):
95
105
  processor_id: str,
96
106
  name: str,
97
107
  webhook_url: str,
98
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
99
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
108
+ webhook_headers: dict[str, str] = FieldUnset,
109
+ need_validation: bool = FieldUnset,
100
110
  ) -> Endpoint:
101
111
  """Create a new endpoint configuration.
102
112
 
@@ -168,11 +178,11 @@ class Endpoints(SyncAPIResource, EndpointsMixin):
168
178
  def update(
169
179
  self,
170
180
  endpoint_id: str,
171
- name: str = PydanticUndefined, # type: ignore[assignment]
172
- default_language: str = PydanticUndefined, # type: ignore[assignment]
173
- webhook_url: str = PydanticUndefined, # type: ignore[assignment]
174
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
175
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
181
+ name: str = FieldUnset,
182
+ default_language: str = FieldUnset,
183
+ webhook_url: str = FieldUnset,
184
+ webhook_headers: dict[str, str] = FieldUnset,
185
+ need_validation: bool = FieldUnset,
176
186
  ) -> Endpoint:
177
187
  """Update an endpoint configuration.
178
188
 
@@ -221,8 +231,8 @@ class AsyncEndpoints(AsyncAPIResource, EndpointsMixin):
221
231
  processor_id: str,
222
232
  name: str,
223
233
  webhook_url: str,
224
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
225
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
234
+ webhook_headers: dict[str, str] = FieldUnset,
235
+ need_validation: bool = FieldUnset,
226
236
  ) -> Endpoint:
227
237
  request = self.prepare_create(
228
238
  processor_id=processor_id,
@@ -258,11 +268,11 @@ class AsyncEndpoints(AsyncAPIResource, EndpointsMixin):
258
268
  async def update(
259
269
  self,
260
270
  endpoint_id: str,
261
- name: str = PydanticUndefined, # type: ignore[assignment]
262
- default_language: str = PydanticUndefined, # type: ignore[assignment]
263
- webhook_url: str = PydanticUndefined, # type: ignore[assignment]
264
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
265
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
271
+ name: str = FieldUnset,
272
+ default_language: str = FieldUnset,
273
+ webhook_url: str = FieldUnset,
274
+ webhook_headers: dict[str, str] = FieldUnset,
275
+ need_validation: bool = FieldUnset,
266
276
  ) -> Endpoint:
267
277
  request = self.prepare_update(
268
278
  endpoint_id=endpoint_id,
@@ -1,10 +1,8 @@
1
1
  from typing import Any, Literal, Optional
2
2
 
3
- from pydantic_core import PydanticUndefined
4
-
5
3
  from ...._resource import AsyncAPIResource, SyncAPIResource
6
4
  from ....types.automations.links import Link, ListLinks, UpdateLinkRequest
7
- from ....types.standards import PreparedRequest
5
+ from ....types.standards import PreparedRequest, FieldUnset
8
6
 
9
7
 
10
8
  class LinksMixin:
@@ -15,19 +13,24 @@ class LinksMixin:
15
13
  processor_id: str,
16
14
  name: str,
17
15
  webhook_url: str,
18
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
19
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
20
- password: str | None = PydanticUndefined, # type: ignore[assignment]
16
+ webhook_headers: dict[str, str] = FieldUnset,
17
+ need_validation: bool = FieldUnset,
18
+ password: str | None = FieldUnset,
21
19
  ) -> PreparedRequest:
22
- request = Link(
23
- processor_id=processor_id,
24
- name=name,
25
- webhook_url=webhook_url,
26
- webhook_headers=webhook_headers,
27
- need_validation=need_validation,
28
- password=password,
29
- )
30
- return PreparedRequest(method="POST", url=self.links_base_url, data=request.model_dump(mode="json"))
20
+ link_dict: dict[str, Any] = {
21
+ 'processor_id': processor_id,
22
+ 'name': name,
23
+ 'webhook_url': webhook_url,
24
+ }
25
+ if webhook_headers is not FieldUnset:
26
+ link_dict['webhook_headers'] = webhook_headers
27
+ if need_validation is not FieldUnset:
28
+ link_dict['need_validation'] = need_validation
29
+ if password is not FieldUnset:
30
+ link_dict['password'] = password
31
+
32
+ request = Link(**link_dict)
33
+ return PreparedRequest(method="POST", url=self.links_base_url, data=request.model_dump(mode="json", exclude_unset=True))
31
34
 
32
35
  def prepare_list(
33
36
  self,
@@ -66,19 +69,25 @@ class LinksMixin:
66
69
  def prepare_update(
67
70
  self,
68
71
  link_id: str,
69
- name: str = PydanticUndefined, # type: ignore[assignment]
70
- webhook_url: str = PydanticUndefined, # type: ignore[assignment]
71
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
72
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
73
- password: str | None = PydanticUndefined, # type: ignore[assignment]
72
+ name: str = FieldUnset,
73
+ webhook_url: str = FieldUnset,
74
+ webhook_headers: dict[str, str] = FieldUnset,
75
+ need_validation: bool = FieldUnset,
76
+ password: str | None = FieldUnset,
74
77
  ) -> PreparedRequest:
75
- request = UpdateLinkRequest(
76
- name=name,
77
- webhook_url=webhook_url,
78
- webhook_headers=webhook_headers,
79
- need_validation=need_validation,
80
- password=password,
81
- )
78
+ update_dict: dict[str, Any] = {}
79
+ if name is not FieldUnset:
80
+ update_dict['name'] = name
81
+ if webhook_url is not FieldUnset:
82
+ update_dict['webhook_url'] = webhook_url
83
+ if webhook_headers is not FieldUnset:
84
+ update_dict['webhook_headers'] = webhook_headers
85
+ if need_validation is not FieldUnset:
86
+ update_dict['need_validation'] = need_validation
87
+ if password is not FieldUnset:
88
+ update_dict['password'] = password
89
+
90
+ request = UpdateLinkRequest(**update_dict)
82
91
  return PreparedRequest(method="PUT", url=f"{self.links_base_url}/{link_id}", data=request.model_dump(mode="json", exclude_unset=True, exclude_defaults=True))
83
92
 
84
93
  def prepare_delete(self, link_id: str) -> PreparedRequest:
@@ -96,9 +105,9 @@ class Links(SyncAPIResource, LinksMixin):
96
105
  processor_id: str,
97
106
  name: str,
98
107
  webhook_url: str,
99
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
100
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
101
- password: str | None = PydanticUndefined, # type: ignore[assignment]
108
+ webhook_headers: dict[str, str] = FieldUnset,
109
+ need_validation: bool = FieldUnset,
110
+ password: str | None = FieldUnset,
102
111
  ) -> Link:
103
112
  """Create a new extraction link configuration.
104
113
 
@@ -174,11 +183,11 @@ class Links(SyncAPIResource, LinksMixin):
174
183
  def update(
175
184
  self,
176
185
  link_id: str,
177
- name: str = PydanticUndefined, # type: ignore[assignment]
178
- webhook_url: str = PydanticUndefined, # type: ignore[assignment]
179
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
180
- password: str | None = PydanticUndefined, # type: ignore[assignment]
181
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
186
+ name: str = FieldUnset,
187
+ webhook_url: str = FieldUnset,
188
+ webhook_headers: dict[str, str] = FieldUnset,
189
+ password: str | None = FieldUnset,
190
+ need_validation: bool = FieldUnset,
182
191
  ) -> Link:
183
192
  """Update an extraction link configuration.
184
193
 
@@ -235,9 +244,9 @@ class AsyncLinks(AsyncAPIResource, LinksMixin):
235
244
  processor_id: str,
236
245
  name: str,
237
246
  webhook_url: str,
238
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
239
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
240
- password: str | None = PydanticUndefined, # type: ignore[assignment]
247
+ webhook_headers: dict[str, str] = FieldUnset,
248
+ need_validation: bool = FieldUnset,
249
+ password: str | None = FieldUnset,
241
250
  ) -> Link:
242
251
  request = self.prepare_create(
243
252
  processor_id=processor_id,
@@ -272,11 +281,11 @@ class AsyncLinks(AsyncAPIResource, LinksMixin):
272
281
  async def update(
273
282
  self,
274
283
  link_id: str,
275
- name: str = PydanticUndefined, # type: ignore[assignment]
276
- webhook_url: str = PydanticUndefined, # type: ignore[assignment]
277
- webhook_headers: dict[str, str] = PydanticUndefined, # type: ignore[assignment]
278
- password: str | None = PydanticUndefined, # type: ignore[assignment]
279
- need_validation: bool = PydanticUndefined, # type: ignore[assignment]
284
+ name: str = FieldUnset,
285
+ webhook_url: str = FieldUnset,
286
+ webhook_headers: dict[str, str] = FieldUnset,
287
+ password: str | None = FieldUnset,
288
+ need_validation: bool = FieldUnset,
280
289
  ) -> Link:
281
290
  request = self.prepare_update(
282
291
  link_id=link_id,