exa-py 1.14.20__py3-none-any.whl → 1.15.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/research/models.py CHANGED
@@ -1,120 +1,321 @@
1
1
  from __future__ import annotations
2
2
 
3
- import json
4
- from dataclasses import dataclass
5
- from typing import Any, Dict, List, Optional
6
-
7
- # Local import placed inside TYPE_CHECKING block to avoid runtime cycles.
8
- from typing import TYPE_CHECKING
9
-
10
- if TYPE_CHECKING: # pragma: no cover – for static analysers only
11
- from ..api import _Result # noqa: F401
12
-
13
-
14
- @dataclass
15
- class ResearchTaskId:
16
- """Structured research task ID.
17
-
18
- Attributes
19
- ----------
20
- id:
21
- Unique identifier for the research task.
22
- """
23
-
24
- id: str
25
-
26
- # ---------------------------------------------------------------------
27
- # Pretty representation helpers
28
- # ---------------------------------------------------------------------
29
- def __str__(self) -> str: # pragma: no cover – convenience only
30
- return f"ID: {self.id}\n"
31
-
32
-
33
- @dataclass
34
- class ResearchTask:
35
- """Structured research task.
36
-
37
- Attributes
38
- ----------
39
- id:
40
- Unique identifier for the research task.
41
- status:
42
- Current task status
43
- instructions:
44
- Instructions for the task
45
- schema:
46
- Output schema defining the task
47
- data:
48
- JSON-serialisable answer generated by Exa (may be ``None`` until the task
49
- completes).
50
- citations:
51
- Mapping from *root field* in the output schema to the list of search
52
- results that were used to generate that part of the answer.
53
- """
54
-
55
- id: str
56
- status: str
3
+ from typing import Annotated, Any, Dict, List, Literal, Optional, Union
4
+
5
+ from pydantic import BaseModel, Field, RootModel
6
+
7
+
8
+ class CostDollars(BaseModel):
9
+ total: float
10
+ num_pages: Annotated[float, Field(alias="numPages")]
11
+ num_searches: Annotated[float, Field(alias="numSearches")]
12
+ reasoning_tokens: Annotated[float, Field(alias="reasoningTokens")]
13
+
14
+
15
+ class Result(BaseModel):
16
+ url: str
17
+
18
+
19
+ class ResearchThinkOperation(BaseModel):
20
+ type: Literal["think"]
21
+ content: str
22
+
23
+
24
+ class ResearchSearchOperation(BaseModel):
25
+ type: Literal["search"]
26
+ search_type: Annotated[
27
+ Literal["neural", "keyword", "auto", "fast"], Field(alias="searchType")
28
+ ]
29
+ query: str
30
+ results: List[Result]
31
+ page_tokens: Annotated[float, Field(alias="pageTokens")]
32
+ goal: Optional[str] = None
33
+
34
+
35
+ class ResearchCrawlOperation(BaseModel):
36
+ type: Literal["crawl"]
37
+ result: Result
38
+ page_tokens: Annotated[float, Field(alias="pageTokens")]
39
+ goal: Optional[str] = None
40
+
41
+
42
+ ResearchOperation = Annotated[
43
+ Union[ResearchThinkOperation, ResearchSearchOperation, ResearchCrawlOperation],
44
+ Field(discriminator="type"),
45
+ ]
46
+
47
+
48
+ class ResearchDefinitionEvent(BaseModel):
49
+ event_type: Annotated[Literal["research-definition"], Field(alias="eventType")]
50
+ research_id: Annotated[str, Field(alias="researchId")]
51
+ created_at: Annotated[
52
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
53
+ ]
54
+ instructions: str
55
+ output_schema: Annotated[Optional[Dict[str, Any]], Field(alias="outputSchema")] = (
56
+ None
57
+ )
58
+
59
+
60
+ class ResearchOutputCompleted(BaseModel):
61
+ output_type: Annotated[Literal["completed"], Field(alias="outputType")]
62
+ content: str
63
+ cost_dollars: Annotated[CostDollars, Field(alias="costDollars")]
64
+ parsed: Optional[Dict[str, Any]] = None
65
+
66
+
67
+ class ResearchOutputFailed(BaseModel):
68
+ output_type: Annotated[Literal["failed"], Field(alias="outputType")]
69
+ error: str
70
+
71
+
72
+ class ResearchOutputEvent(BaseModel):
73
+ event_type: Annotated[Literal["research-output"], Field(alias="eventType")]
74
+ research_id: Annotated[str, Field(alias="researchId")]
75
+ created_at: Annotated[
76
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
77
+ ]
78
+ output: Annotated[
79
+ Union[ResearchOutputCompleted, ResearchOutputFailed],
80
+ Field(discriminator="output_type"),
81
+ ]
82
+
83
+
84
+ class ResearchPlanDefinitionEvent(BaseModel):
85
+ event_type: Annotated[Literal["plan-definition"], Field(alias="eventType")]
86
+ research_id: Annotated[str, Field(alias="researchId")]
87
+ plan_id: Annotated[str, Field(alias="planId")]
88
+ created_at: Annotated[
89
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
90
+ ]
91
+
92
+
93
+ class ResearchPlanOperationEvent(BaseModel):
94
+ event_type: Annotated[Literal["plan-operation"], Field(alias="eventType")]
95
+ research_id: Annotated[str, Field(alias="researchId")]
96
+ plan_id: Annotated[str, Field(alias="planId")]
97
+ operation_id: Annotated[str, Field(alias="operationId")]
98
+ created_at: Annotated[
99
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
100
+ ]
101
+ data: ResearchOperation
102
+
103
+
104
+ class ResearchPlanOutputTasks(BaseModel):
105
+ output_type: Annotated[Literal["tasks"], Field(alias="outputType")]
106
+ reasoning: str
107
+ tasks_instructions: Annotated[List[str], Field(alias="tasksInstructions")]
108
+
109
+
110
+ class ResearchPlanOutputStop(BaseModel):
111
+ output_type: Annotated[Literal["stop"], Field(alias="outputType")]
112
+ reasoning: str
113
+
114
+
115
+ class ResearchPlanOutputEvent(BaseModel):
116
+ event_type: Annotated[Literal["plan-output"], Field(alias="eventType")]
117
+ research_id: Annotated[str, Field(alias="researchId")]
118
+ plan_id: Annotated[str, Field(alias="planId")]
119
+ created_at: Annotated[
120
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
121
+ ]
122
+ output: Annotated[
123
+ Union[ResearchPlanOutputTasks, ResearchPlanOutputStop],
124
+ Field(discriminator="output_type"),
125
+ ]
126
+
127
+
128
+ class ResearchTaskDefinitionEvent(BaseModel):
129
+ event_type: Annotated[Literal["task-definition"], Field(alias="eventType")]
130
+ research_id: Annotated[str, Field(alias="researchId")]
131
+ plan_id: Annotated[str, Field(alias="planId")]
132
+ task_id: Annotated[str, Field(alias="taskId")]
133
+ created_at: Annotated[
134
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
135
+ ]
57
136
  instructions: str
58
- schema: Dict[str, Any]
59
- data: Optional[Dict[str, Any]]
60
- citations: Dict[str, List["_Result"]]
61
-
62
- # ---------------------------------------------------------------------
63
- # Pretty representation helpers
64
- # ---------------------------------------------------------------------
65
- def __str__(self) -> str: # pragma: no cover – convenience only
66
- """Human-readable representation including *all* relevant fields."""
67
- schema_repr = json.dumps(self.schema, indent=2, ensure_ascii=False)
68
- data_repr = (
69
- json.dumps(self.data, indent=2, ensure_ascii=False)
70
- if self.data is not None
71
- else "None"
72
- )
73
-
74
- # Render citations grouped by the root field they belong to.
75
- if self.citations:
76
- # Each key is a root field, each value is a list of _Result objects.
77
- citations_lines = []
78
- for field, sources in self.citations.items():
79
- rendered_sources = "\n ".join(str(src) for src in sources)
80
- citations_lines.append(f"{field}:\n {rendered_sources}")
81
- citations_str = "\n\n".join(citations_lines)
82
- else:
83
- citations_str = "None"
84
-
85
- return (
86
- f"ID: {self.id}\n"
87
- f"Status: {self.status}\n"
88
- f"Instructions: {self.instructions}\n"
89
- f"Schema:\n{schema_repr}\n"
90
- f"Data:\n{data_repr}\n\n"
91
- f"Citations:\n{citations_str}"
92
- )
93
-
94
-
95
- @dataclass
96
- class ListResearchTasksResponse:
97
- """Paginated list of research tasks."""
98
-
99
- data: List[ResearchTask]
100
- has_more: bool
101
- next_cursor: Optional[str]
102
-
103
- # -----------------------------------------------------------------
104
- # Pretty representation helpers
105
- # -----------------------------------------------------------------
106
- def __str__(self) -> str: # pragma: no cover – convenience only
107
- tasks_repr = "\n\n".join(str(task) for task in self.data)
108
- cursor_repr = self.next_cursor or "None"
109
- return (
110
- f"Tasks:\n{tasks_repr}\n\n"
111
- f"Has more: {self.has_more}\n"
112
- f"Next cursor: {cursor_repr}"
113
- )
137
+
138
+
139
+ class ResearchTaskOperationEvent(BaseModel):
140
+ event_type: Annotated[Literal["task-operation"], Field(alias="eventType")]
141
+ research_id: Annotated[str, Field(alias="researchId")]
142
+ plan_id: Annotated[str, Field(alias="planId")]
143
+ task_id: Annotated[str, Field(alias="taskId")]
144
+ operation_id: Annotated[str, Field(alias="operationId")]
145
+ created_at: Annotated[
146
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
147
+ ]
148
+ data: ResearchOperation
149
+
150
+
151
+ class ResearchTaskOutput(BaseModel):
152
+ output_type: Annotated[Literal["completed"], Field(alias="outputType")]
153
+ content: str
154
+
155
+
156
+ class ResearchTaskOutputEvent(BaseModel):
157
+ event_type: Annotated[Literal["task-output"], Field(alias="eventType")]
158
+ research_id: Annotated[str, Field(alias="researchId")]
159
+ plan_id: Annotated[str, Field(alias="planId")]
160
+ task_id: Annotated[str, Field(alias="taskId")]
161
+ created_at: Annotated[
162
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
163
+ ]
164
+ output: ResearchTaskOutput
165
+
166
+
167
+ ResearchMetaEvent = Union[ResearchDefinitionEvent, ResearchOutputEvent]
168
+ ResearchPlanEvent = Union[
169
+ ResearchPlanDefinitionEvent, ResearchPlanOperationEvent, ResearchPlanOutputEvent
170
+ ]
171
+ ResearchTaskEvent = Union[
172
+ ResearchTaskDefinitionEvent, ResearchTaskOperationEvent, ResearchTaskOutputEvent
173
+ ]
174
+ ResearchEvent = Union[ResearchMetaEvent, ResearchPlanEvent, ResearchTaskEvent]
175
+
176
+
177
+ class ResearchOutput(BaseModel):
178
+ content: str
179
+ parsed: Optional[Dict[str, Any]] = None
180
+
181
+
182
+ class ResearchBaseDto(BaseModel):
183
+ research_id: Annotated[
184
+ str,
185
+ Field(
186
+ alias="researchId",
187
+ description="The unique identifier for the research request",
188
+ ),
189
+ ]
190
+ created_at: Annotated[
191
+ float, Field(alias="createdAt", description="Milliseconds since epoch time")
192
+ ]
193
+ model: Annotated[
194
+ Literal["exa-research", "exa-research-pro"],
195
+ Field(description="The model used for the research request"),
196
+ ] = "exa-research"
197
+ instructions: Annotated[
198
+ str, Field(description="The instructions given to this research request")
199
+ ]
200
+
201
+
202
+ class ResearchPendingDto(ResearchBaseDto):
203
+ status: Literal["pending"]
204
+
205
+
206
+ class ResearchRunningDto(ResearchBaseDto):
207
+ status: Literal["running"]
208
+ events: Optional[List[ResearchEvent]] = None
209
+
210
+
211
+ class ResearchCompletedDto(ResearchBaseDto):
212
+ status: Literal["completed"]
213
+ events: Optional[List[ResearchEvent]] = None
214
+ output: ResearchOutput
215
+ cost_dollars: Annotated[CostDollars, Field(alias="costDollars")]
216
+
217
+
218
+ class ResearchCanceledDto(ResearchBaseDto):
219
+ status: Literal["canceled"]
220
+ events: Optional[List[ResearchEvent]] = None
221
+
222
+
223
+ class ResearchFailedDto(ResearchBaseDto):
224
+ status: Literal["failed"]
225
+ events: Optional[List[ResearchEvent]] = None
226
+ error: Annotated[
227
+ str, Field(description="A message indicating why the request failed")
228
+ ]
229
+
230
+
231
+ ResearchDto = Annotated[
232
+ Union[
233
+ ResearchPendingDto,
234
+ ResearchRunningDto,
235
+ ResearchCompletedDto,
236
+ ResearchCanceledDto,
237
+ ResearchFailedDto,
238
+ ],
239
+ Field(discriminator="status"),
240
+ ]
241
+
242
+
243
+ class ListResearchResponseDto(BaseModel):
244
+ data: Annotated[
245
+ List[ResearchDto], Field(description="The list of research requests")
246
+ ]
247
+ has_more: Annotated[
248
+ bool,
249
+ Field(
250
+ alias="hasMore",
251
+ description="Whether there are more results to paginate through",
252
+ ),
253
+ ]
254
+ next_cursor: Annotated[
255
+ Optional[str],
256
+ Field(
257
+ alias="nextCursor",
258
+ description="The cursor to paginate through the next set of results",
259
+ ),
260
+ ]
261
+
262
+
263
+ class ResearchCreateRequestDto(BaseModel):
264
+ model: Literal["exa-research", "exa-research-pro"] = "exa-research"
265
+ instructions: Annotated[
266
+ str,
267
+ Field(
268
+ description="Instructions for what research should be conducted",
269
+ max_length=4096,
270
+ ),
271
+ ]
272
+ output_schema: Annotated[Optional[Dict[str, Any]], Field(alias="outputSchema")] = (
273
+ None
274
+ )
275
+
276
+
277
+ ResearchDtoClass = RootModel[ResearchDto]
278
+ ResearchCreateRequestDtoClass = ResearchCreateRequestDto
279
+ ResearchEventDtoClass = RootModel[ResearchEvent]
280
+ ResearchOperationDtoClass = RootModel[ResearchOperation]
114
281
 
115
282
 
116
283
  __all__ = [
117
- "ResearchTaskId",
118
- "ResearchTask",
119
- "ListResearchTasksResponse",
284
+ "CostDollars",
285
+ "Result",
286
+ "ResearchThinkOperation",
287
+ "ResearchSearchOperation",
288
+ "ResearchCrawlOperation",
289
+ "ResearchOperation",
290
+ "ResearchDefinitionEvent",
291
+ "ResearchOutputCompleted",
292
+ "ResearchOutputFailed",
293
+ "ResearchOutputEvent",
294
+ "ResearchPlanDefinitionEvent",
295
+ "ResearchPlanOperationEvent",
296
+ "ResearchPlanOutputTasks",
297
+ "ResearchPlanOutputStop",
298
+ "ResearchPlanOutputEvent",
299
+ "ResearchTaskDefinitionEvent",
300
+ "ResearchTaskOperationEvent",
301
+ "ResearchTaskOutput",
302
+ "ResearchTaskOutputEvent",
303
+ "ResearchMetaEvent",
304
+ "ResearchPlanEvent",
305
+ "ResearchTaskEvent",
306
+ "ResearchEvent",
307
+ "ResearchOutput",
308
+ "ResearchBaseDto",
309
+ "ResearchPendingDto",
310
+ "ResearchRunningDto",
311
+ "ResearchCompletedDto",
312
+ "ResearchCanceledDto",
313
+ "ResearchFailedDto",
314
+ "ResearchDto",
315
+ "ListResearchResponseDto",
316
+ "ResearchCreateRequestDto",
317
+ "ResearchDtoClass",
318
+ "ResearchCreateRequestDtoClass",
319
+ "ResearchEventDtoClass",
320
+ "ResearchOperationDtoClass",
120
321
  ]