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.
- {lucidicai-2.1.3 → lucidicai-3.1.0}/PKG-INFO +1 -1
- lucidicai-3.1.0/lucidicai/__init__.py +55 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/api/client.py +31 -2
- lucidicai-3.1.0/lucidicai/api/resources/__init__.py +16 -0
- lucidicai-3.1.0/lucidicai/api/resources/dataset.py +532 -0
- lucidicai-3.1.0/lucidicai/api/resources/evals.py +209 -0
- lucidicai-3.1.0/lucidicai/api/resources/event.py +460 -0
- lucidicai-3.1.0/lucidicai/api/resources/experiment.py +108 -0
- lucidicai-3.1.0/lucidicai/api/resources/feature_flag.py +78 -0
- lucidicai-3.1.0/lucidicai/api/resources/prompt.py +84 -0
- lucidicai-3.1.0/lucidicai/api/resources/session.py +633 -0
- lucidicai-3.1.0/lucidicai/client.py +441 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/core/config.py +73 -48
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/core/errors.py +3 -3
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/context.py +20 -2
- lucidicai-3.1.0/lucidicai/sdk/decorators.py +396 -0
- lucidicai-3.1.0/lucidicai/sdk/event.py +628 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/event_builder.py +2 -4
- lucidicai-3.1.0/lucidicai/sdk/features/dataset.py +781 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/features/feature_flag.py +344 -3
- lucidicai-3.1.0/lucidicai/sdk/init.py +137 -0
- lucidicai-3.1.0/lucidicai/sdk/session.py +502 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/shutdown_manager.py +103 -46
- lucidicai-3.1.0/lucidicai/session_obj.py +321 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/context_capture_processor.py +13 -6
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/extract.py +60 -63
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/litellm_bridge.py +3 -44
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/lucidic_exporter.py +143 -131
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/openai_agents_instrumentor.py +2 -2
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/openai_patch.py +7 -6
- lucidicai-3.1.0/lucidicai/telemetry/telemetry_manager.py +183 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/utils/model_pricing.py +21 -30
- lucidicai-3.1.0/lucidicai/telemetry/utils/provider.py +77 -0
- lucidicai-3.1.0/lucidicai/utils/serialization.py +27 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/PKG-INFO +1 -1
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/SOURCES.txt +11 -2
- {lucidicai-2.1.3 → lucidicai-3.1.0}/setup.py +1 -1
- lucidicai-3.1.0/tests/test_event_creation.py +200 -0
- lucidicai-2.1.3/lucidicai/__init__.py +0 -413
- lucidicai-2.1.3/lucidicai/api/resources/__init__.py +0 -1
- lucidicai-2.1.3/lucidicai/api/resources/dataset.py +0 -192
- lucidicai-2.1.3/lucidicai/api/resources/event.py +0 -88
- lucidicai-2.1.3/lucidicai/api/resources/session.py +0 -126
- lucidicai-2.1.3/lucidicai/sdk/decorators.py +0 -187
- lucidicai-2.1.3/lucidicai/sdk/event.py +0 -126
- lucidicai-2.1.3/lucidicai/sdk/features/dataset.py +0 -391
- lucidicai-2.1.3/lucidicai/sdk/init.py +0 -435
- lucidicai-2.1.3/lucidicai/utils/images.py +0 -337
- lucidicai-2.1.3/lucidicai/utils/queue.py +0 -425
- {lucidicai-2.1.3 → lucidicai-3.1.0}/README.md +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/api/__init__.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/core/__init__.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/core/types.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/__init__.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/error_boundary.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/sdk/features/__init__.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/__init__.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/context_bridge.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/openai_uninstrument.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/telemetry_init.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/telemetry/utils/__init__.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/utils/__init__.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai/utils/logger.py +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/dependency_links.txt +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/requires.txt +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/lucidicai.egg-info/top_level.txt +0 -0
- {lucidicai-2.1.3 → lucidicai-3.1.0}/setup.cfg +0 -0
|
@@ -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
|
-
|
|
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
|