exa-py 1.15.1__py3-none-any.whl → 1.15.3__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.

Potentially problematic release.


This version of exa-py might be problematic. Click here for more details.

exa_py/api.py CHANGED
@@ -23,6 +23,7 @@ from typing import (
23
23
  overload,
24
24
  )
25
25
 
26
+ from .websets import AsyncWebsetsClient
26
27
  import httpx
27
28
  import requests
28
29
  from openai import OpenAI
@@ -2413,6 +2414,8 @@ class AsyncExa(Exa):
2413
2414
  super().__init__(api_key, api_base)
2414
2415
  # Override the synchronous ResearchClient with its async counterpart.
2415
2416
  self.research = AsyncResearchClient(self)
2417
+ # Override the synchronous WebsetsClient with its async counterpart.
2418
+ self.websets = AsyncWebsetsClient(self)
2416
2419
  self._client = None
2417
2420
 
2418
2421
  @property
@@ -45,6 +45,7 @@ class AsyncResearchTyped(Generic[T]):
45
45
  self.created_at = research.created_at
46
46
  self.model = research.model
47
47
  self.instructions = research.instructions
48
+ self.output_schema = research.output_schema
48
49
  if hasattr(research, "events"):
49
50
  self.events = research.events
50
51
  if hasattr(research, "output"):
exa_py/research/models.py CHANGED
@@ -197,6 +197,9 @@ class ResearchBaseDto(BaseModel):
197
197
  instructions: Annotated[
198
198
  str, Field(description="The instructions given to this research request")
199
199
  ]
200
+ output_schema: Annotated[Optional[Dict[str, Any]], Field(alias="outputSchema")] = (
201
+ None
202
+ )
200
203
 
201
204
 
202
205
  class ResearchPendingDto(ResearchBaseDto):
@@ -210,6 +213,9 @@ class ResearchRunningDto(ResearchBaseDto):
210
213
 
211
214
  class ResearchCompletedDto(ResearchBaseDto):
212
215
  status: Literal["completed"]
216
+ finished_at: Annotated[
217
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
218
+ ]
213
219
  events: Optional[List[ResearchEvent]] = None
214
220
  output: ResearchOutput
215
221
  cost_dollars: Annotated[CostDollars, Field(alias="costDollars")]
@@ -217,11 +223,17 @@ class ResearchCompletedDto(ResearchBaseDto):
217
223
 
218
224
  class ResearchCanceledDto(ResearchBaseDto):
219
225
  status: Literal["canceled"]
226
+ finished_at: Annotated[
227
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
228
+ ]
220
229
  events: Optional[List[ResearchEvent]] = None
221
230
 
222
231
 
223
232
  class ResearchFailedDto(ResearchBaseDto):
224
233
  status: Literal["failed"]
234
+ finished_at: Annotated[
235
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
236
+ ]
225
237
  events: Optional[List[ResearchEvent]] = None
226
238
  error: Annotated[
227
239
  str, Field(description="A message indicating why the request failed")
@@ -1,9 +1,10 @@
1
- from .client import WebsetsClient
1
+ from .client import WebsetsClient, AsyncWebsetsClient
2
2
  from .imports import ImportsClient
3
3
  from .events import EventsClient
4
4
 
5
5
  __all__ = [
6
6
  "WebsetsClient",
7
+ "AsyncWebsetsClient",
7
8
  "ImportsClient",
8
9
  "EventsClient",
9
10
  ]
@@ -0,0 +1,153 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from typing import List, Optional, Literal, Dict, Any, Union
5
+
6
+ from .types import (
7
+ Webset,
8
+ ListWebsetsResponse,
9
+ GetWebsetResponse,
10
+ UpdateWebsetRequest,
11
+ WebsetStatus,
12
+ CreateWebsetParameters,
13
+ PreviewWebsetParameters,
14
+ PreviewWebsetResponse,
15
+ )
16
+ from .core.async_base import WebsetsAsyncBaseClient
17
+ from .async_items.client import AsyncWebsetItemsClient
18
+ from .async_searches.client import AsyncWebsetSearchesClient
19
+ from .async_enrichments.client import AsyncWebsetEnrichmentsClient
20
+ from .async_webhooks.client import AsyncWebsetWebhooksClient
21
+ from .async_monitors.client import AsyncMonitorsClient
22
+ from .async_imports.client import AsyncImportsClient
23
+ from .async_events.client import AsyncEventsClient
24
+
25
+ class AsyncWebsetsClient(WebsetsAsyncBaseClient):
26
+ """Async client for managing Websets."""
27
+
28
+ def __init__(self, client):
29
+ super().__init__(client)
30
+ self.items = AsyncWebsetItemsClient(client)
31
+ self.searches = AsyncWebsetSearchesClient(client)
32
+ self.enrichments = AsyncWebsetEnrichmentsClient(client)
33
+ self.webhooks = AsyncWebsetWebhooksClient(client)
34
+ self.monitors = AsyncMonitorsClient(client)
35
+ self.imports = AsyncImportsClient(client)
36
+ self.events = AsyncEventsClient(client)
37
+
38
+ async def create(self, params: Union[Dict[str, Any], CreateWebsetParameters]) -> Webset:
39
+ """Create a new Webset.
40
+
41
+ Args:
42
+ params (CreateWebsetParameters): The parameters for creating a webset.
43
+
44
+ Returns:
45
+ Webset: The created webset.
46
+ """
47
+ response = await self.request("/v0/websets", data=params)
48
+ return Webset.model_validate(response)
49
+
50
+ async def preview(self, params: Union[Dict[str, Any], PreviewWebsetParameters]) -> PreviewWebsetResponse:
51
+ """Preview a Webset before creating it.
52
+
53
+ Args:
54
+ params (PreviewWebsetParameters): The parameters for previewing a webset.
55
+
56
+ Returns:
57
+ PreviewWebsetResponse: The preview results.
58
+ """
59
+ response = await self.request("/v0/websets/preview", data=params)
60
+ return PreviewWebsetResponse.model_validate(response)
61
+
62
+ async def get(self, id: str, *, expand: Optional[List[Literal["items"]]] = None) -> GetWebsetResponse:
63
+ """Get a Webset by ID.
64
+
65
+ Args:
66
+ id (str): The id or externalId of the Webset.
67
+ expand (List[Literal["items"]], optional): Expand items in the response.
68
+
69
+ Returns:
70
+ GetWebsetResponse: The retrieved webset.
71
+ """
72
+ params = {}
73
+ if expand is not None:
74
+ params["expand"] = expand
75
+
76
+ response = await self.request(f"/v0/websets/{id}", params=params, method="GET")
77
+ return GetWebsetResponse.model_validate(response)
78
+
79
+ async def list(self, *, cursor: Optional[str] = None, limit: Optional[int] = None) -> ListWebsetsResponse:
80
+ """List all Websets.
81
+
82
+ Args:
83
+ cursor (str, optional): The cursor to paginate through the results.
84
+ limit (int, optional): The number of results to return (max 200).
85
+
86
+ Returns:
87
+ ListWebsetsResponse: List of websets.
88
+ """
89
+ params = {k: v for k, v in {"cursor": cursor, "limit": limit}.items() if v is not None}
90
+ response = await self.request("/v0/websets", params=params, method="GET")
91
+ return ListWebsetsResponse.model_validate(response)
92
+
93
+ async def update(self, id: str, params: Union[Dict[str, Any], UpdateWebsetRequest]) -> Webset:
94
+ """Update a Webset.
95
+
96
+ Args:
97
+ id (str): The id or externalId of the Webset.
98
+ params (UpdateWebsetRequest): The parameters for updating a webset.
99
+
100
+ Returns:
101
+ Webset: The updated webset.
102
+ """
103
+ response = await self.request(f"/v0/websets/{id}", data=params, method="POST")
104
+ return Webset.model_validate(response)
105
+
106
+ async def delete(self, id: str) -> Webset:
107
+ """Delete a Webset.
108
+
109
+ Args:
110
+ id (str): The id or externalId of the Webset.
111
+
112
+ Returns:
113
+ Webset: The deleted webset.
114
+ """
115
+ response = await self.request(f"/v0/websets/{id}", method="DELETE")
116
+ return Webset.model_validate(response)
117
+
118
+ async def cancel(self, id: str) -> Webset:
119
+ """Cancel a running Webset.
120
+
121
+ Args:
122
+ id (str): The id or externalId of the Webset.
123
+
124
+ Returns:
125
+ Webset: The canceled webset.
126
+ """
127
+ response = await self.request(f"/v0/websets/{id}/cancel", method="POST")
128
+ return Webset.model_validate(response)
129
+
130
+ async def wait_until_idle(self, id: str, *, timeout: int = 3600, poll_interval: int = 5) -> Webset:
131
+ """Wait until a Webset is idle.
132
+
133
+ Args:
134
+ id (str): The id or externalId of the Webset.
135
+ timeout (int, optional): Maximum time to wait in seconds. Defaults to 3600.
136
+ poll_interval (int, optional): Time to wait between polls in seconds. Defaults to 5.
137
+
138
+ Returns:
139
+ Webset: The webset once it's idle.
140
+
141
+ Raises:
142
+ TimeoutError: If the webset does not become idle within the timeout period.
143
+ """
144
+ start_time = asyncio.get_event_loop().time()
145
+ while True:
146
+ webset = await self.get(id)
147
+ if webset.status == WebsetStatus.idle.value:
148
+ return webset
149
+
150
+ if asyncio.get_event_loop().time() - start_time > timeout:
151
+ raise TimeoutError(f"Webset {id} did not become idle within {timeout} seconds")
152
+
153
+ await asyncio.sleep(poll_interval)
exa_py/websets/client.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
+ import asyncio
4
5
  from typing import List, Optional, Literal, Dict, Any, Union
5
6
 
6
7
  from .types import (
@@ -14,13 +15,14 @@ from .types import (
14
15
  PreviewWebsetResponse,
15
16
  )
16
17
  from .core.base import WebsetsBaseClient
17
- from .items import WebsetItemsClient
18
- from .searches import WebsetSearchesClient
19
- from .enrichments import WebsetEnrichmentsClient
20
- from .webhooks import WebsetWebhooksClient
21
- from .monitors import MonitorsClient
22
- from .imports import ImportsClient
23
- from .events import EventsClient
18
+ from .core.async_base import WebsetsAsyncBaseClient
19
+ from .items import WebsetItemsClient, AsyncWebsetItemsClient
20
+ from .searches import WebsetSearchesClient, AsyncWebsetSearchesClient
21
+ from .enrichments import WebsetEnrichmentsClient, AsyncWebsetEnrichmentsClient
22
+ from .webhooks import WebsetWebhooksClient, AsyncWebsetWebhooksClient
23
+ from .monitors import MonitorsClient, AsyncMonitorsClient
24
+ from .imports import ImportsClient, AsyncImportsClient
25
+ from .events import EventsClient, AsyncEventsClient
24
26
 
25
27
  class WebsetsClient(WebsetsBaseClient):
26
28
  """Client for managing Websets."""
@@ -154,3 +156,134 @@ class WebsetsClient(WebsetsBaseClient):
154
156
  raise TimeoutError(f"Webset {id} did not become idle within {timeout} seconds")
155
157
 
156
158
  time.sleep(poll_interval)
159
+
160
+
161
+ class AsyncWebsetsClient(WebsetsAsyncBaseClient):
162
+ """Async client for managing Websets."""
163
+
164
+ def __init__(self, client):
165
+ super().__init__(client)
166
+ self.items = AsyncWebsetItemsClient(client)
167
+ self.searches = AsyncWebsetSearchesClient(client)
168
+ self.enrichments = AsyncWebsetEnrichmentsClient(client)
169
+ self.webhooks = AsyncWebsetWebhooksClient(client)
170
+ self.monitors = AsyncMonitorsClient(client)
171
+ self.imports = AsyncImportsClient(client)
172
+ self.events = AsyncEventsClient(client)
173
+
174
+ async def create(self, params: Union[Dict[str, Any], CreateWebsetParameters]) -> Webset:
175
+ """Create a new Webset.
176
+
177
+ Args:
178
+ params (CreateWebsetParameters): The parameters for creating a webset.
179
+
180
+ Returns:
181
+ Webset: The created webset.
182
+ """
183
+ response = await self.request("/v0/websets", data=params)
184
+ return Webset.model_validate(response)
185
+
186
+ async def preview(self, params: Union[Dict[str, Any], PreviewWebsetParameters]) -> PreviewWebsetResponse:
187
+ """Preview a Webset before creating it.
188
+
189
+ Args:
190
+ params (PreviewWebsetParameters): The parameters for previewing a webset.
191
+
192
+ Returns:
193
+ PreviewWebsetResponse: The preview results.
194
+ """
195
+ response = await self.request("/v0/websets/preview", data=params)
196
+ return PreviewWebsetResponse.model_validate(response)
197
+
198
+ async def get(self, id: str, *, expand: Optional[List[Literal["items"]]] = None) -> GetWebsetResponse:
199
+ """Get a Webset by ID.
200
+
201
+ Args:
202
+ id (str): The id or externalId of the Webset.
203
+ expand (List[Literal["items"]], optional): Expand items in the response.
204
+
205
+ Returns:
206
+ GetWebsetResponse: The retrieved webset.
207
+ """
208
+ params = {}
209
+ if expand is not None:
210
+ params["expand"] = expand
211
+
212
+ response = await self.request(f"/v0/websets/{id}", params=params, method="GET")
213
+ return GetWebsetResponse.model_validate(response)
214
+
215
+ async def list(self, *, cursor: Optional[str] = None, limit: Optional[int] = None) -> ListWebsetsResponse:
216
+ """List all Websets.
217
+
218
+ Args:
219
+ cursor (str, optional): The cursor to paginate through the results.
220
+ limit (int, optional): The number of results to return (max 200).
221
+
222
+ Returns:
223
+ ListWebsetsResponse: List of websets.
224
+ """
225
+ params = {k: v for k, v in {"cursor": cursor, "limit": limit}.items() if v is not None}
226
+ response = await self.request("/v0/websets", params=params, method="GET")
227
+ return ListWebsetsResponse.model_validate(response)
228
+
229
+ async def update(self, id: str, params: Union[Dict[str, Any], UpdateWebsetRequest]) -> Webset:
230
+ """Update a Webset.
231
+
232
+ Args:
233
+ id (str): The id or externalId of the Webset.
234
+ params (UpdateWebsetRequest): The parameters for updating a webset.
235
+
236
+ Returns:
237
+ Webset: The updated webset.
238
+ """
239
+ response = await self.request(f"/v0/websets/{id}", data=params, method="POST")
240
+ return Webset.model_validate(response)
241
+
242
+ async def delete(self, id: str) -> Webset:
243
+ """Delete a Webset.
244
+
245
+ Args:
246
+ id (str): The id or externalId of the Webset.
247
+
248
+ Returns:
249
+ Webset: The deleted webset.
250
+ """
251
+ response = await self.request(f"/v0/websets/{id}", method="DELETE")
252
+ return Webset.model_validate(response)
253
+
254
+ async def cancel(self, id: str) -> Webset:
255
+ """Cancel a running Webset.
256
+
257
+ Args:
258
+ id (str): The id or externalId of the Webset.
259
+
260
+ Returns:
261
+ Webset: The canceled webset.
262
+ """
263
+ response = await self.request(f"/v0/websets/{id}/cancel", method="POST")
264
+ return Webset.model_validate(response)
265
+
266
+ async def wait_until_idle(self, id: str, *, timeout: int = 3600, poll_interval: int = 5) -> Webset:
267
+ """Wait until a Webset is idle.
268
+
269
+ Args:
270
+ id (str): The id or externalId of the Webset.
271
+ timeout (int, optional): Maximum time to wait in seconds. Defaults to 3600.
272
+ poll_interval (int, optional): Time to wait between polls in seconds. Defaults to 5.
273
+
274
+ Returns:
275
+ Webset: The webset once it's idle.
276
+
277
+ Raises:
278
+ TimeoutError: If the webset does not become idle within the timeout period.
279
+ """
280
+ start_time = asyncio.get_event_loop().time()
281
+ while True:
282
+ webset = await self.get(id)
283
+ if webset.status == WebsetStatus.idle.value:
284
+ return webset
285
+
286
+ if asyncio.get_event_loop().time() - start_time > timeout:
287
+ raise TimeoutError(f"Webset {id} did not become idle within {timeout} seconds")
288
+
289
+ await asyncio.sleep(poll_interval)
@@ -1,9 +1,11 @@
1
1
  from ..types import *
2
+ from .base import WebsetsBaseClient
3
+ from .async_base import WebsetsAsyncBaseClient
2
4
  import sys
3
5
 
4
6
  # Get all public names from model module that don't start with underscore
5
7
  model_module = sys.modules[__name__]
6
- __all__ = ['WebsetsBaseClient', 'ExaBaseModel'] + [
8
+ __all__ = ['WebsetsBaseClient', 'WebsetsAsyncBaseClient', 'ExaBaseModel'] + [
7
9
  name for name in dir(model_module)
8
- if not name.startswith('_') and name not in ('WebsetsBaseClient', 'ExaBaseModel')
10
+ if not name.startswith('_') and name not in ('WebsetsBaseClient', 'WebsetsAsyncBaseClient', 'ExaBaseModel')
9
11
  ]
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import asyncio
5
+ from typing import Any, Dict, Optional, Type, TypeVar, Union
6
+
7
+ from .base import ExaBaseModel
8
+
9
+ # Generic type for any ExaBaseModel
10
+ ModelT = TypeVar('ModelT', bound=ExaBaseModel)
11
+
12
+
13
+
14
+ class WebsetsAsyncBaseClient:
15
+ base_url: str
16
+
17
+ """Base async client for Exa API resources."""
18
+
19
+ def __init__(self, client):
20
+ """Initialize the async client.
21
+
22
+ Args:
23
+ client: The parent AsyncExa client.
24
+ """
25
+ self._client = client
26
+
27
+ def _prepare_data(self, data: Union[Dict[str, Any], ExaBaseModel, str], model_class: Optional[Type[ModelT]] = None) -> Union[Dict[str, Any], str]:
28
+ """Prepare data for API request, converting dict to model if needed.
29
+
30
+ Args:
31
+ data: Either a dictionary, model instance, or string
32
+ model_class: The model class to use if data is a dictionary
33
+
34
+ Returns:
35
+ Dictionary prepared for API request or string if string data was provided
36
+ """
37
+ if isinstance(data, str):
38
+ # Return string as is
39
+ return data
40
+ elif isinstance(data, dict) and model_class:
41
+ # Convert dict to model instance
42
+ model_instance = model_class.model_validate(data)
43
+ return model_instance.model_dump(mode='json', by_alias=True, exclude_none=True)
44
+ elif isinstance(data, ExaBaseModel):
45
+ # Use model's dump method
46
+ return data.model_dump(mode='json', by_alias=True, exclude_none=True)
47
+ elif isinstance(data, dict):
48
+ # Use dict directly
49
+ return data
50
+ else:
51
+ raise TypeError(f"Expected dict, ExaBaseModel, or str, got {type(data)}")
52
+
53
+ async def request(self, endpoint: str, data: Optional[Union[Dict[str, Any], ExaBaseModel, str]] = None,
54
+ method: str = "POST", params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
55
+ """Make an async request to the Exa API.
56
+
57
+ Args:
58
+ endpoint (str): The API endpoint to request.
59
+ data (Union[Dict[str, Any], ExaBaseModel, str], optional): The request data. Can be a dictionary, model instance, or string. Defaults to None.
60
+ method (str, optional): The HTTP method. Defaults to "POST".
61
+ params (Dict[str, Any], optional): The query parameters. Defaults to None.
62
+
63
+ Returns:
64
+ Dict[str, Any]: The API response.
65
+ """
66
+ if isinstance(data, str):
67
+ # If data is a string, pass it as is
68
+ pass
69
+ elif data is not None and isinstance(data, ExaBaseModel):
70
+ # If data is a model instance, convert it to a dict
71
+ data = data.model_dump(mode='json', by_alias=True, exclude_none=True)
72
+
73
+ # Ensure proper URL construction by removing leading slash from endpoint if present
74
+ if endpoint.startswith("/"):
75
+ endpoint = endpoint[1:]
76
+
77
+ return await self._client.async_request("/websets/" + endpoint, data=data, method=method, params=params)
@@ -1,3 +1,3 @@
1
- from .client import WebsetEnrichmentsClient
1
+ from .client import WebsetEnrichmentsClient, AsyncWebsetEnrichmentsClient
2
2
 
3
- __all__ = ["WebsetEnrichmentsClient"]
3
+ __all__ = ["WebsetEnrichmentsClient", "AsyncWebsetEnrichmentsClient"]
@@ -8,6 +8,7 @@ from ..types import (
8
8
  WebsetEnrichment,
9
9
  )
10
10
  from ..core.base import WebsetsBaseClient
11
+ from ..core.async_base import WebsetsAsyncBaseClient
11
12
 
12
13
  class WebsetEnrichmentsClient(WebsetsBaseClient):
13
14
  """Client for managing Webset Enrichments."""
@@ -79,4 +80,77 @@ class WebsetEnrichmentsClient(WebsetsBaseClient):
79
80
  WebsetEnrichment: The canceled enrichment.
80
81
  """
81
82
  response = self.request(f"/v0/websets/{webset_id}/enrichments/{id}/cancel", method="POST")
83
+ return WebsetEnrichment.model_validate(response)
84
+
85
+
86
+ class AsyncWebsetEnrichmentsClient(WebsetsAsyncBaseClient):
87
+ """Async client for managing Webset Enrichments."""
88
+
89
+ def __init__(self, client):
90
+ super().__init__(client)
91
+
92
+ async def create(self, webset_id: str, params: Union[Dict[str, Any], CreateEnrichmentParameters]) -> WebsetEnrichment:
93
+ """Create an Enrichment for a Webset.
94
+
95
+ Args:
96
+ webset_id (str): The id of the Webset.
97
+ params (CreateEnrichmentParameters): The parameters for creating an enrichment.
98
+
99
+ Returns:
100
+ WebsetEnrichment: The created enrichment.
101
+ """
102
+ response = await self.request(f"/v0/websets/{webset_id}/enrichments", data=params)
103
+ return WebsetEnrichment.model_validate(response)
104
+
105
+ async def get(self, webset_id: str, id: str) -> WebsetEnrichment:
106
+ """Get an Enrichment by ID.
107
+
108
+ Args:
109
+ webset_id (str): The id of the Webset.
110
+ id (str): The id of the Enrichment.
111
+
112
+ Returns:
113
+ WebsetEnrichment: The retrieved enrichment.
114
+ """
115
+ response = await self.request(f"/v0/websets/{webset_id}/enrichments/{id}", method="GET")
116
+ return WebsetEnrichment.model_validate(response)
117
+
118
+ async def update(self, webset_id: str, id: str, params: Union[Dict[str, Any], UpdateEnrichmentParameters]) -> WebsetEnrichment:
119
+ """Update an Enrichment.
120
+
121
+ Args:
122
+ webset_id (str): The id of the Webset.
123
+ id (str): The id of the Enrichment.
124
+ params (UpdateEnrichmentParameters): The parameters for updating an enrichment.
125
+
126
+ Returns:
127
+ WebsetEnrichment: The updated enrichment.
128
+ """
129
+ response = await self.request(f"/v0/websets/{webset_id}/enrichments/{id}", data=params, method="PATCH")
130
+ return WebsetEnrichment.model_validate(response)
131
+
132
+ async def delete(self, webset_id: str, id: str) -> WebsetEnrichment:
133
+ """Delete an Enrichment.
134
+
135
+ Args:
136
+ webset_id (str): The id of the Webset.
137
+ id (str): The id of the Enrichment.
138
+
139
+ Returns:
140
+ WebsetEnrichment: The deleted enrichment.
141
+ """
142
+ response = await self.request(f"/v0/websets/{webset_id}/enrichments/{id}", method="DELETE")
143
+ return WebsetEnrichment.model_validate(response)
144
+
145
+ async def cancel(self, webset_id: str, id: str) -> WebsetEnrichment:
146
+ """Cancel a running Enrichment.
147
+
148
+ Args:
149
+ webset_id (str): The id of the Webset.
150
+ id (str): The id of the Enrichment.
151
+
152
+ Returns:
153
+ WebsetEnrichment: The canceled enrichment.
154
+ """
155
+ response = await self.request(f"/v0/websets/{webset_id}/enrichments/{id}/cancel", method="POST")
82
156
  return WebsetEnrichment.model_validate(response)
@@ -1,3 +1,3 @@
1
- from .client import EventsClient
1
+ from .client import EventsClient, AsyncEventsClient
2
2
 
3
- __all__ = ["EventsClient"]
3
+ __all__ = ["EventsClient", "AsyncEventsClient"]
@@ -24,6 +24,7 @@ from ..types import (
24
24
  MonitorRunCompletedEvent,
25
25
  )
26
26
  from ..core.base import WebsetsBaseClient
27
+ from ..core.async_base import WebsetsAsyncBaseClient
27
28
 
28
29
  # Type alias for all event types
29
30
  Event = Union[
@@ -112,6 +113,87 @@ class EventsClient(WebsetsBaseClient):
112
113
  'monitor.run.completed': MonitorRunCompletedEvent,
113
114
  }
114
115
 
116
+ event_class = event_type_map.get(event_type)
117
+ if event_class:
118
+ return event_class.model_validate(response)
119
+ else:
120
+ # Fallback - try each type until one validates
121
+ # This shouldn't happen in normal operation
122
+ for event_class in event_type_map.values():
123
+ try:
124
+ return event_class.model_validate(response)
125
+ except Exception:
126
+ continue
127
+
128
+ raise ValueError(f"Unknown event type: {event_type}")
129
+
130
+
131
+ class AsyncEventsClient(WebsetsAsyncBaseClient):
132
+ """Async client for managing Events."""
133
+
134
+ def __init__(self, client):
135
+ super().__init__(client)
136
+
137
+ async def list(self, *, cursor: Optional[str] = None, limit: Optional[int] = None,
138
+ types: Optional[List[EventType]] = None) -> ListEventsResponse:
139
+ """List all Events.
140
+
141
+ Args:
142
+ cursor (str, optional): The cursor to paginate through the results.
143
+ limit (int, optional): The number of results to return.
144
+ types (List[EventType], optional): The types of events to filter by.
145
+
146
+ Returns:
147
+ ListEventsResponse: List of events.
148
+ """
149
+ params = {}
150
+ if cursor is not None:
151
+ params["cursor"] = cursor
152
+ if limit is not None:
153
+ params["limit"] = limit
154
+ if types is not None:
155
+ # Convert EventType enums to their string values
156
+ params["types"] = [t.value if hasattr(t, 'value') else t for t in types]
157
+
158
+ response = await self.request("/v0/events", params=params, method="GET")
159
+ return ListEventsResponse.model_validate(response)
160
+
161
+ async def get(self, id: str) -> Event:
162
+ """Get an Event by ID.
163
+
164
+ Args:
165
+ id (str): The ID of the Event.
166
+
167
+ Returns:
168
+ Event: The retrieved event.
169
+ """
170
+ response = await self.request(f"/v0/events/{id}", method="GET")
171
+
172
+ # The response should contain a 'type' field that helps us determine
173
+ # which specific event class to use for validation
174
+ event_type = response.get('type')
175
+
176
+ # Map event types to their corresponding classes
177
+ event_type_map = {
178
+ 'webset.created': WebsetCreatedEvent,
179
+ 'webset.deleted': WebsetDeletedEvent,
180
+ 'webset.idle': WebsetIdleEvent,
181
+ 'webset.paused': WebsetPausedEvent,
182
+ 'webset.item.created': WebsetItemCreatedEvent,
183
+ 'webset.item.enriched': WebsetItemEnrichedEvent,
184
+ 'webset.search.created': WebsetSearchCreatedEvent,
185
+ 'webset.search.updated': WebsetSearchUpdatedEvent,
186
+ 'webset.search.canceled': WebsetSearchCanceledEvent,
187
+ 'webset.search.completed': WebsetSearchCompletedEvent,
188
+ 'import.created': ImportCreatedEvent,
189
+ 'import.completed': ImportCompletedEvent,
190
+ 'monitor.created': MonitorCreatedEvent,
191
+ 'monitor.updated': MonitorUpdatedEvent,
192
+ 'monitor.deleted': MonitorDeletedEvent,
193
+ 'monitor.run.created': MonitorRunCreatedEvent,
194
+ 'monitor.run.completed': MonitorRunCompletedEvent,
195
+ }
196
+
115
197
  event_class = event_type_map.get(event_type)
116
198
  if event_class:
117
199
  return event_class.model_validate(response)