lucidicai 2.1.3__tar.gz → 3.1.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.
Files changed (67) hide show
  1. {lucidicai-2.1.3 → lucidicai-3.1.0}/PKG-INFO +1 -1
  2. lucidicai-3.1.0/lucidicai/__init__.py +55 -0
  3. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/api/client.py +31 -2
  4. lucidicai-3.1.0/lucidicai/api/resources/__init__.py +16 -0
  5. lucidicai-3.1.0/lucidicai/api/resources/dataset.py +532 -0
  6. lucidicai-3.1.0/lucidicai/api/resources/evals.py +209 -0
  7. lucidicai-3.1.0/lucidicai/api/resources/event.py +460 -0
  8. lucidicai-3.1.0/lucidicai/api/resources/experiment.py +108 -0
  9. lucidicai-3.1.0/lucidicai/api/resources/feature_flag.py +78 -0
  10. lucidicai-3.1.0/lucidicai/api/resources/prompt.py +84 -0
  11. lucidicai-3.1.0/lucidicai/api/resources/session.py +633 -0
  12. lucidicai-3.1.0/lucidicai/client.py +441 -0
  13. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/core/config.py +73 -48
  14. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/core/errors.py +3 -3
  15. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/context.py +20 -2
  16. lucidicai-3.1.0/lucidicai/sdk/decorators.py +396 -0
  17. lucidicai-3.1.0/lucidicai/sdk/event.py +628 -0
  18. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/event_builder.py +2 -4
  19. lucidicai-3.1.0/lucidicai/sdk/features/dataset.py +781 -0
  20. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/features/feature_flag.py +344 -3
  21. lucidicai-3.1.0/lucidicai/sdk/init.py +137 -0
  22. lucidicai-3.1.0/lucidicai/sdk/session.py +502 -0
  23. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/shutdown_manager.py +103 -46
  24. lucidicai-3.1.0/lucidicai/session_obj.py +321 -0
  25. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/context_capture_processor.py +13 -6
  26. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/extract.py +60 -63
  27. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/litellm_bridge.py +3 -44
  28. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/lucidic_exporter.py +143 -131
  29. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/openai_agents_instrumentor.py +2 -2
  30. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/openai_patch.py +7 -6
  31. lucidicai-3.1.0/lucidicai/telemetry/telemetry_manager.py +183 -0
  32. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/utils/model_pricing.py +21 -30
  33. lucidicai-3.1.0/lucidicai/telemetry/utils/provider.py +77 -0
  34. lucidicai-3.1.0/lucidicai/utils/serialization.py +27 -0
  35. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/PKG-INFO +1 -1
  36. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/SOURCES.txt +11 -2
  37. {lucidicai-2.1.3 → lucidicai-3.1.0}/setup.py +1 -1
  38. lucidicai-3.1.0/tests/test_event_creation.py +200 -0
  39. lucidicai-2.1.3/lucidicai/__init__.py +0 -413
  40. lucidicai-2.1.3/lucidicai/api/resources/__init__.py +0 -1
  41. lucidicai-2.1.3/lucidicai/api/resources/dataset.py +0 -192
  42. lucidicai-2.1.3/lucidicai/api/resources/event.py +0 -88
  43. lucidicai-2.1.3/lucidicai/api/resources/session.py +0 -126
  44. lucidicai-2.1.3/lucidicai/sdk/decorators.py +0 -187
  45. lucidicai-2.1.3/lucidicai/sdk/event.py +0 -126
  46. lucidicai-2.1.3/lucidicai/sdk/features/dataset.py +0 -391
  47. lucidicai-2.1.3/lucidicai/sdk/init.py +0 -435
  48. lucidicai-2.1.3/lucidicai/utils/images.py +0 -337
  49. lucidicai-2.1.3/lucidicai/utils/queue.py +0 -425
  50. {lucidicai-2.1.3 → lucidicai-3.1.0}/README.md +0 -0
  51. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/api/__init__.py +0 -0
  52. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/core/__init__.py +0 -0
  53. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/core/types.py +0 -0
  54. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/__init__.py +0 -0
  55. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/error_boundary.py +0 -0
  56. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/features/__init__.py +0 -0
  57. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/__init__.py +0 -0
  58. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/context_bridge.py +0 -0
  59. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/openai_uninstrument.py +0 -0
  60. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/telemetry_init.py +0 -0
  61. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/utils/__init__.py +0 -0
  62. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/utils/__init__.py +0 -0
  63. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/utils/logger.py +0 -0
  64. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/dependency_links.txt +0 -0
  65. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/requires.txt +0 -0
  66. {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/top_level.txt +0 -0
  67. {lucidicai-2.1.3 → lucidicai-3.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lucidicai
3
- Version: 2.1.3
3
+ Version: 3.1.0
4
4
  Summary: Lucidic AI Python SDK
5
5
  Author: Andy Liang
6
6
  Author-email: andy@lucidic.ai
@@ -0,0 +1,55 @@
1
+ """Lucidic AI SDK - Instance-based client for AI observability.
2
+
3
+ This SDK provides observability for AI applications, tracking workflows,
4
+ costs, and performance across multiple LLM providers.
5
+
6
+ Example:
7
+ from lucidicai import LucidicAI
8
+
9
+ client = LucidicAI(api_key="...", agent_id="...", providers=["openai"])
10
+
11
+ with client.create_session(session_name="My Session") as session:
12
+ @client.event
13
+ def my_function():
14
+ # LLM calls are automatically tracked
15
+ pass
16
+ my_function()
17
+
18
+ client.close()
19
+ """
20
+
21
+ # Main client class
22
+ from .client import LucidicAI
23
+
24
+ # Session object
25
+ from .session_obj import Session
26
+
27
+ # Error types
28
+ from .core.errors import (
29
+ LucidicError,
30
+ LucidicNotInitializedError,
31
+ APIKeyVerificationError,
32
+ InvalidOperationError,
33
+ PromptError,
34
+ FeatureFlagError,
35
+ )
36
+
37
+ # Version
38
+ __version__ = "3.1.0"
39
+
40
+ # All exports
41
+ __all__ = [
42
+ # Main client
43
+ "LucidicAI",
44
+ # Session object
45
+ "Session",
46
+ # Error types
47
+ "LucidicError",
48
+ "LucidicNotInitializedError",
49
+ "APIKeyVerificationError",
50
+ "InvalidOperationError",
51
+ "PromptError",
52
+ "FeatureFlagError",
53
+ # Version
54
+ "__version__",
55
+ ]
@@ -43,6 +43,7 @@ class HttpClient:
43
43
  # Lazy-initialized clients
44
44
  self._sync_client: Optional[httpx.Client] = None
45
45
  self._async_client: Optional[httpx.AsyncClient] = None
46
+ self._async_client_loop: Optional[asyncio.AbstractEventLoop] = None
46
47
 
47
48
  def _build_headers(self) -> Dict[str, str]:
48
49
  """Build default headers for requests."""
@@ -75,8 +76,34 @@ class HttpClient:
75
76
 
76
77
  @property
77
78
  def async_client(self) -> httpx.AsyncClient:
78
- """Get or create the asynchronous HTTP client."""
79
- if self._async_client is None or self._async_client.is_closed:
79
+ """Get or create the asynchronous HTTP client.
80
+
81
+ The client is recreated if the event loop has changed, since
82
+ httpx.AsyncClient is tied to a specific event loop.
83
+ """
84
+ # Check if we need to recreate the client
85
+ current_loop = None
86
+ try:
87
+ current_loop = asyncio.get_running_loop()
88
+ except RuntimeError:
89
+ pass # No running loop
90
+
91
+ # Recreate client if: no client, client closed, or event loop changed
92
+ needs_new_client = (
93
+ self._async_client is None or
94
+ self._async_client.is_closed or
95
+ (current_loop is not None and self._async_client_loop is not current_loop)
96
+ )
97
+
98
+ if needs_new_client:
99
+ # Close old client if it exists and isn't already closed
100
+ if self._async_client is not None and not self._async_client.is_closed:
101
+ try:
102
+ # Can't await in a property, so we just let it be garbage collected
103
+ pass
104
+ except Exception:
105
+ pass
106
+
80
107
  transport = httpx.AsyncHTTPTransport(**self._transport_kwargs)
81
108
  self._async_client = httpx.AsyncClient(
82
109
  base_url=self.base_url,
@@ -85,6 +112,8 @@ class HttpClient:
85
112
  limits=self._limits,
86
113
  transport=transport,
87
114
  )
115
+ self._async_client_loop = current_loop
116
+
88
117
  return self._async_client
89
118
 
90
119
  def _add_timestamp(self, data: Optional[Dict[str, Any]]) -> Dict[str, Any]:
@@ -0,0 +1,16 @@
1
+ """API resource modules."""
2
+ from .session import SessionResource
3
+ from .event import EventResource
4
+ from .dataset import DatasetResource
5
+ from .experiment import ExperimentResource
6
+ from .prompt import PromptResource
7
+ from .feature_flag import FeatureFlagResource
8
+
9
+ __all__ = [
10
+ "SessionResource",
11
+ "EventResource",
12
+ "DatasetResource",
13
+ "ExperimentResource",
14
+ "PromptResource",
15
+ "FeatureFlagResource",
16
+ ]
@@ -0,0 +1,532 @@
1
+ """Dataset resource API operations."""
2
+ import logging
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ from ..client import HttpClient
6
+
7
+ logger = logging.getLogger("Lucidic")
8
+
9
+
10
+ class DatasetResource:
11
+ """Handle dataset-related API operations."""
12
+
13
+ def __init__(
14
+ self,
15
+ http: HttpClient,
16
+ agent_id: Optional[str] = None,
17
+ production: bool = False,
18
+ ):
19
+ """Initialize dataset resource.
20
+
21
+ Args:
22
+ http: HTTP client instance
23
+ agent_id: Default agent ID for datasets
24
+ production: Whether to suppress errors in production mode
25
+ """
26
+ self.http = http
27
+ self._agent_id = agent_id
28
+ self._production = production
29
+
30
+ # ==================== Dataset Methods ====================
31
+
32
+ def list(self, agent_id: Optional[str] = None) -> Dict[str, Any]:
33
+ """List all datasets for agent.
34
+
35
+ Args:
36
+ agent_id: Optional agent ID to filter by (uses default if not provided)
37
+
38
+ Returns:
39
+ Dictionary with num_datasets and datasets list
40
+ """
41
+ try:
42
+ params = {}
43
+ if agent_id or self._agent_id:
44
+ params["agent_id"] = agent_id or self._agent_id
45
+ return self.http.get("sdk/datasets", params)
46
+ except Exception as e:
47
+ if self._production:
48
+ logger.error(f"[DatasetResource] Failed to list datasets: {e}")
49
+ return {"num_datasets": 0, "datasets": []}
50
+ raise
51
+
52
+ def create(
53
+ self,
54
+ name: str,
55
+ description: Optional[str] = None,
56
+ tags: Optional[List[str]] = None,
57
+ suggested_flag_config: Optional[Dict[str, Any]] = None,
58
+ agent_id: Optional[str] = None,
59
+ ) -> Dict[str, Any]:
60
+ """Create new dataset.
61
+
62
+ Args:
63
+ name: Dataset name (must be unique per agent)
64
+ description: Optional description
65
+ tags: Optional list of tags
66
+ suggested_flag_config: Optional flag configuration
67
+ agent_id: Optional agent ID (uses default if not provided)
68
+
69
+ Returns:
70
+ Dictionary with dataset_id
71
+ """
72
+ try:
73
+ data: Dict[str, Any] = {"name": name}
74
+ if description is not None:
75
+ data["description"] = description
76
+ if tags is not None:
77
+ data["tags"] = tags
78
+ if suggested_flag_config is not None:
79
+ data["suggested_flag_config"] = suggested_flag_config
80
+ data["agent_id"] = agent_id or self._agent_id
81
+ return self.http.post("sdk/datasets/create", data)
82
+ except Exception as e:
83
+ if self._production:
84
+ logger.error(f"[DatasetResource] Failed to create dataset: {e}")
85
+ return {}
86
+ raise
87
+
88
+ def get(self, dataset_id: str) -> Dict[str, Any]:
89
+ """Get dataset with all items.
90
+
91
+ Args:
92
+ dataset_id: Dataset UUID
93
+
94
+ Returns:
95
+ Full dataset data including all items
96
+ """
97
+ try:
98
+ return self.http.get("getdataset", {"dataset_id": dataset_id})
99
+ except Exception as e:
100
+ if self._production:
101
+ logger.error(f"[DatasetResource] Failed to get dataset: {e}")
102
+ return {}
103
+ raise
104
+
105
+ def update(self, dataset_id: str, **kwargs) -> Dict[str, Any]:
106
+ """Update dataset metadata.
107
+
108
+ Args:
109
+ dataset_id: Dataset UUID
110
+ **kwargs: Fields to update (name, description, tags, suggested_flag_config)
111
+
112
+ Returns:
113
+ Updated dataset data
114
+ """
115
+ try:
116
+ data = {"dataset_id": dataset_id}
117
+ data.update(kwargs)
118
+ return self.http.put("sdk/datasets/update", data)
119
+ except Exception as e:
120
+ if self._production:
121
+ logger.error(f"[DatasetResource] Failed to update dataset: {e}")
122
+ return {}
123
+ raise
124
+
125
+ def delete(self, dataset_id: str) -> Dict[str, Any]:
126
+ """Delete dataset and all items.
127
+
128
+ Args:
129
+ dataset_id: Dataset UUID
130
+
131
+ Returns:
132
+ Success message
133
+ """
134
+ try:
135
+ return self.http.delete("sdk/datasets/delete", {"dataset_id": dataset_id})
136
+ except Exception as e:
137
+ if self._production:
138
+ logger.error(f"[DatasetResource] Failed to delete dataset: {e}")
139
+ return {}
140
+ raise
141
+
142
+ # ==================== Dataset Item Methods ====================
143
+
144
+ def create_item(
145
+ self,
146
+ dataset_id: str,
147
+ name: str,
148
+ input_data: Dict[str, Any],
149
+ expected_output: Optional[Any] = None,
150
+ description: Optional[str] = None,
151
+ tags: Optional[List[str]] = None,
152
+ metadata: Optional[Dict[str, Any]] = None,
153
+ flag_overrides: Optional[Dict[str, Any]] = None,
154
+ ) -> Dict[str, Any]:
155
+ """Create dataset item.
156
+
157
+ Args:
158
+ dataset_id: Dataset UUID
159
+ name: Item name
160
+ input_data: Input data dictionary
161
+ expected_output: Optional expected output
162
+ description: Optional description
163
+ tags: Optional list of tags
164
+ metadata: Optional metadata dictionary
165
+ flag_overrides: Optional flag overrides
166
+
167
+ Returns:
168
+ Dictionary with datasetitem_id
169
+ """
170
+ try:
171
+ data: Dict[str, Any] = {
172
+ "dataset_id": dataset_id,
173
+ "name": name,
174
+ "input": input_data
175
+ }
176
+
177
+ if expected_output is not None:
178
+ data["expected_output"] = expected_output
179
+ if description is not None:
180
+ data["description"] = description
181
+ if tags is not None:
182
+ data["tags"] = tags
183
+ if metadata is not None:
184
+ data["metadata"] = metadata
185
+ if flag_overrides is not None:
186
+ data["flag_overrides"] = flag_overrides
187
+
188
+ return self.http.post("sdk/datasets/items/create", data)
189
+ except Exception as e:
190
+ if self._production:
191
+ logger.error(f"[DatasetResource] Failed to create item: {e}")
192
+ return {}
193
+ raise
194
+
195
+ def get_item(self, dataset_id: str, item_id: str) -> Dict[str, Any]:
196
+ """Get specific dataset item.
197
+
198
+ Args:
199
+ dataset_id: Dataset UUID
200
+ item_id: Item UUID
201
+
202
+ Returns:
203
+ Dataset item data
204
+ """
205
+ try:
206
+ return self.http.get("sdk/datasets/items/get", {
207
+ "dataset_id": dataset_id,
208
+ "datasetitem_id": item_id
209
+ })
210
+ except Exception as e:
211
+ if self._production:
212
+ logger.error(f"[DatasetResource] Failed to get item: {e}")
213
+ return {}
214
+ raise
215
+
216
+ def update_item(self, dataset_id: str, item_id: str, **kwargs) -> Dict[str, Any]:
217
+ """Update dataset item.
218
+
219
+ Args:
220
+ dataset_id: Dataset UUID
221
+ item_id: Item UUID
222
+ **kwargs: Fields to update
223
+
224
+ Returns:
225
+ Updated item data
226
+ """
227
+ try:
228
+ data = {
229
+ "dataset_id": dataset_id,
230
+ "datasetitem_id": item_id
231
+ }
232
+ data.update(kwargs)
233
+ return self.http.put("sdk/datasets/items/update", data)
234
+ except Exception as e:
235
+ if self._production:
236
+ logger.error(f"[DatasetResource] Failed to update item: {e}")
237
+ return {}
238
+ raise
239
+
240
+ def delete_item(self, dataset_id: str, item_id: str) -> Dict[str, Any]:
241
+ """Delete dataset item.
242
+
243
+ Args:
244
+ dataset_id: Dataset UUID
245
+ item_id: Item UUID
246
+
247
+ Returns:
248
+ Success message
249
+ """
250
+ try:
251
+ return self.http.delete("sdk/datasets/items/delete", {
252
+ "dataset_id": dataset_id,
253
+ "datasetitem_id": item_id
254
+ })
255
+ except Exception as e:
256
+ if self._production:
257
+ logger.error(f"[DatasetResource] Failed to delete item: {e}")
258
+ return {}
259
+ raise
260
+
261
+ def list_item_sessions(self, dataset_id: str, item_id: str) -> Dict[str, Any]:
262
+ """List all sessions for a dataset item.
263
+
264
+ Args:
265
+ dataset_id: Dataset UUID
266
+ item_id: Item UUID
267
+
268
+ Returns:
269
+ Dictionary with num_sessions and sessions list
270
+ """
271
+ try:
272
+ return self.http.get("sdk/datasets/items/sessions", {
273
+ "dataset_id": dataset_id,
274
+ "datasetitem_id": item_id
275
+ })
276
+ except Exception as e:
277
+ if self._production:
278
+ logger.error(f"[DatasetResource] Failed to list item sessions: {e}")
279
+ return {"num_sessions": 0, "sessions": []}
280
+ raise
281
+
282
+ # ==================== Asynchronous Dataset Methods ====================
283
+
284
+ async def alist(self, agent_id: Optional[str] = None) -> Dict[str, Any]:
285
+ """List all datasets for agent (asynchronous).
286
+
287
+ Args:
288
+ agent_id: Optional agent ID to filter by (uses default if not provided)
289
+
290
+ Returns:
291
+ Dictionary with num_datasets and datasets list
292
+ """
293
+ try:
294
+ params = {}
295
+ if agent_id or self._agent_id:
296
+ params["agent_id"] = agent_id or self._agent_id
297
+ return await self.http.aget("sdk/datasets", params)
298
+ except Exception as e:
299
+ if self._production:
300
+ logger.error(f"[DatasetResource] Failed to list datasets: {e}")
301
+ return {"num_datasets": 0, "datasets": []}
302
+ raise
303
+
304
+ async def acreate(
305
+ self,
306
+ name: str,
307
+ description: Optional[str] = None,
308
+ tags: Optional[List[str]] = None,
309
+ suggested_flag_config: Optional[Dict[str, Any]] = None,
310
+ agent_id: Optional[str] = None,
311
+ ) -> Dict[str, Any]:
312
+ """Create new dataset (asynchronous).
313
+
314
+ Args:
315
+ name: Dataset name (must be unique per agent)
316
+ description: Optional description
317
+ tags: Optional list of tags
318
+ suggested_flag_config: Optional flag configuration
319
+ agent_id: Optional agent ID (uses default if not provided)
320
+
321
+ Returns:
322
+ Dictionary with dataset_id
323
+ """
324
+ try:
325
+ data: Dict[str, Any] = {"name": name}
326
+ if description is not None:
327
+ data["description"] = description
328
+ if tags is not None:
329
+ data["tags"] = tags
330
+ if suggested_flag_config is not None:
331
+ data["suggested_flag_config"] = suggested_flag_config
332
+ data["agent_id"] = agent_id or self._agent_id
333
+ return await self.http.apost("sdk/datasets/create", data)
334
+ except Exception as e:
335
+ if self._production:
336
+ logger.error(f"[DatasetResource] Failed to create dataset: {e}")
337
+ return {}
338
+ raise
339
+
340
+ async def aget(self, dataset_id: str) -> Dict[str, Any]:
341
+ """Get dataset with all items (asynchronous).
342
+
343
+ Args:
344
+ dataset_id: Dataset UUID
345
+
346
+ Returns:
347
+ Full dataset data including all items
348
+ """
349
+ try:
350
+ return await self.http.aget("getdataset", {"dataset_id": dataset_id})
351
+ except Exception as e:
352
+ if self._production:
353
+ logger.error(f"[DatasetResource] Failed to get dataset: {e}")
354
+ return {}
355
+ raise
356
+
357
+ async def aupdate(self, dataset_id: str, **kwargs) -> Dict[str, Any]:
358
+ """Update dataset metadata (asynchronous).
359
+
360
+ Args:
361
+ dataset_id: Dataset UUID
362
+ **kwargs: Fields to update (name, description, tags, suggested_flag_config)
363
+
364
+ Returns:
365
+ Updated dataset data
366
+ """
367
+ try:
368
+ data = {"dataset_id": dataset_id}
369
+ data.update(kwargs)
370
+ return await self.http.aput("sdk/datasets/update", data)
371
+ except Exception as e:
372
+ if self._production:
373
+ logger.error(f"[DatasetResource] Failed to update dataset: {e}")
374
+ return {}
375
+ raise
376
+
377
+ async def adelete(self, dataset_id: str) -> Dict[str, Any]:
378
+ """Delete dataset and all items (asynchronous).
379
+
380
+ Args:
381
+ dataset_id: Dataset UUID
382
+
383
+ Returns:
384
+ Success message
385
+ """
386
+ try:
387
+ return await self.http.adelete("sdk/datasets/delete", {"dataset_id": dataset_id})
388
+ except Exception as e:
389
+ if self._production:
390
+ logger.error(f"[DatasetResource] Failed to delete dataset: {e}")
391
+ return {}
392
+ raise
393
+
394
+ # ==================== Asynchronous Item Methods ====================
395
+
396
+ async def acreate_item(
397
+ self,
398
+ dataset_id: str,
399
+ name: str,
400
+ input_data: Dict[str, Any],
401
+ expected_output: Optional[Any] = None,
402
+ description: Optional[str] = None,
403
+ tags: Optional[List[str]] = None,
404
+ metadata: Optional[Dict[str, Any]] = None,
405
+ flag_overrides: Optional[Dict[str, Any]] = None,
406
+ ) -> Dict[str, Any]:
407
+ """Create dataset item (asynchronous).
408
+
409
+ Args:
410
+ dataset_id: Dataset UUID
411
+ name: Item name
412
+ input_data: Input data dictionary
413
+ expected_output: Optional expected output
414
+ description: Optional description
415
+ tags: Optional list of tags
416
+ metadata: Optional metadata dictionary
417
+ flag_overrides: Optional flag overrides
418
+
419
+ Returns:
420
+ Dictionary with datasetitem_id
421
+ """
422
+ try:
423
+ data: Dict[str, Any] = {
424
+ "dataset_id": dataset_id,
425
+ "name": name,
426
+ "input": input_data
427
+ }
428
+
429
+ if expected_output is not None:
430
+ data["expected_output"] = expected_output
431
+ if description is not None:
432
+ data["description"] = description
433
+ if tags is not None:
434
+ data["tags"] = tags
435
+ if metadata is not None:
436
+ data["metadata"] = metadata
437
+ if flag_overrides is not None:
438
+ data["flag_overrides"] = flag_overrides
439
+
440
+ return await self.http.apost("sdk/datasets/items/create", data)
441
+ except Exception as e:
442
+ if self._production:
443
+ logger.error(f"[DatasetResource] Failed to create item: {e}")
444
+ return {}
445
+ raise
446
+
447
+ async def aget_item(self, dataset_id: str, item_id: str) -> Dict[str, Any]:
448
+ """Get specific dataset item (asynchronous).
449
+
450
+ Args:
451
+ dataset_id: Dataset UUID
452
+ item_id: Item UUID
453
+
454
+ Returns:
455
+ Dataset item data
456
+ """
457
+ try:
458
+ return await self.http.aget("sdk/datasets/items/get", {
459
+ "dataset_id": dataset_id,
460
+ "datasetitem_id": item_id
461
+ })
462
+ except Exception as e:
463
+ if self._production:
464
+ logger.error(f"[DatasetResource] Failed to get item: {e}")
465
+ return {}
466
+ raise
467
+
468
+ async def aupdate_item(self, dataset_id: str, item_id: str, **kwargs) -> Dict[str, Any]:
469
+ """Update dataset item (asynchronous).
470
+
471
+ Args:
472
+ dataset_id: Dataset UUID
473
+ item_id: Item UUID
474
+ **kwargs: Fields to update
475
+
476
+ Returns:
477
+ Updated item data
478
+ """
479
+ try:
480
+ data = {
481
+ "dataset_id": dataset_id,
482
+ "datasetitem_id": item_id
483
+ }
484
+ data.update(kwargs)
485
+ return await self.http.aput("sdk/datasets/items/update", data)
486
+ except Exception as e:
487
+ if self._production:
488
+ logger.error(f"[DatasetResource] Failed to update item: {e}")
489
+ return {}
490
+ raise
491
+
492
+ async def adelete_item(self, dataset_id: str, item_id: str) -> Dict[str, Any]:
493
+ """Delete dataset item (asynchronous).
494
+
495
+ Args:
496
+ dataset_id: Dataset UUID
497
+ item_id: Item UUID
498
+
499
+ Returns:
500
+ Success message
501
+ """
502
+ try:
503
+ return await self.http.adelete("sdk/datasets/items/delete", {
504
+ "dataset_id": dataset_id,
505
+ "datasetitem_id": item_id
506
+ })
507
+ except Exception as e:
508
+ if self._production:
509
+ logger.error(f"[DatasetResource] Failed to delete item: {e}")
510
+ return {}
511
+ raise
512
+
513
+ async def alist_item_sessions(self, dataset_id: str, item_id: str) -> Dict[str, Any]:
514
+ """List all sessions for a dataset item (asynchronous).
515
+
516
+ Args:
517
+ dataset_id: Dataset UUID
518
+ item_id: Item UUID
519
+
520
+ Returns:
521
+ Dictionary with num_sessions and sessions list
522
+ """
523
+ try:
524
+ return await self.http.aget("sdk/datasets/items/sessions", {
525
+ "dataset_id": dataset_id,
526
+ "datasetitem_id": item_id
527
+ })
528
+ except Exception as e:
529
+ if self._production:
530
+ logger.error(f"[DatasetResource] Failed to list item sessions: {e}")
531
+ return {"num_sessions": 0, "sessions": []}
532
+ raise