exa-py 1.10.0__tar.gz → 1.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of exa-py might be problematic. Click here for more details.
- {exa_py-1.10.0 → exa_py-1.12.0}/PKG-INFO +1 -1
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/api.py +33 -7
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/client.py +23 -17
- exa_py-1.12.0/exa_py/websets/core/base.py +96 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/enrichments/client.py +4 -2
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/searches/client.py +4 -2
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/types.py +92 -30
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/webhooks/client.py +44 -6
- {exa_py-1.10.0 → exa_py-1.12.0}/pyproject.toml +2 -2
- exa_py-1.10.0/exa_py/websets/core/base.py +0 -41
- {exa_py-1.10.0 → exa_py-1.12.0}/README.md +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/__init__.py +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/py.typed +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/utils.py +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/__init__.py +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/_generator/pydantic/BaseModel.jinja2 +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/core/__init__.py +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/enrichments/__init__.py +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/items/__init__.py +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/items/client.py +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/searches/__init__.py +0 -0
- {exa_py-1.10.0 → exa_py-1.12.0}/exa_py/websets/webhooks/__init__.py +0 -0
|
@@ -7,6 +7,7 @@ import re
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from functools import wraps
|
|
9
9
|
from typing import (
|
|
10
|
+
Any,
|
|
10
11
|
Callable,
|
|
11
12
|
Dict,
|
|
12
13
|
Generic,
|
|
@@ -36,6 +37,7 @@ from exa_py.utils import (
|
|
|
36
37
|
maybe_get_query,
|
|
37
38
|
)
|
|
38
39
|
from .websets import WebsetsClient
|
|
40
|
+
from .websets.core.base import ExaJSONEncoder
|
|
39
41
|
|
|
40
42
|
is_beta = os.getenv("IS_BETA") == "True"
|
|
41
43
|
|
|
@@ -840,7 +842,7 @@ class Exa:
|
|
|
840
842
|
self,
|
|
841
843
|
api_key: Optional[str],
|
|
842
844
|
base_url: str = "https://api.exa.ai",
|
|
843
|
-
user_agent: str = "exa-py 1.
|
|
845
|
+
user_agent: str = "exa-py 1.12.0",
|
|
844
846
|
):
|
|
845
847
|
"""Initialize the Exa client with the provided API key and optional base URL and user agent.
|
|
846
848
|
|
|
@@ -857,17 +859,17 @@ class Exa:
|
|
|
857
859
|
"API key must be provided as an argument or in EXA_API_KEY environment variable"
|
|
858
860
|
)
|
|
859
861
|
self.base_url = base_url
|
|
860
|
-
self.headers = {"x-api-key": api_key, "User-Agent": user_agent}
|
|
862
|
+
self.headers = {"x-api-key": api_key, "User-Agent": user_agent, "Content-Type": "application/json"}
|
|
861
863
|
self.websets = WebsetsClient(self)
|
|
862
864
|
|
|
863
|
-
def request(self, endpoint: str, data=None, method="POST", params=None):
|
|
865
|
+
def request(self, endpoint: str, data: Optional[Union[Dict[str, Any], str]] = None, method: str = "POST", params: Optional[Dict[str, Any]] = None) -> Union[Dict[str, Any], requests.Response]:
|
|
864
866
|
"""Send a request to the Exa API, optionally streaming if data['stream'] is True.
|
|
865
867
|
|
|
866
868
|
Args:
|
|
867
869
|
endpoint (str): The API endpoint (path).
|
|
868
870
|
data (dict, optional): The JSON payload to send. Defaults to None.
|
|
869
871
|
method (str, optional): The HTTP method to use. Defaults to "POST".
|
|
870
|
-
params (
|
|
872
|
+
params (Dict[str, Any], optional): Query parameters to include. Defaults to None.
|
|
871
873
|
|
|
872
874
|
Returns:
|
|
873
875
|
Union[dict, requests.Response]: If streaming, returns the Response object.
|
|
@@ -876,9 +878,20 @@ class Exa:
|
|
|
876
878
|
Raises:
|
|
877
879
|
ValueError: If the request fails (non-200 status code).
|
|
878
880
|
"""
|
|
881
|
+
# Handle the case when data is a string
|
|
882
|
+
if isinstance(data, str):
|
|
883
|
+
# Use the string directly as the data payload
|
|
884
|
+
json_data = data
|
|
885
|
+
else:
|
|
886
|
+
# Otherwise, serialize the dictionary to JSON if it exists
|
|
887
|
+
json_data = json.dumps(data, cls=ExaJSONEncoder) if data else None
|
|
888
|
+
|
|
879
889
|
if data and data.get("stream"):
|
|
880
890
|
res = requests.post(
|
|
881
|
-
self.base_url + endpoint,
|
|
891
|
+
self.base_url + endpoint,
|
|
892
|
+
data=json_data,
|
|
893
|
+
headers=self.headers,
|
|
894
|
+
stream=True
|
|
882
895
|
)
|
|
883
896
|
return res
|
|
884
897
|
|
|
@@ -888,11 +901,15 @@ class Exa:
|
|
|
888
901
|
)
|
|
889
902
|
elif method.upper() == "POST":
|
|
890
903
|
res = requests.post(
|
|
891
|
-
self.base_url + endpoint,
|
|
904
|
+
self.base_url + endpoint,
|
|
905
|
+
data=json_data,
|
|
906
|
+
headers=self.headers
|
|
892
907
|
)
|
|
893
908
|
elif method.upper() == "PATCH":
|
|
894
909
|
res = requests.patch(
|
|
895
|
-
self.base_url + endpoint,
|
|
910
|
+
self.base_url + endpoint,
|
|
911
|
+
data=json_data,
|
|
912
|
+
headers=self.headers
|
|
896
913
|
)
|
|
897
914
|
elif method.upper() == "DELETE":
|
|
898
915
|
res = requests.delete(
|
|
@@ -1826,6 +1843,7 @@ class Exa:
|
|
|
1826
1843
|
*,
|
|
1827
1844
|
stream: Optional[bool] = False,
|
|
1828
1845
|
text: Optional[bool] = False,
|
|
1846
|
+
system_prompt: Optional[str] = None,
|
|
1829
1847
|
model: Optional[Literal["exa", "exa-pro"]] = None,
|
|
1830
1848
|
) -> Union[AnswerResponse, StreamAnswerResponse]: ...
|
|
1831
1849
|
|
|
@@ -1835,6 +1853,7 @@ class Exa:
|
|
|
1835
1853
|
*,
|
|
1836
1854
|
stream: Optional[bool] = False,
|
|
1837
1855
|
text: Optional[bool] = False,
|
|
1856
|
+
system_prompt: Optional[str] = None,
|
|
1838
1857
|
model: Optional[Literal["exa", "exa-pro"]] = None,
|
|
1839
1858
|
) -> Union[AnswerResponse, StreamAnswerResponse]:
|
|
1840
1859
|
"""Generate an answer to a query using Exa's search and LLM capabilities.
|
|
@@ -1842,6 +1861,7 @@ class Exa:
|
|
|
1842
1861
|
Args:
|
|
1843
1862
|
query (str): The query to answer.
|
|
1844
1863
|
text (bool, optional): Whether to include full text in the results. Defaults to False.
|
|
1864
|
+
system_prompt (str, optional): A system prompt to guide the LLM's behavior when generating the answer.
|
|
1845
1865
|
model (str, optional): The model to use for answering. Either "exa" or "exa-pro". Defaults to None.
|
|
1846
1866
|
|
|
1847
1867
|
Returns:
|
|
@@ -1870,6 +1890,7 @@ class Exa:
|
|
|
1870
1890
|
query: str,
|
|
1871
1891
|
*,
|
|
1872
1892
|
text: bool = False,
|
|
1893
|
+
system_prompt: Optional[str] = None,
|
|
1873
1894
|
model: Optional[Literal["exa", "exa-pro"]] = None,
|
|
1874
1895
|
) -> StreamAnswerResponse:
|
|
1875
1896
|
"""Generate a streaming answer response.
|
|
@@ -1877,6 +1898,7 @@ class Exa:
|
|
|
1877
1898
|
Args:
|
|
1878
1899
|
query (str): The query to answer.
|
|
1879
1900
|
text (bool): Whether to include full text in the results. Defaults to False.
|
|
1901
|
+
system_prompt (str, optional): A system prompt to guide the LLM's behavior when generating the answer.
|
|
1880
1902
|
model (str, optional): The model to use for answering. Either "exa" or "exa-pro". Defaults to None.
|
|
1881
1903
|
|
|
1882
1904
|
Returns:
|
|
@@ -2165,6 +2187,7 @@ class AsyncExa(Exa):
|
|
|
2165
2187
|
*,
|
|
2166
2188
|
stream: Optional[bool] = False,
|
|
2167
2189
|
text: Optional[bool] = False,
|
|
2190
|
+
system_prompt: Optional[str] = None,
|
|
2168
2191
|
model: Optional[Literal["exa", "exa-pro"]] = None,
|
|
2169
2192
|
) -> Union[AnswerResponse, StreamAnswerResponse]:
|
|
2170
2193
|
"""Generate an answer to a query using Exa's search and LLM capabilities.
|
|
@@ -2172,6 +2195,7 @@ class AsyncExa(Exa):
|
|
|
2172
2195
|
Args:
|
|
2173
2196
|
query (str): The query to answer.
|
|
2174
2197
|
text (bool, optional): Whether to include full text in the results. Defaults to False.
|
|
2198
|
+
system_prompt (str, optional): A system prompt to guide the LLM's behavior when generating the answer.
|
|
2175
2199
|
model (str, optional): The model to use for answering. Either "exa" or "exa-pro". Defaults to None.
|
|
2176
2200
|
|
|
2177
2201
|
Returns:
|
|
@@ -2200,6 +2224,7 @@ class AsyncExa(Exa):
|
|
|
2200
2224
|
query: str,
|
|
2201
2225
|
*,
|
|
2202
2226
|
text: bool = False,
|
|
2227
|
+
system_prompt: Optional[str] = None,
|
|
2203
2228
|
model: Optional[Literal["exa", "exa-pro"]] = None,
|
|
2204
2229
|
) -> AsyncStreamAnswerResponse:
|
|
2205
2230
|
"""Generate a streaming answer response.
|
|
@@ -2207,6 +2232,7 @@ class AsyncExa(Exa):
|
|
|
2207
2232
|
Args:
|
|
2208
2233
|
query (str): The query to answer.
|
|
2209
2234
|
text (bool): Whether to include full text in the results. Defaults to False.
|
|
2235
|
+
system_prompt (str, optional): A system prompt to guide the LLM's behavior when generating the answer.
|
|
2210
2236
|
model (str, optional): The model to use for answering. Either "exa" or "exa-pro". Defaults to None.
|
|
2211
2237
|
|
|
2212
2238
|
Returns:
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from typing import List, Optional, Literal
|
|
5
|
+
from typing import List, Optional, Literal, Dict, Any, Union
|
|
6
6
|
|
|
7
7
|
from .types import (
|
|
8
8
|
Webset,
|
|
@@ -28,16 +28,16 @@ class WebsetsClient(WebsetsBaseClient):
|
|
|
28
28
|
self.enrichments = WebsetEnrichmentsClient(client)
|
|
29
29
|
self.webhooks = WebsetWebhooksClient(client)
|
|
30
30
|
|
|
31
|
-
def create(self, params: CreateWebsetParameters) -> Webset:
|
|
31
|
+
def create(self, params: Union[Dict[str, Any], CreateWebsetParameters]) -> Webset:
|
|
32
32
|
"""Create a new Webset.
|
|
33
33
|
|
|
34
34
|
Args:
|
|
35
|
-
params (
|
|
35
|
+
params (CreateWebsetParameters): The parameters for creating a webset.
|
|
36
36
|
|
|
37
37
|
Returns:
|
|
38
38
|
Webset: The created webset.
|
|
39
39
|
"""
|
|
40
|
-
response = self.request("/v0/websets", data=params
|
|
40
|
+
response = self.request("/v0/websets", data=params)
|
|
41
41
|
return Webset.model_validate(response)
|
|
42
42
|
|
|
43
43
|
def get(self, id: str, *, expand: Optional[List[Literal["items"]]] = None) -> GetWebsetResponse:
|
|
@@ -69,7 +69,7 @@ class WebsetsClient(WebsetsBaseClient):
|
|
|
69
69
|
response = self.request("/v0/websets", params=params, method="GET")
|
|
70
70
|
return ListWebsetsResponse.model_validate(response)
|
|
71
71
|
|
|
72
|
-
def update(self, id: str, params: UpdateWebsetRequest) -> Webset:
|
|
72
|
+
def update(self, id: str, params: Union[Dict[str, Any], UpdateWebsetRequest]) -> Webset:
|
|
73
73
|
"""Update a Webset.
|
|
74
74
|
|
|
75
75
|
Args:
|
|
@@ -79,7 +79,7 @@ class WebsetsClient(WebsetsBaseClient):
|
|
|
79
79
|
Returns:
|
|
80
80
|
Webset: The updated webset.
|
|
81
81
|
"""
|
|
82
|
-
response = self.request(f"/v0/websets/{id}", data=params
|
|
82
|
+
response = self.request(f"/v0/websets/{id}", data=params, method="POST")
|
|
83
83
|
return Webset.model_validate(response)
|
|
84
84
|
|
|
85
85
|
def delete(self, id: str) -> Webset:
|
|
@@ -104,23 +104,29 @@ class WebsetsClient(WebsetsBaseClient):
|
|
|
104
104
|
Webset: The canceled webset.
|
|
105
105
|
"""
|
|
106
106
|
response = self.request(f"/v0/websets/{id}/cancel", method="POST")
|
|
107
|
-
return Webset.model_validate(response)
|
|
108
|
-
|
|
109
|
-
def wait_until_idle(self, id: str, *, timeout:
|
|
107
|
+
return Webset.model_validate(response)
|
|
108
|
+
|
|
109
|
+
def wait_until_idle(self, id: str, *, timeout: int = 3600, poll_interval: int = 5) -> Webset:
|
|
110
110
|
"""Wait until a Webset is idle.
|
|
111
111
|
|
|
112
112
|
Args:
|
|
113
113
|
id (str): The id or externalId of the Webset.
|
|
114
|
-
|
|
114
|
+
timeout (int, optional): Maximum time to wait in seconds. Defaults to 3600.
|
|
115
|
+
poll_interval (int, optional): Time to wait between polls in seconds. Defaults to 5.
|
|
116
|
+
|
|
115
117
|
Returns:
|
|
116
|
-
Webset: The webset.
|
|
118
|
+
Webset: The webset once it's idle.
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
TimeoutError: If the webset does not become idle within the timeout period.
|
|
117
122
|
"""
|
|
118
123
|
start_time = time.time()
|
|
119
124
|
while True:
|
|
120
125
|
webset = self.get(id)
|
|
121
|
-
if webset.status == WebsetStatus.idle:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if
|
|
125
|
-
raise
|
|
126
|
-
|
|
126
|
+
if webset.status == WebsetStatus.idle.value:
|
|
127
|
+
return webset
|
|
128
|
+
|
|
129
|
+
if time.time() - start_time > timeout:
|
|
130
|
+
raise TimeoutError(f"Webset {id} did not become idle within {timeout} seconds")
|
|
131
|
+
|
|
132
|
+
time.sleep(poll_interval)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pydantic import ConfigDict, BaseModel, AnyUrl
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any, Dict, Optional, TypeVar, Generic, Type, get_origin, get_args, Union
|
|
7
|
+
|
|
8
|
+
# Generic type var for any Enum
|
|
9
|
+
EnumT = TypeVar('EnumT', bound=Enum)
|
|
10
|
+
|
|
11
|
+
# Generic type for any ExaBaseModel
|
|
12
|
+
ModelT = TypeVar('ModelT', bound='ExaBaseModel')
|
|
13
|
+
|
|
14
|
+
# Custom JSON encoder for handling AnyUrl
|
|
15
|
+
class ExaJSONEncoder(json.JSONEncoder):
|
|
16
|
+
def default(self, obj):
|
|
17
|
+
if isinstance(obj, AnyUrl):
|
|
18
|
+
return str(obj)
|
|
19
|
+
return super().default(obj)
|
|
20
|
+
|
|
21
|
+
class ExaBaseModel(BaseModel):
|
|
22
|
+
"""Base model for all Exa models with common configuration."""
|
|
23
|
+
model_config = ConfigDict(
|
|
24
|
+
populate_by_name=True,
|
|
25
|
+
use_enum_values=True,
|
|
26
|
+
coerce_numbers_to_str=False, # Don't convert numbers to strings
|
|
27
|
+
str_strip_whitespace=True, # Strip whitespace from strings
|
|
28
|
+
str_to_lower=False, # Don't convert strings to lowercase
|
|
29
|
+
str_to_upper=False, # Don't convert strings to uppercase
|
|
30
|
+
from_attributes=True, # Allow initialization from attributes
|
|
31
|
+
validate_assignment=True, # Validate on assignment
|
|
32
|
+
extra='forbid', # Forbid extra fields
|
|
33
|
+
json_encoders={AnyUrl: str} # Convert AnyUrl to string when serializing to JSON
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
class WebsetsBaseClient:
|
|
37
|
+
base_url: str
|
|
38
|
+
|
|
39
|
+
"""Base client for Exa API resources."""
|
|
40
|
+
|
|
41
|
+
def __init__(self, client):
|
|
42
|
+
"""Initialize the client.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
client: The parent Exa client.
|
|
46
|
+
"""
|
|
47
|
+
self._client = client
|
|
48
|
+
|
|
49
|
+
def _prepare_data(self, data: Union[Dict[str, Any], ExaBaseModel, str], model_class: Optional[Type[ModelT]] = None) -> Union[Dict[str, Any], str]:
|
|
50
|
+
"""Prepare data for API request, converting dict to model if needed.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
data: Either a dictionary, model instance, or string
|
|
54
|
+
model_class: The model class to use if data is a dictionary
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Dictionary prepared for API request or string if string data was provided
|
|
58
|
+
"""
|
|
59
|
+
if isinstance(data, str):
|
|
60
|
+
# Return string as is
|
|
61
|
+
return data
|
|
62
|
+
elif isinstance(data, dict) and model_class:
|
|
63
|
+
# Convert dict to model instance
|
|
64
|
+
model_instance = model_class.model_validate(data)
|
|
65
|
+
return model_instance.model_dump(by_alias=True, exclude_none=True)
|
|
66
|
+
elif isinstance(data, ExaBaseModel):
|
|
67
|
+
# Use model's dump method
|
|
68
|
+
return data.model_dump(by_alias=True, exclude_none=True)
|
|
69
|
+
elif isinstance(data, dict):
|
|
70
|
+
# Use dict directly
|
|
71
|
+
return data
|
|
72
|
+
else:
|
|
73
|
+
raise TypeError(f"Expected dict, ExaBaseModel, or str, got {type(data)}")
|
|
74
|
+
|
|
75
|
+
def request(self, endpoint: str, data: Optional[Union[Dict[str, Any], ExaBaseModel, str]] = None,
|
|
76
|
+
method: str = "POST", params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
77
|
+
"""Make a request to the Exa API.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
endpoint (str): The API endpoint to request.
|
|
81
|
+
data (Union[Dict[str, Any], ExaBaseModel, str], optional): The request data. Can be a dictionary, model instance, or string. Defaults to None.
|
|
82
|
+
method (str, optional): The HTTP method. Defaults to "POST".
|
|
83
|
+
params (Dict[str, Any], optional): The query parameters. Defaults to None.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Dict[str, Any]: The API response.
|
|
87
|
+
"""
|
|
88
|
+
if isinstance(data, str):
|
|
89
|
+
# If data is a string, pass it as is
|
|
90
|
+
pass
|
|
91
|
+
elif data is not None and isinstance(data, ExaBaseModel):
|
|
92
|
+
# If data is a model instance, convert it to a dict
|
|
93
|
+
data = data.model_dump(by_alias=True, exclude_none=True)
|
|
94
|
+
|
|
95
|
+
return self._client.request("/websets/" + endpoint, data=data, method=method, params=params)
|
|
96
|
+
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import Dict, Any, Union
|
|
4
|
+
|
|
3
5
|
from ..types import (
|
|
4
6
|
CreateEnrichmentParameters,
|
|
5
7
|
WebsetEnrichment,
|
|
@@ -12,7 +14,7 @@ class WebsetEnrichmentsClient(WebsetsBaseClient):
|
|
|
12
14
|
def __init__(self, client):
|
|
13
15
|
super().__init__(client)
|
|
14
16
|
|
|
15
|
-
def create(self, webset_id: str, params: CreateEnrichmentParameters) -> WebsetEnrichment:
|
|
17
|
+
def create(self, webset_id: str, params: Union[Dict[str, Any], CreateEnrichmentParameters]) -> WebsetEnrichment:
|
|
16
18
|
"""Create an Enrichment for a Webset.
|
|
17
19
|
|
|
18
20
|
Args:
|
|
@@ -22,7 +24,7 @@ class WebsetEnrichmentsClient(WebsetsBaseClient):
|
|
|
22
24
|
Returns:
|
|
23
25
|
WebsetEnrichment: The created enrichment.
|
|
24
26
|
"""
|
|
25
|
-
response = self.request(f"/v0/websets/{webset_id}/enrichments", data=params
|
|
27
|
+
response = self.request(f"/v0/websets/{webset_id}/enrichments", data=params)
|
|
26
28
|
return WebsetEnrichment.model_validate(response)
|
|
27
29
|
|
|
28
30
|
def get(self, webset_id: str, id: str) -> WebsetEnrichment:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import Dict, Any, Union
|
|
4
|
+
|
|
3
5
|
from ..types import (
|
|
4
6
|
CreateWebsetSearchParameters,
|
|
5
7
|
WebsetSearch,
|
|
@@ -12,7 +14,7 @@ class WebsetSearchesClient(WebsetsBaseClient):
|
|
|
12
14
|
def __init__(self, client):
|
|
13
15
|
super().__init__(client)
|
|
14
16
|
|
|
15
|
-
def create(self, webset_id: str, params: CreateWebsetSearchParameters) -> WebsetSearch:
|
|
17
|
+
def create(self, webset_id: str, params: Union[Dict[str, Any], CreateWebsetSearchParameters]) -> WebsetSearch:
|
|
16
18
|
"""Create a new Search for the Webset.
|
|
17
19
|
|
|
18
20
|
Args:
|
|
@@ -22,7 +24,7 @@ class WebsetSearchesClient(WebsetsBaseClient):
|
|
|
22
24
|
Returns:
|
|
23
25
|
WebsetSearch: The created search.
|
|
24
26
|
"""
|
|
25
|
-
response = self.request(f"/v0/websets/{webset_id}/searches", data=params
|
|
27
|
+
response = self.request(f"/v0/websets/{webset_id}/searches", data=params)
|
|
26
28
|
return WebsetSearch.model_validate(response)
|
|
27
29
|
|
|
28
30
|
def get(self, webset_id: str, id: str) -> WebsetSearch:
|
|
@@ -29,7 +29,7 @@ class CreateEnrichmentParameters(ExaBaseModel):
|
|
|
29
29
|
"""
|
|
30
30
|
Provide a description of the enrichment task you want to perform to each Webset Item.
|
|
31
31
|
"""
|
|
32
|
-
format: Optional[
|
|
32
|
+
format: Optional[Format] = None
|
|
33
33
|
"""
|
|
34
34
|
Format of the enrichment response.
|
|
35
35
|
|
|
@@ -46,7 +46,7 @@ class CreateEnrichmentParameters(ExaBaseModel):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class CreateWebhookParameters(ExaBaseModel):
|
|
49
|
-
events: List[
|
|
49
|
+
events: List[EventType] = Field(..., max_items=12, min_items=1)
|
|
50
50
|
"""
|
|
51
51
|
The events to trigger the webhook
|
|
52
52
|
"""
|
|
@@ -127,7 +127,7 @@ class CreateWebsetSearchParameters(ExaBaseModel):
|
|
|
127
127
|
"""
|
|
128
128
|
The behaviour of the Search when it is added to a Webset.
|
|
129
129
|
|
|
130
|
-
- `override`: the search will reuse the existing Items found in the Webset and evaluate them against the new criteria. Any Items that don't match the new criteria will
|
|
130
|
+
- `override`: the search will reuse the existing Items found in the Webset and evaluate them against the new criteria. Any Items that don't match the new criteria will be discarded.
|
|
131
131
|
"""
|
|
132
132
|
metadata: Optional[Dict[str, Any]] = None
|
|
133
133
|
"""
|
|
@@ -149,9 +149,9 @@ class Criterion(ExaBaseModel):
|
|
|
149
149
|
class EnrichmentResult(ExaBaseModel):
|
|
150
150
|
object: Literal['enrichment_result']
|
|
151
151
|
format: WebsetEnrichmentFormat
|
|
152
|
-
result: List[str]
|
|
152
|
+
result: Optional[List[str]] = None
|
|
153
153
|
"""
|
|
154
|
-
The result of the enrichment.
|
|
154
|
+
The result of the enrichment. None if the enrichment wasn't successful.
|
|
155
155
|
"""
|
|
156
156
|
reasoning: Optional[str] = None
|
|
157
157
|
"""
|
|
@@ -167,7 +167,7 @@ class EnrichmentResult(ExaBaseModel):
|
|
|
167
167
|
"""
|
|
168
168
|
|
|
169
169
|
|
|
170
|
-
class
|
|
170
|
+
class EventType(Enum):
|
|
171
171
|
webset_created = 'webset.created'
|
|
172
172
|
webset_deleted = 'webset.deleted'
|
|
173
173
|
webset_paused = 'webset.paused'
|
|
@@ -182,6 +182,7 @@ class Event(Enum):
|
|
|
182
182
|
webset_item_enriched = 'webset.item.enriched'
|
|
183
183
|
|
|
184
184
|
|
|
185
|
+
|
|
185
186
|
class Format(Enum):
|
|
186
187
|
"""
|
|
187
188
|
Format of the enrichment response.
|
|
@@ -225,6 +226,21 @@ class ListEventsResponse(ExaBaseModel):
|
|
|
225
226
|
"""
|
|
226
227
|
|
|
227
228
|
|
|
229
|
+
class ListWebhookAttemptsResponse(ExaBaseModel):
|
|
230
|
+
data: List[WebhookAttempt]
|
|
231
|
+
"""
|
|
232
|
+
The list of webhook attempts
|
|
233
|
+
"""
|
|
234
|
+
has_more: bool = Field(..., alias='hasMore')
|
|
235
|
+
"""
|
|
236
|
+
Whether there are more results to paginate through
|
|
237
|
+
"""
|
|
238
|
+
next_cursor: Optional[str] = Field(..., alias='nextCursor')
|
|
239
|
+
"""
|
|
240
|
+
The cursor to paginate through the next set of results
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
|
|
228
244
|
class ListWebhooksResponse(ExaBaseModel):
|
|
229
245
|
data: List[Webhook]
|
|
230
246
|
"""
|
|
@@ -335,7 +351,7 @@ class Search(ExaBaseModel):
|
|
|
335
351
|
|
|
336
352
|
Any URL provided will be crawled and used as context for the search.
|
|
337
353
|
"""
|
|
338
|
-
count: confloat(ge=1.0)
|
|
354
|
+
count: Optional[confloat(ge=1.0)] = 10
|
|
339
355
|
"""
|
|
340
356
|
Number of Items the Webset will attempt to find.
|
|
341
357
|
|
|
@@ -374,7 +390,7 @@ class Source(Enum):
|
|
|
374
390
|
|
|
375
391
|
|
|
376
392
|
class UpdateWebhookParameters(ExaBaseModel):
|
|
377
|
-
events: Optional[List[
|
|
393
|
+
events: Optional[List[EventType]] = Field(None, max_items=12, min_items=1)
|
|
378
394
|
"""
|
|
379
395
|
The events to trigger the webhook
|
|
380
396
|
"""
|
|
@@ -389,7 +405,7 @@ class UpdateWebhookParameters(ExaBaseModel):
|
|
|
389
405
|
|
|
390
406
|
|
|
391
407
|
class UpdateWebsetRequest(ExaBaseModel):
|
|
392
|
-
metadata: Optional[Dict[str,
|
|
408
|
+
metadata: Optional[Dict[str, constr(max_length=1000)]] = None
|
|
393
409
|
"""
|
|
394
410
|
Set of key-value pairs you want to associate with this object.
|
|
395
411
|
"""
|
|
@@ -405,7 +421,7 @@ class Webhook(ExaBaseModel):
|
|
|
405
421
|
"""
|
|
406
422
|
The status of the webhook
|
|
407
423
|
"""
|
|
408
|
-
events: List[
|
|
424
|
+
events: List[EventType] = Field(..., min_items=1)
|
|
409
425
|
"""
|
|
410
426
|
The events to trigger the webhook
|
|
411
427
|
"""
|
|
@@ -413,7 +429,11 @@ class Webhook(ExaBaseModel):
|
|
|
413
429
|
"""
|
|
414
430
|
The URL to send the webhook to
|
|
415
431
|
"""
|
|
416
|
-
|
|
432
|
+
secret: Optional[str] = None
|
|
433
|
+
"""
|
|
434
|
+
The secret to verify the webhook signature. Only returned on Webhook creation.
|
|
435
|
+
"""
|
|
436
|
+
metadata: Optional[Dict[str, Any]] = {}
|
|
417
437
|
"""
|
|
418
438
|
The metadata of the webhook
|
|
419
439
|
"""
|
|
@@ -427,6 +447,54 @@ class Webhook(ExaBaseModel):
|
|
|
427
447
|
"""
|
|
428
448
|
|
|
429
449
|
|
|
450
|
+
class WebhookAttempt(ExaBaseModel):
|
|
451
|
+
id: str
|
|
452
|
+
"""
|
|
453
|
+
The unique identifier for the webhook attempt
|
|
454
|
+
"""
|
|
455
|
+
object: Literal['webhook_attempt']
|
|
456
|
+
event_id: str = Field(..., alias='eventId')
|
|
457
|
+
"""
|
|
458
|
+
The unique identifier for the event
|
|
459
|
+
"""
|
|
460
|
+
event_type: EventType = Field(..., alias='eventType')
|
|
461
|
+
"""
|
|
462
|
+
The type of event
|
|
463
|
+
"""
|
|
464
|
+
webhook_id: str = Field(..., alias='webhookId')
|
|
465
|
+
"""
|
|
466
|
+
The unique identifier for the webhook
|
|
467
|
+
"""
|
|
468
|
+
url: str
|
|
469
|
+
"""
|
|
470
|
+
The URL that was used during the attempt
|
|
471
|
+
"""
|
|
472
|
+
successful: bool
|
|
473
|
+
"""
|
|
474
|
+
Whether the attempt was successful
|
|
475
|
+
"""
|
|
476
|
+
response_headers: Dict[str, Any] = Field(..., alias='responseHeaders')
|
|
477
|
+
"""
|
|
478
|
+
The headers of the response
|
|
479
|
+
"""
|
|
480
|
+
response_body: str = Field(..., alias='responseBody')
|
|
481
|
+
"""
|
|
482
|
+
The body of the response
|
|
483
|
+
"""
|
|
484
|
+
response_status_code: float = Field(..., alias='responseStatusCode')
|
|
485
|
+
"""
|
|
486
|
+
The status code of the response
|
|
487
|
+
"""
|
|
488
|
+
attempt: float
|
|
489
|
+
"""
|
|
490
|
+
The attempt number of the webhook
|
|
491
|
+
"""
|
|
492
|
+
attempted_at: datetime = Field(..., alias='attemptedAt')
|
|
493
|
+
"""
|
|
494
|
+
The date and time the webhook attempt was made
|
|
495
|
+
"""
|
|
496
|
+
|
|
497
|
+
|
|
430
498
|
class WebhookStatus(Enum):
|
|
431
499
|
"""
|
|
432
500
|
The status of the webhook
|
|
@@ -458,7 +526,7 @@ class Webset(ExaBaseModel):
|
|
|
458
526
|
"""
|
|
459
527
|
The Enrichments to apply to the Webset Items.
|
|
460
528
|
"""
|
|
461
|
-
metadata: Optional[Dict[str, Any]] =
|
|
529
|
+
metadata: Optional[Dict[str, Any]] = {}
|
|
462
530
|
"""
|
|
463
531
|
Set of key-value pairs you want to associate with this object.
|
|
464
532
|
"""
|
|
@@ -558,7 +626,7 @@ class WebsetEnrichment(ExaBaseModel):
|
|
|
558
626
|
|
|
559
627
|
This will be automatically generated based on the description and format.
|
|
560
628
|
"""
|
|
561
|
-
metadata: Optional[Dict[str, Any]] =
|
|
629
|
+
metadata: Optional[Dict[str, Any]] = {}
|
|
562
630
|
"""
|
|
563
631
|
The metadata of the enrichment
|
|
564
632
|
"""
|
|
@@ -595,10 +663,6 @@ class WebsetEnrichmentStatus(Enum):
|
|
|
595
663
|
completed = 'completed'
|
|
596
664
|
|
|
597
665
|
|
|
598
|
-
class WebsetExportFormat(Enum):
|
|
599
|
-
csv = 'csv'
|
|
600
|
-
|
|
601
|
-
|
|
602
666
|
class WebsetIdleEvent(ExaBaseModel):
|
|
603
667
|
id: str
|
|
604
668
|
"""
|
|
@@ -645,7 +709,7 @@ class WebsetItem(ExaBaseModel):
|
|
|
645
709
|
"""
|
|
646
710
|
The criteria evaluations of the item
|
|
647
711
|
"""
|
|
648
|
-
enrichments:
|
|
712
|
+
enrichments: List[EnrichmentResult]
|
|
649
713
|
"""
|
|
650
714
|
The enrichments results of the Webset item
|
|
651
715
|
"""
|
|
@@ -806,7 +870,7 @@ class WebsetItemEvaluation(ExaBaseModel):
|
|
|
806
870
|
"""
|
|
807
871
|
The satisfaction of the criterion
|
|
808
872
|
"""
|
|
809
|
-
references:
|
|
873
|
+
references: List[Reference] = []
|
|
810
874
|
"""
|
|
811
875
|
The references used to generate the result. `null` if the evaluation is not yet completed.
|
|
812
876
|
"""
|
|
@@ -912,14 +976,12 @@ class WebsetSearch(ExaBaseModel):
|
|
|
912
976
|
"""
|
|
913
977
|
The query used to create the search.
|
|
914
978
|
"""
|
|
915
|
-
entity:
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
WebsetCustomEntity,
|
|
922
|
-
]
|
|
979
|
+
entity: Union[
|
|
980
|
+
WebsetCompanyEntity,
|
|
981
|
+
WebsetPersonEntity,
|
|
982
|
+
WebsetArticleEntity,
|
|
983
|
+
WebsetResearchPaperEntity,
|
|
984
|
+
WebsetCustomEntity,
|
|
923
985
|
]
|
|
924
986
|
"""
|
|
925
987
|
The entity the search will return results for.
|
|
@@ -938,7 +1000,7 @@ class WebsetSearch(ExaBaseModel):
|
|
|
938
1000
|
"""
|
|
939
1001
|
The progress of the search
|
|
940
1002
|
"""
|
|
941
|
-
metadata: Optional[Dict[str, Any]] =
|
|
1003
|
+
metadata: Optional[Dict[str, Any]] = {}
|
|
942
1004
|
"""
|
|
943
1005
|
Set of key-value pairs you want to associate with this object.
|
|
944
1006
|
"""
|
|
@@ -964,7 +1026,7 @@ class WebsetSearchBehaviour(Enum):
|
|
|
964
1026
|
"""
|
|
965
1027
|
The behaviour of the Search when it is added to a Webset.
|
|
966
1028
|
|
|
967
|
-
- `override`: the search will reuse the existing Items found in the Webset and evaluate them against the new criteria. Any Items that don't match the new criteria will
|
|
1029
|
+
- `override`: the search will reuse the existing Items found in the Webset and evaluate them against the new criteria. Any Items that don't match the new criteria will be discarded.
|
|
968
1030
|
"""
|
|
969
1031
|
|
|
970
1032
|
override = 'override'
|
|
@@ -1051,4 +1113,4 @@ class GetWebsetResponse(Webset):
|
|
|
1051
1113
|
items: Optional[List[WebsetItem]] = None
|
|
1052
1114
|
"""
|
|
1053
1115
|
When expand query parameter contains `items`, this will contain the items in the webset
|
|
1054
|
-
"""
|
|
1116
|
+
"""
|
|
@@ -1,22 +1,60 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional, Dict, Any, Union, Literal
|
|
4
4
|
|
|
5
5
|
from ..types import (
|
|
6
6
|
CreateWebhookParameters,
|
|
7
7
|
Webhook,
|
|
8
8
|
ListWebhooksResponse,
|
|
9
9
|
UpdateWebhookParameters,
|
|
10
|
+
ListWebhookAttemptsResponse,
|
|
11
|
+
EventType,
|
|
10
12
|
)
|
|
11
13
|
from ..core.base import WebsetsBaseClient
|
|
12
14
|
|
|
15
|
+
class WebhookAttemptsClient(WebsetsBaseClient):
|
|
16
|
+
"""Client for managing Webhook Attempts."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client):
|
|
19
|
+
super().__init__(client)
|
|
20
|
+
|
|
21
|
+
def list(self, webhook_id: str, *, cursor: Optional[str] = None,
|
|
22
|
+
limit: Optional[int] = None, event_type: Optional[Union[EventType, str]] = None) -> ListWebhookAttemptsResponse:
|
|
23
|
+
"""List all attempts made by a Webhook ordered in descending order.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
webhook_id (str): The ID of the webhook.
|
|
27
|
+
cursor (str, optional): The cursor to paginate through the results.
|
|
28
|
+
limit (int, optional): The number of results to return (max 200).
|
|
29
|
+
event_type (Union[EventType, str], optional): The type of event to filter by.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
ListWebhookAttemptsResponse: List of webhook attempts.
|
|
33
|
+
"""
|
|
34
|
+
event_type_value = None
|
|
35
|
+
if event_type is not None:
|
|
36
|
+
if isinstance(event_type, EventType):
|
|
37
|
+
event_type_value = event_type.value
|
|
38
|
+
else:
|
|
39
|
+
event_type_value = event_type
|
|
40
|
+
|
|
41
|
+
params = {k: v for k, v in {
|
|
42
|
+
"cursor": cursor,
|
|
43
|
+
"limit": limit,
|
|
44
|
+
"eventType": event_type_value
|
|
45
|
+
}.items() if v is not None}
|
|
46
|
+
|
|
47
|
+
response = self.request(f"/v0/webhooks/{webhook_id}/attempts", params=params, method="GET")
|
|
48
|
+
return ListWebhookAttemptsResponse.model_validate(response)
|
|
49
|
+
|
|
13
50
|
class WebsetWebhooksClient(WebsetsBaseClient):
|
|
14
51
|
"""Client for managing Webset Webhooks."""
|
|
15
52
|
|
|
16
53
|
def __init__(self, client):
|
|
17
54
|
super().__init__(client)
|
|
55
|
+
self.attempts = WebhookAttemptsClient(client)
|
|
18
56
|
|
|
19
|
-
def create(self, params: CreateWebhookParameters) -> Webhook:
|
|
57
|
+
def create(self, params: Union[Dict[str, Any], CreateWebhookParameters]) -> Webhook:
|
|
20
58
|
"""Create a Webhook.
|
|
21
59
|
|
|
22
60
|
Args:
|
|
@@ -25,7 +63,7 @@ class WebsetWebhooksClient(WebsetsBaseClient):
|
|
|
25
63
|
Returns:
|
|
26
64
|
Webhook: The created webhook.
|
|
27
65
|
"""
|
|
28
|
-
response = self.request("/v0/webhooks", data=params
|
|
66
|
+
response = self.request("/v0/webhooks", data=params)
|
|
29
67
|
return Webhook.model_validate(response)
|
|
30
68
|
|
|
31
69
|
def get(self, id: str) -> Webhook:
|
|
@@ -54,7 +92,7 @@ class WebsetWebhooksClient(WebsetsBaseClient):
|
|
|
54
92
|
response = self.request("/v0/webhooks", params=params, method="GET")
|
|
55
93
|
return ListWebhooksResponse.model_validate(response)
|
|
56
94
|
|
|
57
|
-
def update(self, id: str, params: UpdateWebhookParameters) -> Webhook:
|
|
95
|
+
def update(self, id: str, params: Union[Dict[str, Any], UpdateWebhookParameters]) -> Webhook:
|
|
58
96
|
"""Update a Webhook.
|
|
59
97
|
|
|
60
98
|
Args:
|
|
@@ -64,7 +102,7 @@ class WebsetWebhooksClient(WebsetsBaseClient):
|
|
|
64
102
|
Returns:
|
|
65
103
|
Webhook: The updated webhook.
|
|
66
104
|
"""
|
|
67
|
-
response = self.request(f"/v0/webhooks/{id}", data=params
|
|
105
|
+
response = self.request(f"/v0/webhooks/{id}", data=params, method="PATCH")
|
|
68
106
|
return Webhook.model_validate(response)
|
|
69
107
|
|
|
70
108
|
def delete(self, id: str) -> Webhook:
|
|
@@ -77,4 +115,4 @@ class WebsetWebhooksClient(WebsetsBaseClient):
|
|
|
77
115
|
Webhook: The deleted webhook.
|
|
78
116
|
"""
|
|
79
117
|
response = self.request(f"/v0/webhooks/{id}", method="DELETE")
|
|
80
|
-
return Webhook.model_validate(response)
|
|
118
|
+
return Webhook.model_validate(response)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "exa-py"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.12.0"
|
|
4
4
|
description = "Python SDK for Exa API."
|
|
5
5
|
authors = ["Exa AI <hello@exa.ai>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -32,7 +32,7 @@ in-project = true
|
|
|
32
32
|
|
|
33
33
|
[project]
|
|
34
34
|
name = "exa-py"
|
|
35
|
-
version = "1.
|
|
35
|
+
version = "1.12.0"
|
|
36
36
|
description = "Python SDK for Exa API."
|
|
37
37
|
readme = "README.md"
|
|
38
38
|
requires-python = ">=3.9"
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
from pydantic import ConfigDict
|
|
5
|
-
|
|
6
|
-
from typing import Any, Dict, Optional
|
|
7
|
-
|
|
8
|
-
from pydantic import BaseModel
|
|
9
|
-
|
|
10
|
-
class ExaBaseModel(BaseModel):
|
|
11
|
-
"""Base model for all Exa models with common configuration."""
|
|
12
|
-
model_config = ConfigDict(populate_by_name=True)
|
|
13
|
-
|
|
14
|
-
class WebsetsBaseClient:
|
|
15
|
-
base_url: str
|
|
16
|
-
|
|
17
|
-
"""Base client for Exa API resources."""
|
|
18
|
-
|
|
19
|
-
def __init__(self, client):
|
|
20
|
-
"""Initialize the client.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
client: The parent Exa client.
|
|
24
|
-
"""
|
|
25
|
-
self._client = client
|
|
26
|
-
|
|
27
|
-
def request(self, endpoint: str, data: Optional[Dict[str, Any]] = None,
|
|
28
|
-
method: str = "POST", params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
29
|
-
"""Make a request to the Exa API.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
endpoint (str): The API endpoint to request.
|
|
33
|
-
data (Dict[str, Any], optional): The request data. Defaults to None.
|
|
34
|
-
method (str, optional): The HTTP method. Defaults to "POST".
|
|
35
|
-
params (Dict[str, Any], optional): The query parameters. Defaults to None.
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
Dict[str, Any]: The API response.
|
|
39
|
-
"""
|
|
40
|
-
return self._client.request("/websets/" + endpoint, data=data, method=method, params=params)
|
|
41
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|