exa-py 1.14.6__tar.gz → 1.14.7__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.

Files changed (30) hide show
  1. {exa_py-1.14.6 → exa_py-1.14.7}/PKG-INFO +1 -1
  2. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/__init__.py +2 -0
  3. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/client.py +2 -0
  4. exa_py-1.14.7/exa_py/websets/imports/__init__.py +3 -0
  5. exa_py-1.14.7/exa_py/websets/imports/client.py +179 -0
  6. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/types.py +299 -1
  7. {exa_py-1.14.6 → exa_py-1.14.7}/pyproject.toml +1 -1
  8. {exa_py-1.14.6 → exa_py-1.14.7}/README.md +0 -0
  9. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/__init__.py +0 -0
  10. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/api.py +0 -0
  11. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/py.typed +0 -0
  12. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/research/__init__.py +0 -0
  13. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/research/client.py +0 -0
  14. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/research/models.py +0 -0
  15. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/utils.py +0 -0
  16. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/_generator/pydantic/BaseModel.jinja2 +0 -0
  17. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/core/__init__.py +0 -0
  18. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/core/base.py +0 -0
  19. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/enrichments/__init__.py +0 -0
  20. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/enrichments/client.py +0 -0
  21. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/items/__init__.py +0 -0
  22. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/items/client.py +0 -0
  23. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/monitors/__init__.py +0 -0
  24. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/monitors/client.py +0 -0
  25. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/monitors/runs/__init__.py +0 -0
  26. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/monitors/runs/client.py +0 -0
  27. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/searches/__init__.py +0 -0
  28. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/searches/client.py +0 -0
  29. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/webhooks/__init__.py +0 -0
  30. {exa_py-1.14.6 → exa_py-1.14.7}/exa_py/websets/webhooks/client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: exa-py
3
- Version: 1.14.6
3
+ Version: 1.14.7
4
4
  Summary: Python SDK for Exa API.
5
5
  License: MIT
6
6
  Author: Exa AI
@@ -1,5 +1,7 @@
1
1
  from .client import WebsetsClient
2
+ from .imports import ImportsClient
2
3
 
3
4
  __all__ = [
4
5
  "WebsetsClient",
6
+ "ImportsClient",
5
7
  ]
@@ -17,6 +17,7 @@ from .searches import WebsetSearchesClient
17
17
  from .enrichments import WebsetEnrichmentsClient
18
18
  from .webhooks import WebsetWebhooksClient
19
19
  from .monitors import MonitorsClient
20
+ from .imports import ImportsClient
20
21
 
21
22
  class WebsetsClient(WebsetsBaseClient):
22
23
  """Client for managing Websets."""
@@ -28,6 +29,7 @@ class WebsetsClient(WebsetsBaseClient):
28
29
  self.enrichments = WebsetEnrichmentsClient(client)
29
30
  self.webhooks = WebsetWebhooksClient(client)
30
31
  self.monitors = MonitorsClient(client)
32
+ self.imports = ImportsClient(client)
31
33
 
32
34
  def create(self, params: Union[Dict[str, Any], CreateWebsetParameters]) -> Webset:
33
35
  """Create a new Webset.
@@ -0,0 +1,3 @@
1
+ from .client import ImportsClient
2
+
3
+ __all__ = ["ImportsClient"]
@@ -0,0 +1,179 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ import os
5
+ import csv
6
+ import io
7
+ import requests
8
+ from typing import Dict, Any, Union, Optional
9
+ from pathlib import Path
10
+
11
+ from ..types import (
12
+ CreateImportParameters,
13
+ CreateImportResponse,
14
+ Import,
15
+ ListImportsResponse,
16
+ UpdateImport,
17
+ )
18
+ from ..core.base import WebsetsBaseClient
19
+
20
+ class ImportsClient(WebsetsBaseClient):
21
+ """Client for managing Imports."""
22
+
23
+ def __init__(self, client):
24
+ super().__init__(client)
25
+
26
+ def create(
27
+ self,
28
+ params: Union[Dict[str, Any], CreateImportParameters],
29
+ *,
30
+ csv_data: Optional[Union[str, Path]] = None
31
+ ) -> Union[CreateImportResponse, Import]:
32
+ """Create a new import to upload your data into Websets.
33
+
34
+ Imports can be used to:
35
+ - Enrich: Enhance your data with additional information using our AI-powered enrichment engine
36
+ - Search: Query your data using Websets' agentic search with natural language filters
37
+ - Exclude: Prevent duplicate or already known results from appearing in your searches
38
+
39
+ Args:
40
+ params (CreateImportParameters): The parameters for creating an import.
41
+ csv_data (Union[str, Path], optional): CSV data to upload. Can be raw CSV string or file path.
42
+ When provided, size and count will be automatically calculated
43
+ if not specified in params.
44
+
45
+ Returns:
46
+ CreateImportResponse: If csv_data is None (traditional usage with upload URL).
47
+ Import: If csv_data is provided (uploaded and processing).
48
+ """
49
+ if csv_data is not None:
50
+ if isinstance(csv_data, (str, Path)) and os.path.isfile(csv_data):
51
+ with open(csv_data, 'r', encoding='utf-8') as f:
52
+ csv_content = f.read()
53
+ else:
54
+ csv_content = str(csv_data)
55
+
56
+ if isinstance(params, dict):
57
+ current_size = params.get('size')
58
+ current_count = params.get('count')
59
+ else:
60
+ current_size = getattr(params, 'size', None)
61
+ current_count = getattr(params, 'count', None)
62
+
63
+ if current_size is None or current_count is None:
64
+ calculated_size = len(csv_content.encode('utf-8'))
65
+ csv_reader = csv.reader(io.StringIO(csv_content))
66
+ rows = list(csv_reader)
67
+ calculated_count = max(0, len(rows) - 1)
68
+
69
+ if isinstance(params, dict):
70
+ params = params.copy()
71
+ if current_size is None:
72
+ params['size'] = calculated_size
73
+ if current_count is None:
74
+ params['count'] = calculated_count
75
+ else:
76
+ params_dict = params.model_dump()
77
+ if current_size is None:
78
+ params_dict['size'] = calculated_size
79
+ if current_count is None:
80
+ params_dict['count'] = calculated_count
81
+ params = CreateImportParameters.model_validate(params_dict)
82
+
83
+ response = self.request("/v0/imports", data=params)
84
+ import_response = CreateImportResponse.model_validate(response)
85
+
86
+ if csv_data is None:
87
+ return import_response
88
+
89
+ upload_response = requests.put(
90
+ import_response.upload_url,
91
+ data=csv_content,
92
+ headers={'Content-Type': 'text/csv'}
93
+ )
94
+ upload_response.raise_for_status()
95
+
96
+ return self.get(import_response.id)
97
+
98
+ def get(self, import_id: str) -> Import:
99
+ """Get a specific import.
100
+
101
+ Args:
102
+ import_id (str): The id of the Import.
103
+
104
+ Returns:
105
+ Import: The retrieved import.
106
+ """
107
+ response = self.request(f"/v0/imports/{import_id}", method="GET")
108
+ return Import.model_validate(response)
109
+
110
+ def list(self, *, cursor: Optional[str] = None, limit: Optional[int] = None) -> ListImportsResponse:
111
+ """List all imports.
112
+
113
+ Args:
114
+ cursor (str, optional): The cursor to paginate through the results.
115
+ limit (int, optional): The number of results to return (1-200, default 25).
116
+
117
+ Returns:
118
+ ListImportsResponse: List of imports with pagination info.
119
+ """
120
+ params = {
121
+ k: v
122
+ for k, v in {
123
+ "cursor": cursor,
124
+ "limit": limit
125
+ }.items()
126
+ if v is not None
127
+ }
128
+ response = self.request("/v0/imports", params=params, method="GET")
129
+ return ListImportsResponse.model_validate(response)
130
+
131
+ def update(self, import_id: str, params: Union[Dict[str, Any], UpdateImport]) -> Import:
132
+ """Update an import configuration.
133
+
134
+ Args:
135
+ import_id (str): The id of the Import.
136
+ params (UpdateImport): The parameters for updating an import.
137
+
138
+ Returns:
139
+ Import: The updated import.
140
+ """
141
+ response = self.request(f"/v0/imports/{import_id}", data=params, method="PATCH")
142
+ return Import.model_validate(response)
143
+
144
+ def delete(self, import_id: str) -> Import:
145
+ """Delete an import.
146
+
147
+ Args:
148
+ import_id (str): The id of the Import.
149
+
150
+ Returns:
151
+ Import: The deleted import.
152
+ """
153
+ response = self.request(f"/v0/imports/{import_id}", method="DELETE")
154
+ return Import.model_validate(response)
155
+
156
+ def wait_until_completed(self, import_id: str, *, timeout: int = 1800, poll_interval: int = 5) -> Import:
157
+ """Wait until an import is completed or failed.
158
+
159
+ Args:
160
+ import_id (str): The id of the Import.
161
+ timeout (int, optional): Maximum time to wait in seconds. Defaults to 1800 (30 minutes).
162
+ poll_interval (int, optional): Time to wait between polls in seconds. Defaults to 5.
163
+
164
+ Returns:
165
+ Import: The import once it's completed or failed.
166
+
167
+ Raises:
168
+ TimeoutError: If the import does not complete within the timeout period.
169
+ """
170
+ start_time = time.time()
171
+ while True:
172
+ import_obj = self.get(import_id)
173
+ if import_obj.status in ['completed', 'failed']:
174
+ return import_obj
175
+
176
+ if time.time() - start_time > timeout:
177
+ raise TimeoutError(f"Import {import_id} did not complete within {timeout} seconds")
178
+
179
+ time.sleep(poll_interval)
@@ -93,10 +93,14 @@ class CreateWebhookParameters(ExaBaseModel):
93
93
 
94
94
 
95
95
  class CreateWebsetParameters(ExaBaseModel):
96
- search: CreateWebsetParametersSearch
96
+ search: Optional[CreateWebsetParametersSearch] = None
97
97
  """
98
98
  Create initial search for the Webset.
99
99
  """
100
+ imports: Optional[List[ImportItem]] = Field(None, alias='import')
101
+ """
102
+ Import data from existing Websets and Imports into this Webset.
103
+ """
100
104
  enrichments: Optional[List[CreateEnrichmentParameters]] = None
101
105
  """
102
106
  Add Enrichments for the Webset.
@@ -153,6 +157,10 @@ class CreateWebsetSearchParameters(ExaBaseModel):
153
157
 
154
158
  It's not required to provide your own criteria, we automatically detect the criteria from all the information provided in the query.
155
159
  """
160
+ exclude: Optional[List[ExcludeItem]] = None
161
+ """
162
+ Sources (existing imports or websets) to exclude from search results. Any results found within these sources will be omitted to prevent finding them during search.
163
+ """
156
164
  behavior: Optional[WebsetSearchBehavior] = 'override'
157
165
  """
158
166
  The behavior of the Search when it is added to a Webset.
@@ -219,6 +227,9 @@ class EventType(Enum):
219
227
  webset_search_canceled = 'webset.search.canceled'
220
228
  webset_search_completed = 'webset.search.completed'
221
229
  webset_search_updated = 'webset.search.updated'
230
+ import_created = 'import.created'
231
+ import_completed = 'import.completed'
232
+ import_processing = 'import.processing'
222
233
  webset_export_created = 'webset.export.created'
223
234
  webset_export_completed = 'webset.export.completed'
224
235
  webset_item_created = 'webset.item.created'
@@ -240,6 +251,41 @@ class Format(Enum):
240
251
  phone = 'phone'
241
252
 
242
253
 
254
+ class ImportFormat(Enum):
255
+ """
256
+ The format of the import.
257
+ """
258
+ csv = 'csv'
259
+ webset = 'webset'
260
+
261
+
262
+ class ImportStatus(Enum):
263
+ """
264
+ The status of the import.
265
+ """
266
+ pending = 'pending'
267
+ processing = 'processing'
268
+ completed = 'completed'
269
+ failed = 'failed'
270
+
271
+
272
+ class ImportFailedReason(Enum):
273
+ """
274
+ The reason the import failed.
275
+ """
276
+ invalid_format = 'invalid_format'
277
+ invalid_file_content = 'invalid_file_content'
278
+ missing_identifier = 'missing_identifier'
279
+
280
+
281
+ class ImportSource(Enum):
282
+ """
283
+ The source type for imports and excludes.
284
+ """
285
+ import_ = 'import'
286
+ webset = 'webset'
287
+
288
+
243
289
  class ListEventsResponse(ExaBaseModel):
244
290
  data: List[
245
291
  Union[
@@ -358,6 +404,254 @@ class ListWebsetsResponse(ExaBaseModel):
358
404
  """
359
405
 
360
406
 
407
+ class ImportItem(ExaBaseModel):
408
+ """
409
+ Represents a source to import from.
410
+ """
411
+ source: ImportSource
412
+ """
413
+ The type of source (import or webset)
414
+ """
415
+ id: constr(min_length=1)
416
+ """
417
+ The ID of the source to import from
418
+ """
419
+
420
+
421
+ class ExcludeItem(ExaBaseModel):
422
+ """
423
+ Represents a source to exclude from search results.
424
+ """
425
+ source: ImportSource
426
+ """
427
+ The type of source (import or webset)
428
+ """
429
+ id: constr(min_length=1)
430
+ """
431
+ The ID of the source to exclude
432
+ """
433
+
434
+
435
+ class CsvImportConfig(ExaBaseModel):
436
+ """
437
+ Configuration for CSV imports.
438
+ """
439
+ identifier: Optional[PositiveInt] = None
440
+ """
441
+ Column index containing the key identifier for the entity (e.g., URL). If not provided, will be inferred.
442
+ """
443
+
444
+
445
+ class CreateImportParameters(ExaBaseModel):
446
+ """
447
+ Parameters for creating an import.
448
+ """
449
+ size: Optional[confloat(le=50000000.0)] = None
450
+ """
451
+ The size of the file in bytes. Maximum size is 50 MB.
452
+ Auto-calculated when csv_data is provided and size is not specified.
453
+ """
454
+ count: Optional[float] = None
455
+ """
456
+ The number of records to import
457
+ Auto-calculated when csv_data is provided and count is not specified.
458
+ """
459
+ title: Optional[str] = None
460
+ """
461
+ The title of the import
462
+ """
463
+ format: ImportFormat
464
+ """
465
+ The format of the import data
466
+ """
467
+ entity: Union[
468
+ WebsetCompanyEntity,
469
+ WebsetPersonEntity,
470
+ WebsetArticleEntity,
471
+ WebsetResearchPaperEntity,
472
+ WebsetCustomEntity,
473
+ ]
474
+ """
475
+ The type of entity the import contains
476
+ """
477
+ csv: Optional[CsvImportConfig] = None
478
+ """
479
+ CSV-specific configuration when format is 'csv'
480
+ """
481
+ metadata: Optional[Dict[str, Any]] = None
482
+ """
483
+ Set of key-value pairs you want to associate with this object.
484
+ """
485
+
486
+
487
+ class CreateImportResponse(ExaBaseModel):
488
+ """
489
+ Response from creating an import.
490
+ """
491
+ id: str
492
+ """
493
+ The unique identifier for the Import
494
+ """
495
+ object: Literal['import']
496
+ """
497
+ The type of object
498
+ """
499
+ status: ImportStatus
500
+ """
501
+ The status of the Import
502
+ """
503
+ format: ImportFormat
504
+ """
505
+ The format of the import
506
+ """
507
+ entity: Optional[Union[
508
+ WebsetCompanyEntity,
509
+ WebsetPersonEntity,
510
+ WebsetArticleEntity,
511
+ WebsetResearchPaperEntity,
512
+ WebsetCustomEntity,
513
+ ]] = None
514
+ """
515
+ The type of entity the import contains
516
+ """
517
+ title: str
518
+ """
519
+ The title of the import
520
+ """
521
+ count: float
522
+ """
523
+ The number of entities in the import
524
+ """
525
+ metadata: Dict[str, Any]
526
+ """
527
+ Set of key-value pairs associated with this object
528
+ """
529
+ failed_reason: Optional[ImportFailedReason] = Field(None, alias='failedReason')
530
+ """
531
+ The reason the import failed, if applicable
532
+ """
533
+ failed_at: Optional[datetime] = Field(None, alias='failedAt')
534
+ """
535
+ When the import failed, if applicable
536
+ """
537
+ failed_message: Optional[str] = Field(None, alias='failedMessage')
538
+ """
539
+ A human readable message describing the import failure
540
+ """
541
+ created_at: datetime = Field(..., alias='createdAt')
542
+ """
543
+ When the import was created
544
+ """
545
+ updated_at: datetime = Field(..., alias='updatedAt')
546
+ """
547
+ When the import was last updated
548
+ """
549
+ upload_url: str = Field(..., alias='uploadUrl')
550
+ """
551
+ The URL to upload the file to
552
+ """
553
+ upload_valid_until: str = Field(..., alias='uploadValidUntil')
554
+ """
555
+ The date and time until the upload URL is valid
556
+ """
557
+
558
+
559
+ class Import(ExaBaseModel):
560
+ """
561
+ Represents an import.
562
+ """
563
+ id: str
564
+ """
565
+ The unique identifier for the Import
566
+ """
567
+ object: Literal['import']
568
+ """
569
+ The type of object
570
+ """
571
+ status: ImportStatus
572
+ """
573
+ The status of the Import
574
+ """
575
+ format: ImportFormat
576
+ """
577
+ The format of the import
578
+ """
579
+ entity: Optional[Union[
580
+ WebsetCompanyEntity,
581
+ WebsetPersonEntity,
582
+ WebsetArticleEntity,
583
+ WebsetResearchPaperEntity,
584
+ WebsetCustomEntity,
585
+ ]] = None
586
+ """
587
+ The type of entity the import contains
588
+ """
589
+ title: str
590
+ """
591
+ The title of the import
592
+ """
593
+ count: float
594
+ """
595
+ The number of entities in the import
596
+ """
597
+ metadata: Dict[str, Any]
598
+ """
599
+ Set of key-value pairs associated with this object
600
+ """
601
+ failed_reason: Optional[ImportFailedReason] = Field(None, alias='failedReason')
602
+ """
603
+ The reason the import failed, if applicable
604
+ """
605
+ failed_at: Optional[datetime] = Field(None, alias='failedAt')
606
+ """
607
+ When the import failed, if applicable
608
+ """
609
+ failed_message: Optional[str] = Field(None, alias='failedMessage')
610
+ """
611
+ A human readable message describing the import failure
612
+ """
613
+ created_at: datetime = Field(..., alias='createdAt')
614
+ """
615
+ When the import was created
616
+ """
617
+ updated_at: datetime = Field(..., alias='updatedAt')
618
+ """
619
+ When the import was last updated
620
+ """
621
+
622
+
623
+ class ListImportsResponse(ExaBaseModel):
624
+ """
625
+ Response from listing imports.
626
+ """
627
+ data: List[Import]
628
+ """
629
+ The list of imports
630
+ """
631
+ has_more: bool = Field(..., alias='hasMore')
632
+ """
633
+ Whether there are more results to paginate through
634
+ """
635
+ next_cursor: Optional[str] = Field(None, alias='nextCursor')
636
+ """
637
+ The cursor to use for the next page of results
638
+ """
639
+
640
+
641
+ class UpdateImport(ExaBaseModel):
642
+ """
643
+ Parameters for updating an import.
644
+ """
645
+ metadata: Optional[Dict[str, Any]] = None
646
+ """
647
+ Set of key-value pairs you want to associate with this object.
648
+ """
649
+ title: Optional[str] = None
650
+ """
651
+ The title of the import
652
+ """
653
+
654
+
361
655
  class Option(ExaBaseModel):
362
656
  label: str
363
657
  """
@@ -451,6 +745,10 @@ class CreateWebsetParametersSearch(ExaBaseModel):
451
745
 
452
746
  It's not required to provide your own criteria, we automatically detect the criteria from all the information provided in the query.
453
747
  """
748
+ exclude: Optional[List[ExcludeItem]] = None
749
+ """
750
+ Sources (existing imports or websets) to exclude from search results. Any results found within these sources will be omitted to prevent finding them during search.
751
+ """
454
752
 
455
753
 
456
754
  class Source(Enum):
@@ -25,7 +25,7 @@ in-project = true
25
25
 
26
26
  [project]
27
27
  name = "exa-py"
28
- version = "1.14.6"
28
+ version = "1.14.7"
29
29
  description = "Python SDK for Exa API."
30
30
  readme = "README.md"
31
31
  requires-python = ">=3.9"
File without changes
File without changes
File without changes
File without changes
File without changes