exa-py 1.12.1__py3-none-any.whl → 1.13.0__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
@@ -834,6 +834,36 @@ def nest_fields(original_dict: Dict, fields_to_nest: List[str], new_key: str):
834
834
 
835
835
  return original_dict
836
836
 
837
+ @dataclass
838
+ class ResearchTaskResponse:
839
+ """A class representing the response for a research task.
840
+
841
+ Attributes:
842
+ id (str): The unique identifier for the research request.
843
+ status (str): Status of the research request.
844
+ output (Optional[Dict[str, Any]]): The answer structured as JSON, if available.
845
+ citations (Optional[Dict[str, List[_Result]]]): List of citations used to generate the answer, grouped by root field in the output schema.
846
+ """
847
+
848
+ id: str
849
+ status: str
850
+ output: Optional[Dict[str, Any]]
851
+ citations: Dict[str, List[_Result]]
852
+
853
+ def __str__(self):
854
+ output_repr = (
855
+ json.dumps(self.output, indent=2, ensure_ascii=False)
856
+ if self.output is not None
857
+ else "None"
858
+ )
859
+ citations_str = "\n\n".join(str(src) for src in self.citations)
860
+ return (
861
+ f"ID: {self.id}\n"
862
+ f"Status: {self.status}\n"
863
+ f"Output: {output_repr}\n\n"
864
+ f"Citations:\n{citations_str}"
865
+ )
866
+
837
867
 
838
868
  class Exa:
839
869
  """A client for interacting with Exa API."""
@@ -1911,6 +1941,37 @@ class Exa:
1911
1941
  raw_response = self.request("/answer", options)
1912
1942
  return StreamAnswerResponse(raw_response)
1913
1943
 
1944
+ def researchTask(
1945
+ self,
1946
+ *,
1947
+ input_instructions: str,
1948
+ output_schema: Dict[str, Any],
1949
+ ) -> ResearchTaskResponse:
1950
+ """Submit a research request to Exa.
1951
+
1952
+ Args:
1953
+ input_instructions (str): The instructions for the research task.
1954
+ output_schema (Dict[str, Any]): JSON schema describing the desired answer structure.
1955
+ """
1956
+ # Build the request payload expected by the Exa API
1957
+ options = {
1958
+ "input": {"instructions": input_instructions},
1959
+ "output": {"schema": output_schema},
1960
+ }
1961
+
1962
+ response = self.request("/research/tasks", options)
1963
+
1964
+ return ResearchTaskResponse(
1965
+ id=response["id"],
1966
+ status=response["status"],
1967
+ output=response.get("output"),
1968
+ citations={
1969
+ key: [_Result(**to_snake_case(citation)) for citation in citations_list]
1970
+ for key, citations_list in response.get("citations", {}).items()
1971
+ },
1972
+ )
1973
+
1974
+
1914
1975
  class AsyncExa(Exa):
1915
1976
  def __init__(self, api_key: str, api_base: str = "https://api.exa.ai"):
1916
1977
  super().__init__(api_key, api_base)
@@ -2244,3 +2305,36 @@ class AsyncExa(Exa):
2244
2305
  options["stream"] = True
2245
2306
  raw_response = await self.async_request("/answer", options)
2246
2307
  return AsyncStreamAnswerResponse(raw_response)
2308
+
2309
+ async def researchTask(
2310
+ self,
2311
+ *,
2312
+ input_instructions: str,
2313
+ output_schema: Dict[str, Any],
2314
+ ) -> ResearchTaskResponse:
2315
+ """Asynchronously submit a research request to Exa.
2316
+
2317
+ Args:
2318
+ input_instructions (str): The instructions for the research task.
2319
+ output_schema (Dict[str, Any]): JSON schema describing the desired answer structure.
2320
+
2321
+ Returns:
2322
+ ResearchTaskResponse: The parsed response from the Exa API.
2323
+ """
2324
+ # Build the request payload expected by the Exa API
2325
+ options = {
2326
+ "input": {"instructions": input_instructions},
2327
+ "output": {"schema": output_schema},
2328
+ }
2329
+
2330
+ response = await self.async_request("/research/tasks", options)
2331
+
2332
+ return ResearchTaskResponse(
2333
+ id=response["id"],
2334
+ status=response["status"],
2335
+ output=response.get("output"),
2336
+ citations={
2337
+ key: [_Result(**to_snake_case(citation)) for citation in citations_list]
2338
+ for key, citations_list in response.get("citations", {}).items()
2339
+ },
2340
+ )
@@ -1,25 +1,21 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: exa-py
3
- Version: 1.12.1
3
+ Version: 1.13.0
4
4
  Summary: Python SDK for Exa API.
5
+ Home-page: https://github.com/exa-labs/exa-py
6
+ Author: Exa
7
+ Author-email: Exa AI <hello@exa.ai>
5
8
  License: MIT
6
- Author: Exa AI
7
- Author-email: hello@exa.ai
8
9
  Requires-Python: >=3.9
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Requires-Dist: httpx (>=0.28.1)
17
- Requires-Dist: openai (>=1.48)
18
- Requires-Dist: pydantic (>=2.10.6)
19
- Requires-Dist: pytest-mock (>=3.14.0)
20
- Requires-Dist: requests (>=2.32.3)
21
- Requires-Dist: typing-extensions (>=4.12.2)
22
10
  Description-Content-Type: text/markdown
11
+ Requires-Dist: requests>=2.32.3
12
+ Requires-Dist: typing-extensions>=4.12.2
13
+ Requires-Dist: openai>=1.48
14
+ Requires-Dist: pydantic>=2.10.6
15
+ Requires-Dist: pytest-mock>=3.14.0
16
+ Requires-Dist: httpx>=0.28.1
17
+ Dynamic: author
18
+ Dynamic: home-page
23
19
 
24
20
  # Exa
25
21
 
@@ -50,9 +46,6 @@ exa = Exa(api_key="your-api-key")
50
46
  # basic search
51
47
  results = exa.search("This is a Exa query:")
52
48
 
53
- # autoprompted search
54
- results = exa.search("autopromptable query", use_autoprompt=True)
55
-
56
49
  # keyword search (non-neural)
57
50
  results = exa.search("Google-style query", type="keyword")
58
51
 
@@ -65,13 +58,9 @@ exa = Exa(api_key="your-api-key")
65
58
  # search and get text contents
66
59
  results = exa.search_and_contents("This is a Exa query:")
67
60
 
68
- # search and get highlights
69
- results = exa.search_and_contents("This is a Exa query:", highlights=True)
70
-
71
61
  # search and get contents with contents options
72
62
  results = exa.search_and_contents("This is a Exa query:",
73
- text={"include_html_tags": True, "max_characters": 1000},
74
- highlights={"highlights_per_url": 2, "num_sentences": 1, "query": "This is the highlight query:"})
63
+ text={"include_html_tags": True, "max_characters": 1000})
75
64
 
76
65
  # find similar documents
77
66
  results = exa.find_similar("https://example.com")
@@ -80,18 +69,14 @@ exa = Exa(api_key="your-api-key")
80
69
  results = exa.find_similar("https://example.com", exclude_source_domain=True)
81
70
 
82
71
  # find similar with contents
83
- results = exa.find_similar_and_contents("https://example.com", text=True, highlights=True)
72
+ results = exa.find_similar_and_contents("https://example.com", text=True)
84
73
 
85
74
  # get text contents
86
- results = exa.get_contents(["urls"])
87
-
88
- # get highlights
89
- results = exa.get_contents(["urls"], highlights=True)
75
+ results = exa.get_contents(["tesla.com"])
90
76
 
91
77
  # get contents with contents options
92
78
  results = exa.get_contents(["urls"],
93
- text={"include_html_tags": True, "max_characters": 1000},
94
- highlights={"highlights_per_url": 2, "num_sentences": 1, "query": "This is the highlight query:"})
79
+ text={"include_html_tags": True, "max_characters": 1000})
95
80
 
96
81
  # basic answer
97
82
  response = exa.answer("This is a query to answer a question")
@@ -108,4 +93,3 @@ exa = Exa(api_key="your-api-key")
108
93
 
109
94
  ```
110
95
 
111
-
@@ -1,10 +1,10 @@
1
1
  exa_py/__init__.py,sha256=M2GC9oSdoV6m2msboW0vMWWl8wrth4o6gmEV4MYLGG8,66
2
- exa_py/api.py,sha256=pY--ciwm4n8Mx7ojf8OF24piFjjd7WPT1M3U7yARV-M,83941
2
+ exa_py/api.py,sha256=0Xa2s-ZhOI4mynqZ6zZQW5tWwRK_SNQNUvmwhEPPk4s,87147
3
3
  exa_py/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  exa_py/utils.py,sha256=Rc1FJjoR9LQ7L_OJM91Sd1GNkbHjcLyEvJENhRix6gc,2405
5
5
  exa_py/websets/__init__.py,sha256=uOBAb9VrIHrPKoddGOp2ai2KgWlyUVCLMZqfbGOlboA,70
6
- exa_py/websets/_generator/pydantic/BaseModel.jinja2,sha256=RUDCmPZVamoVx1WudylscYFfDhGoNNtRYlpTvKjAiuA,1276
7
6
  exa_py/websets/client.py,sha256=GWHebkvfiGY46sIuksAhYE1RLJrHQVS2PGhlA3xbxhE,4757
7
+ exa_py/websets/types.py,sha256=jKnJFAHTFN55EzsusgDce-yux71zVbdSJ1m8utR4EjU,28096
8
8
  exa_py/websets/core/__init__.py,sha256=xOyrFaqtBocMUu321Jpbk7IzIQRNZufSIGJXrKoG-Bg,323
9
9
  exa_py/websets/core/base.py,sha256=2vCkPLEKI2K2U7d-76kqVL7qyye007iDdp15GseJN1c,3936
10
10
  exa_py/websets/enrichments/__init__.py,sha256=5dJIEKKceUost3RnI6PpCSB3VjUCBzxseEsIXu-ZY-Y,83
@@ -13,9 +13,9 @@ exa_py/websets/items/__init__.py,sha256=DCWZJVtRmUjnMEkKdb5gW1LT9cHcb-J8lENMnyyB
13
13
  exa_py/websets/items/client.py,sha256=oZoYr52WrE76Ox6GyoS9rMn7bTrIpno0FKgIWFtb57U,2796
14
14
  exa_py/websets/searches/__init__.py,sha256=_0Zx8ES5fFTEL3T8mhLxq_xK2t0JONx6ad6AtbvClsE,77
15
15
  exa_py/websets/searches/client.py,sha256=X3f7axWGfecmxf-2tBTX0Yf_--xToz1X8ZHbbudEzy0,1790
16
- exa_py/websets/types.py,sha256=jKnJFAHTFN55EzsusgDce-yux71zVbdSJ1m8utR4EjU,28096
17
16
  exa_py/websets/webhooks/__init__.py,sha256=iTPBCxFd73z4RifLQMX6iRECx_6pwlI5qscLNjMOUHE,77
18
17
  exa_py/websets/webhooks/client.py,sha256=zsIRMTeJU65yj-zo7Zz-gG02Prtzgcx6utGFSoY4HQQ,4222
19
- exa_py-1.12.1.dist-info/METADATA,sha256=Pk_iVbhNBMLmO5e5D_rA0rj8mLlCP9TrbwaqXKlbX_c,3565
20
- exa_py-1.12.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
21
- exa_py-1.12.1.dist-info/RECORD,,
18
+ exa_py-1.13.0.dist-info/METADATA,sha256=gRNplBW67MFSW6rKGHP-i_1R6VhyNy68Yxjh4n07rsA,2726
19
+ exa_py-1.13.0.dist-info/WHEEL,sha256=FBI2p9UMPaLApXXRpz7jgjcaDrkbla1BGsqZ5aB70OM,97
20
+ exa_py-1.13.0.dist-info/top_level.txt,sha256=Mfkmscdw9HWR1PtVhU1gAiVo6DHu_tyiVdb89gfZBVI,7
21
+ exa_py-1.13.0.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.2
2
+ Generator: setuptools (78.1.0.post0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ exa_py
@@ -1,42 +0,0 @@
1
- {% for decorator in decorators -%}
2
- {{ decorator }}
3
- {% endfor -%}
4
- class {{ class_name }}({{ base_class }}):{% if comment is defined %} # {{ comment }}{% endif %}
5
- {%- if description %}
6
- """
7
- {{ description | indent(4) }}
8
- """
9
- {%- endif %}
10
- {%- if not fields and not description %}
11
- pass
12
- {%- endif %}
13
- {%- if config %}
14
- {%- filter indent(4) %}
15
- {%- endfilter %}
16
- {%- endif %}
17
- {%- for field in fields -%}
18
- {%- if field.name == "type" and field.field %}
19
- type: Literal['{{ field.default }}']
20
- {%- elif field.name == "object" and field.field %}
21
- object: Literal['{{ field.default }}']
22
- {%- elif not field.annotated and field.field %}
23
- {{ field.name }}: {{ field.type_hint }} = {{ field.field }}
24
- {%- else %}
25
- {%- if field.annotated %}
26
- {{ field.name }}: {{ field.annotated }}
27
- {%- else %}
28
- {{ field.name }}: {{ field.type_hint }}
29
- {%- endif %}
30
- {%- if not (field.required or (field.represented_default == 'None' and field.strip_default_none)) or field.data_type.is_optional
31
- %} = {{ field.represented_default }}
32
- {%- endif -%}
33
- {%- endif %}
34
- {%- if field.docstring %}
35
- """
36
- {{ field.docstring | indent(4) }}
37
- """
38
- {%- endif %}
39
- {%- for method in methods -%}
40
- {{ method }}
41
- {%- endfor -%}
42
- {%- endfor -%}