exa-py 1.14.4__tar.gz → 1.14.5__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.14.4 → exa_py-1.14.5}/PKG-INFO +1 -1
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/api.py +27 -1
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py.egg-info/PKG-INFO +1 -1
- {exa_py-1.14.4 → exa_py-1.14.5}/pyproject.toml +1 -8
- {exa_py-1.14.4 → exa_py-1.14.5}/tests/test_search_api.py +31 -2
- {exa_py-1.14.4 → exa_py-1.14.5}/README.md +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/py.typed +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/research/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/research/client.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/research/models.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/utils.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/client.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/core/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/core/base.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/enrichments/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/enrichments/client.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/items/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/items/client.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/monitors/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/monitors/client.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/monitors/runs/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/monitors/runs/client.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/searches/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/searches/client.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/types.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/webhooks/__init__.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py/websets/webhooks/client.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py.egg-info/SOURCES.txt +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py.egg-info/dependency_links.txt +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py.egg-info/requires.txt +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/exa_py.egg-info/top_level.txt +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/setup.cfg +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/setup.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/tests/test_monitors.py +0 -0
- {exa_py-1.14.4 → exa_py-1.14.5}/tests/test_websets.py +0 -0
|
@@ -166,6 +166,7 @@ CONTENTS_OPTIONS_TYPES = {
|
|
|
166
166
|
"text": [dict, bool],
|
|
167
167
|
"highlights": [dict, bool],
|
|
168
168
|
"summary": [dict, bool],
|
|
169
|
+
"context": [dict, bool],
|
|
169
170
|
"metadata": [dict, bool],
|
|
170
171
|
"livecrawl_timeout": [int],
|
|
171
172
|
"livecrawl": [LIVECRAWL_OPTIONS],
|
|
@@ -292,6 +293,16 @@ class SummaryContentsOptions(TypedDict, total=False):
|
|
|
292
293
|
schema: JSONSchema
|
|
293
294
|
|
|
294
295
|
|
|
296
|
+
class ContextContentsOptions(TypedDict, total=False):
|
|
297
|
+
"""Options for retrieving aggregated context from a set of search results.
|
|
298
|
+
|
|
299
|
+
Attributes:
|
|
300
|
+
max_characters (int): The maximum number of characters to include in the context string.
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
max_characters: int
|
|
304
|
+
|
|
305
|
+
|
|
295
306
|
class ExtrasOptions(TypedDict, total=False):
|
|
296
307
|
"""A class representing additional extraction fields (e.g. links, images)"""
|
|
297
308
|
|
|
@@ -807,17 +818,23 @@ class SearchResponse(Generic[T]):
|
|
|
807
818
|
autoprompt_string (str, optional): The Exa query created by autoprompt.
|
|
808
819
|
resolved_search_type (str, optional): 'neural' or 'keyword' if auto.
|
|
809
820
|
auto_date (str, optional): A date for filtering if autoprompt found one.
|
|
821
|
+
context (str, optional): Combined context string when requested via contents.context.
|
|
822
|
+
statuses (List[ContentStatus], optional): Status list from get_contents.
|
|
823
|
+
cost_dollars (CostDollars, optional): Cost breakdown.
|
|
810
824
|
"""
|
|
811
825
|
|
|
812
826
|
results: List[T]
|
|
813
827
|
autoprompt_string: Optional[str]
|
|
814
828
|
resolved_search_type: Optional[str]
|
|
815
829
|
auto_date: Optional[str]
|
|
830
|
+
context: Optional[str] = None
|
|
816
831
|
statuses: Optional[List[ContentStatus]] = None
|
|
817
832
|
cost_dollars: Optional[CostDollars] = None
|
|
818
833
|
|
|
819
834
|
def __str__(self):
|
|
820
835
|
output = "\n\n".join(str(result) for result in self.results)
|
|
836
|
+
if self.context:
|
|
837
|
+
output += f"\nContext: {self.context}"
|
|
821
838
|
if self.autoprompt_string:
|
|
822
839
|
output += f"\n\nAutoprompt String: {self.autoprompt_string}"
|
|
823
840
|
if self.resolved_search_type:
|
|
@@ -1254,6 +1271,7 @@ class Exa:
|
|
|
1254
1271
|
"text",
|
|
1255
1272
|
"highlights",
|
|
1256
1273
|
"summary",
|
|
1274
|
+
"context",
|
|
1257
1275
|
"subpages",
|
|
1258
1276
|
"subpage_target",
|
|
1259
1277
|
"livecrawl",
|
|
@@ -1270,6 +1288,7 @@ class Exa:
|
|
|
1270
1288
|
data["autopromptString"] if "autopromptString" in data else None,
|
|
1271
1289
|
data["resolvedSearchType"] if "resolvedSearchType" in data else None,
|
|
1272
1290
|
data["autoDate"] if "autoDate" in data else None,
|
|
1291
|
+
context=data.get("context"),
|
|
1273
1292
|
cost_dollars=cost_dollars,
|
|
1274
1293
|
)
|
|
1275
1294
|
|
|
@@ -1414,7 +1433,6 @@ class Exa:
|
|
|
1414
1433
|
options,
|
|
1415
1434
|
{**CONTENTS_OPTIONS_TYPES, **CONTENTS_ENDPOINT_OPTIONS_TYPES},
|
|
1416
1435
|
)
|
|
1417
|
-
|
|
1418
1436
|
options = to_camel_case(options)
|
|
1419
1437
|
data = self.request("/contents", options)
|
|
1420
1438
|
cost_dollars = parse_cost_dollars(data.get("costDollars"))
|
|
@@ -1424,6 +1442,7 @@ class Exa:
|
|
|
1424
1442
|
data.get("autopromptString"),
|
|
1425
1443
|
data.get("resolvedSearchType"),
|
|
1426
1444
|
data.get("autoDate"),
|
|
1445
|
+
context=data.get("context"),
|
|
1427
1446
|
cost_dollars=cost_dollars,
|
|
1428
1447
|
statuses=statuses,
|
|
1429
1448
|
)
|
|
@@ -1715,6 +1734,7 @@ class Exa:
|
|
|
1715
1734
|
"text",
|
|
1716
1735
|
"highlights",
|
|
1717
1736
|
"summary",
|
|
1737
|
+
"context",
|
|
1718
1738
|
"subpages",
|
|
1719
1739
|
"subpage_target",
|
|
1720
1740
|
"livecrawl",
|
|
@@ -1731,6 +1751,7 @@ class Exa:
|
|
|
1731
1751
|
data.get("autopromptString"),
|
|
1732
1752
|
data.get("resolvedSearchType"),
|
|
1733
1753
|
data.get("autoDate"),
|
|
1754
|
+
context=data.get("context"),
|
|
1734
1755
|
cost_dollars=cost_dollars,
|
|
1735
1756
|
)
|
|
1736
1757
|
|
|
@@ -2067,6 +2088,7 @@ class AsyncExa(Exa):
|
|
|
2067
2088
|
"text",
|
|
2068
2089
|
"highlights",
|
|
2069
2090
|
"summary",
|
|
2091
|
+
"context",
|
|
2070
2092
|
"subpages",
|
|
2071
2093
|
"subpage_target",
|
|
2072
2094
|
"livecrawl",
|
|
@@ -2083,6 +2105,7 @@ class AsyncExa(Exa):
|
|
|
2083
2105
|
data["autopromptString"] if "autopromptString" in data else None,
|
|
2084
2106
|
data["resolvedSearchType"] if "resolvedSearchType" in data else None,
|
|
2085
2107
|
data["autoDate"] if "autoDate" in data else None,
|
|
2108
|
+
context=data.get("context"),
|
|
2086
2109
|
cost_dollars=cost_dollars,
|
|
2087
2110
|
)
|
|
2088
2111
|
|
|
@@ -2113,6 +2136,7 @@ class AsyncExa(Exa):
|
|
|
2113
2136
|
data.get("autopromptString"),
|
|
2114
2137
|
data.get("resolvedSearchType"),
|
|
2115
2138
|
data.get("autoDate"),
|
|
2139
|
+
context=data.get("context"),
|
|
2116
2140
|
cost_dollars=cost_dollars,
|
|
2117
2141
|
statuses=statuses,
|
|
2118
2142
|
)
|
|
@@ -2192,6 +2216,7 @@ class AsyncExa(Exa):
|
|
|
2192
2216
|
"text",
|
|
2193
2217
|
"highlights",
|
|
2194
2218
|
"summary",
|
|
2219
|
+
"context",
|
|
2195
2220
|
"subpages",
|
|
2196
2221
|
"subpage_target",
|
|
2197
2222
|
"livecrawl",
|
|
@@ -2208,6 +2233,7 @@ class AsyncExa(Exa):
|
|
|
2208
2233
|
data.get("autopromptString"),
|
|
2209
2234
|
data.get("resolvedSearchType"),
|
|
2210
2235
|
data.get("autoDate"),
|
|
2236
|
+
context=data.get("context"),
|
|
2211
2237
|
cost_dollars=cost_dollars,
|
|
2212
2238
|
)
|
|
2213
2239
|
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "exa-py"
|
|
3
|
-
version = "1.14.2"
|
|
4
|
-
description = "Python SDK for Exa API."
|
|
5
|
-
authors = ["Exa AI <hello@exa.ai>"]
|
|
6
|
-
readme = "README.md"
|
|
7
|
-
|
|
8
1
|
[tool.poetry.dependencies]
|
|
9
2
|
python = "^3.9"
|
|
10
3
|
requests = "^2.32.3"
|
|
@@ -32,7 +25,7 @@ in-project = true
|
|
|
32
25
|
|
|
33
26
|
[project]
|
|
34
27
|
name = "exa-py"
|
|
35
|
-
version = "1.14.
|
|
28
|
+
version = "1.14.5"
|
|
36
29
|
description = "Python SDK for Exa API."
|
|
37
30
|
readme = "README.md"
|
|
38
31
|
requires-python = ">=3.9"
|
|
@@ -63,7 +63,7 @@ def test_research_client_attrs():
|
|
|
63
63
|
def test_get_contents_live_preferred():
|
|
64
64
|
exa = Exa(API_KEY)
|
|
65
65
|
resp = exa.get_contents(urls=["https://techcrunch.com"], text=True, livecrawl="preferred")
|
|
66
|
-
assert isinstance(resp, exa_api.
|
|
66
|
+
assert isinstance(resp, exa_api.SearchResponse)
|
|
67
67
|
# statuses may be empty when cached – still fine
|
|
68
68
|
assert len(resp.results) >= 1
|
|
69
69
|
|
|
@@ -104,4 +104,33 @@ def test_research_task_live():
|
|
|
104
104
|
exa = Exa(API_KEY)
|
|
105
105
|
schema = {"type": "object", "properties": {"answer": {"type": "string"}}, "required": ["answer"]}
|
|
106
106
|
resp = exa.researchTask(input_instructions="Return the string 'pong'", output_schema=schema)
|
|
107
|
-
assert resp.id
|
|
107
|
+
assert resp.id
|
|
108
|
+
|
|
109
|
+
########################################
|
|
110
|
+
# Live tests for new context / statuses features
|
|
111
|
+
########################################
|
|
112
|
+
|
|
113
|
+
@pytest.mark.skipif(not _have_real_key(), reason="EXA_API_KEY not provided")
|
|
114
|
+
def test_search_and_contents_context_live():
|
|
115
|
+
"""search_and_contents with context=True should return non-empty context string."""
|
|
116
|
+
exa = Exa(API_KEY)
|
|
117
|
+
resp = exa.search_and_contents("openai research", num_results=3, context=True, text=False)
|
|
118
|
+
assert resp.context is not None and isinstance(resp.context, str) and len(resp.context) > 0
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@pytest.mark.skipif(not _have_real_key(), reason="EXA_API_KEY not provided")
|
|
122
|
+
def test_find_similar_and_contents_context_live():
|
|
123
|
+
"""find_similar_and_contents with context flag should include context string."""
|
|
124
|
+
exa = Exa(API_KEY)
|
|
125
|
+
resp = exa.find_similar_and_contents("https://www.openai.com", num_results=3, context=True, text=False)
|
|
126
|
+
# context may be empty depending on backend, but attribute should exist (None or str)
|
|
127
|
+
assert hasattr(resp, "context")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.mark.skipif(not _have_real_key(), reason="EXA_API_KEY not provided")
|
|
131
|
+
def test_get_contents_statuses_live():
|
|
132
|
+
"""get_contents should expose statuses list (possibly empty)."""
|
|
133
|
+
exa = Exa(API_KEY)
|
|
134
|
+
resp = exa.get_contents(urls=["https://techcrunch.com"], text=True, livecrawl="never")
|
|
135
|
+
# statuses attribute exists; ensure it's a list
|
|
136
|
+
assert isinstance(resp.statuses, list)
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|