trodo-python 2.25.0__tar.gz → 2.26.1__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.
- {trodo_python-2.25.0 → trodo_python-2.26.1}/PKG-INFO +1 -1
- {trodo_python-2.25.0 → trodo_python-2.26.1}/pyproject.toml +1 -1
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_datasets.py +44 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/__init__.py +26 -1
- trodo_python-2.26.1/trodo/_version.py +1 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/api/http_client.py +39 -0
- trodo_python-2.26.1/trodo/managers/dataset_manager.py +134 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo_python.egg-info/PKG-INFO +1 -1
- trodo_python-2.25.0/trodo/_version.py +0 -1
- trodo_python-2.25.0/trodo/managers/dataset_manager.py +0 -74
- {trodo_python-2.25.0 → trodo_python-2.26.1}/README.md +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/setup.cfg +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_anon_distinct_id.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_auto_instrument_fixes.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_cross_process_session.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_end_run.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_error_enrichment.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_experiments.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_framework_deference.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_get_prompt_version_types.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_instrumentor_resolution.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_llm_usage_cost.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_multi_agent_shape.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_processor_methods.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_prompt_fingerprint.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_prompt_trace.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_prompts.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_register_otel.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_sender.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_start_run.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_user_traits.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/tests/test_wrap_agent_unchanged.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/api/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/api/_retry.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/api/async_client.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/api/endpoints.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/auto/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/auto/auto_event_manager.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/client.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/delivery/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/delivery/sender.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/managers/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/managers/experiment_manager.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/managers/group_manager.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/managers/people_manager.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/managers/prompt_manager.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/managers/user_manager.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/auto_instrument.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/context.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/helpers.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/processor.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/prompt_trace.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/register.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/transport.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/otel/wrap_agent.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/prompts/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/prompts/compile.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/prompts/template.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/prompts/types.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/queue/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/queue/batch_flusher.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/queue/event_queue.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/session/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/session/server_session.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/session/session_manager.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/types.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/user_context.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/util/__init__.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo/util/lru.py +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo_python.egg-info/SOURCES.txt +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo_python.egg-info/dependency_links.txt +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo_python.egg-info/requires.txt +0 -0
- {trodo_python-2.25.0 → trodo_python-2.26.1}/trodo_python.egg-info/top_level.txt +0 -0
|
@@ -100,3 +100,47 @@ def test_append_requires_items():
|
|
|
100
100
|
def test_append_requires_input_on_each_item():
|
|
101
101
|
with pytest.raises(ValueError, match="requires an 'input'"):
|
|
102
102
|
DatasetManager(FakeHttp()).append("ds", [{"expected_output": "x"}])
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# export — DatasetManager.export (the labelled JSONL, read with an export token)
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
class FakeExportHttp:
|
|
110
|
+
def __init__(self, response):
|
|
111
|
+
self._response = response
|
|
112
|
+
self.last = None
|
|
113
|
+
|
|
114
|
+
def export_dataset(self, ref, params, token):
|
|
115
|
+
self.last = (ref, params, token)
|
|
116
|
+
return self._response
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_export_sends_token_and_filters_and_parses_jsonl():
|
|
120
|
+
http = FakeExportHttp({"text": '{"id":"a","expected":"x"}\n{"id":"b","expected":null}\n', "status": 200, "rows": 2, "version": 3})
|
|
121
|
+
out = DatasetManager(http).export("support-eval", "trodo_dx_abc", labels="confirmed", include_unlabelled=True, version=3, since="2026-01-01T00:00:00Z")
|
|
122
|
+
assert http.last == ("support-eval", {"labels": "confirmed", "include_unlabelled": 1, "version": 3, "since": "2026-01-01T00:00:00Z"}, "trodo_dx_abc")
|
|
123
|
+
assert out == {"rows": [{"id": "a", "expected": "x"}, {"id": "b", "expected": None}], "version": 3}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_export_requires_a_token_and_raises_on_http_errors():
|
|
127
|
+
with pytest.raises(ValueError):
|
|
128
|
+
DatasetManager(FakeExportHttp({})).export("ds", "")
|
|
129
|
+
with pytest.raises(RuntimeError, match="HTTP 404"):
|
|
130
|
+
DatasetManager(FakeExportHttp({"__error": True, "status": 404, "error": "not_found"})).export("ds", "trodo_dx_abc")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_append_accepts_unit_references_without_input():
|
|
134
|
+
http = FakeHttp()
|
|
135
|
+
DatasetManager(http).append("ds", [
|
|
136
|
+
{"span_id": "s1", "tags": ["hard"]},
|
|
137
|
+
{"run_id": "r1"},
|
|
138
|
+
{"conversation_id": "c1", "metadata": {"k": 1}},
|
|
139
|
+
{"eval_run_id": "v1"},
|
|
140
|
+
])
|
|
141
|
+
assert http.last_body["items"] == [
|
|
142
|
+
{"span_id": "s1", "tags": ["hard"]},
|
|
143
|
+
{"run_id": "r1"},
|
|
144
|
+
{"conversation_id": "c1", "metadata": {"k": 1}},
|
|
145
|
+
{"eval_run_id": "v1"},
|
|
146
|
+
]
|
|
@@ -41,7 +41,7 @@ Downstream microservice (join the caller's run instead of making a new one):
|
|
|
41
41
|
|
|
42
42
|
from __future__ import annotations
|
|
43
43
|
|
|
44
|
-
__version__ = "2.
|
|
44
|
+
__version__ = "2.26.1"
|
|
45
45
|
|
|
46
46
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
47
47
|
|
|
@@ -122,6 +122,7 @@ __all__ = [
|
|
|
122
122
|
"PromptSummary",
|
|
123
123
|
# Datasets & experiments
|
|
124
124
|
"append_dataset",
|
|
125
|
+
"export_dataset",
|
|
125
126
|
"ingest_experiment",
|
|
126
127
|
"create_experiment",
|
|
127
128
|
"run_experiment",
|
|
@@ -435,6 +436,30 @@ def append_dataset(
|
|
|
435
436
|
return _get_client().datasets.append(ref, items, create=create)
|
|
436
437
|
|
|
437
438
|
|
|
439
|
+
def export_dataset(
|
|
440
|
+
ref: str,
|
|
441
|
+
token: str,
|
|
442
|
+
labels: str = "all",
|
|
443
|
+
include_unlabelled: bool = False,
|
|
444
|
+
version: Optional[int] = None,
|
|
445
|
+
since: Optional[str] = None,
|
|
446
|
+
) -> Dict[str, Any]:
|
|
447
|
+
"""Read a dataset's labelled export with an export token (Settings → Datasets).
|
|
448
|
+
|
|
449
|
+
Rows carry ``input``, ``output``, ``expected``, ``evaluations``,
|
|
450
|
+
``label_source`` / ``label_status`` / ``label_weight`` and ``source``::
|
|
451
|
+
|
|
452
|
+
out = trodo.export_dataset("support-failures", token=os.environ["TRODO_EXPORT_TOKEN"], labels="confirmed")
|
|
453
|
+
for row in out["rows"]:
|
|
454
|
+
...
|
|
455
|
+
|
|
456
|
+
Returns ``{"rows": [...], "version": int | None}``.
|
|
457
|
+
"""
|
|
458
|
+
return _get_client().datasets.export(
|
|
459
|
+
ref, token, labels=labels, include_unlabelled=include_unlabelled, version=version, since=since,
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
|
|
438
463
|
def ingest_experiment(
|
|
439
464
|
dataset: str,
|
|
440
465
|
outputs: List[Dict[str, Any]],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SDK_VERSION = "2.26.0"
|
|
@@ -180,6 +180,45 @@ class HttpClient:
|
|
|
180
180
|
f"/api/sdk/datasets/{quote(str(ref), safe='')}/items", body
|
|
181
181
|
)
|
|
182
182
|
|
|
183
|
+
def export_dataset(self, ref: str, params: Dict[str, Any], token: str) -> Dict[str, Any]:
|
|
184
|
+
"""Read a dataset's labelled export (GET /datasets/:ref/export) as JSONL
|
|
185
|
+
text, authenticated with an EXPORT TOKEN (``trodo_dx_…``), never the
|
|
186
|
+
site id. Returns ``{"text", "status", "rows", "version"}`` or
|
|
187
|
+
``{"__error": True, "status", ...}``. No retries on 4xx."""
|
|
188
|
+
from urllib.parse import quote
|
|
189
|
+
url = f"{self.api_base}/api/sdk/datasets/{quote(str(ref), safe='')}/export"
|
|
190
|
+
clean = {k: v for k, v in (params or {}).items() if v is not None and v != ""}
|
|
191
|
+
self._log(f"GET {url}")
|
|
192
|
+
try:
|
|
193
|
+
resp = requests.get(
|
|
194
|
+
url,
|
|
195
|
+
params=clean,
|
|
196
|
+
headers={"Authorization": f"Bearer {token}", "User-Agent": f"trodo-python/{SDK_VERSION}"},
|
|
197
|
+
timeout=max(self.timeout, 60),
|
|
198
|
+
)
|
|
199
|
+
except Exception as exc: # network
|
|
200
|
+
self._log(f"Error: {exc}")
|
|
201
|
+
if self.on_error:
|
|
202
|
+
self.on_error(exc)
|
|
203
|
+
return {"__error": True, "message": str(exc)}
|
|
204
|
+
if resp.status_code >= 400:
|
|
205
|
+
base: Dict[str, Any] = {"__error": True, "status": resp.status_code}
|
|
206
|
+
try:
|
|
207
|
+
data = resp.json()
|
|
208
|
+
if isinstance(data, dict):
|
|
209
|
+
base.update(data)
|
|
210
|
+
except Exception:
|
|
211
|
+
base["message"] = resp.text[:300]
|
|
212
|
+
return base
|
|
213
|
+
rows = resp.headers.get("x-trodo-export-rows")
|
|
214
|
+
version = resp.headers.get("x-trodo-dataset-version")
|
|
215
|
+
return {
|
|
216
|
+
"text": resp.text,
|
|
217
|
+
"status": resp.status_code,
|
|
218
|
+
"rows": int(rows) if rows and rows.isdigit() else None,
|
|
219
|
+
"version": int(version) if version and version.isdigit() else None,
|
|
220
|
+
}
|
|
221
|
+
|
|
183
222
|
def ingest_experiment(self, body: Dict[str, Any]) -> ApiResult:
|
|
184
223
|
return self._request("/api/sdk/experiments/ingest", body)
|
|
185
224
|
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Dataset ingest — append evaluation items to a Trodo dataset.
|
|
2
|
+
|
|
3
|
+
Datasets are the fixed inputs an experiment runs against: each item is a set of
|
|
4
|
+
variable values (``input``) plus an optional gold answer (``expected_output``)
|
|
5
|
+
and free-form ``metadata``. This manager appends items to a dataset addressed by
|
|
6
|
+
UUID or by name; when a name is used and no such dataset exists it is
|
|
7
|
+
auto-created (``create=True``, the default), so a first append bootstraps the
|
|
8
|
+
dataset without a separate create call.
|
|
9
|
+
|
|
10
|
+
Mirrors ``sdks/trodo-node-sdk/src/managers/DatasetManager.ts`` in shape.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
from typing import Any, Dict, List, Optional, Union
|
|
17
|
+
|
|
18
|
+
__all__ = ["DatasetManager"]
|
|
19
|
+
|
|
20
|
+
# The optional per-item fields, in wire order. ``input`` is required on a row.
|
|
21
|
+
_ITEM_OPTIONAL = ("output", "expected_output", "metadata", "source_trace_ref", "tags")
|
|
22
|
+
# A reference to production instead of content: the item becomes that unit's
|
|
23
|
+
# row (Input, Output and Evaluations filled by Trodo). ``eval_run_id`` names a
|
|
24
|
+
# verdict: Output is what the eval said, Expected its correction.
|
|
25
|
+
_UNIT_REF_KEYS = ("span_id", "run_id", "conversation_id", "eval_run_id")
|
|
26
|
+
_UNIT_REF_OPTIONAL = ("tags", "metadata")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _clean_item(item: Dict[str, Any]) -> Dict[str, Any]:
|
|
30
|
+
if not isinstance(item, dict):
|
|
31
|
+
raise ValueError("trodo: each dataset item must be a dict")
|
|
32
|
+
ref = [k for k in _UNIT_REF_KEYS if item.get(k)]
|
|
33
|
+
if ref and ("input" not in item or item["input"] is None):
|
|
34
|
+
out: Dict[str, Any] = {ref[0]: str(item[ref[0]])}
|
|
35
|
+
for key in _UNIT_REF_OPTIONAL:
|
|
36
|
+
if item.get(key) is not None:
|
|
37
|
+
out[key] = item[key]
|
|
38
|
+
return out
|
|
39
|
+
if "input" not in item or item["input"] is None:
|
|
40
|
+
raise ValueError("trodo: each dataset item requires an 'input' dict, or a span_id / run_id / conversation_id / eval_run_id")
|
|
41
|
+
out = {"input": item["input"]}
|
|
42
|
+
for key in _ITEM_OPTIONAL:
|
|
43
|
+
if item.get(key) is not None:
|
|
44
|
+
out[key] = item[key]
|
|
45
|
+
return out
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class DatasetManager:
|
|
49
|
+
"""Append items to the team's evaluation datasets."""
|
|
50
|
+
|
|
51
|
+
def __init__(self, http_client: Any) -> None:
|
|
52
|
+
self._http = http_client
|
|
53
|
+
|
|
54
|
+
def append(
|
|
55
|
+
self,
|
|
56
|
+
ref: str,
|
|
57
|
+
items: Union[Dict[str, Any], List[Dict[str, Any]]],
|
|
58
|
+
create: bool = True,
|
|
59
|
+
) -> Dict[str, Any]:
|
|
60
|
+
"""Append one or more items to the dataset *ref* (a UUID or a name).
|
|
61
|
+
|
|
62
|
+
*items* is a list of dicts shaped ``{"input": {<var>: value, ...},
|
|
63
|
+
"expected_output"?: str, "metadata"?: dict, "source_trace_ref"?: dict}``;
|
|
64
|
+
a single dict is accepted and wrapped into a one-item list. ``None``
|
|
65
|
+
optional fields are dropped from the wire payload.
|
|
66
|
+
|
|
67
|
+
When *ref* is a name that doesn't exist yet, ``create=True`` (default)
|
|
68
|
+
auto-creates the dataset; pass ``create=False`` to require it to exist.
|
|
69
|
+
|
|
70
|
+
Returns the parsed response dict:
|
|
71
|
+
``{"dataset_id", "dataset_name", "appended", "item_count"}``.
|
|
72
|
+
|
|
73
|
+
:raises ValueError: if *ref* is empty, *items* is empty, or an item has
|
|
74
|
+
no ``input``.
|
|
75
|
+
"""
|
|
76
|
+
if not ref:
|
|
77
|
+
raise ValueError("trodo: append_dataset(ref) requires a dataset ref")
|
|
78
|
+
if isinstance(items, dict):
|
|
79
|
+
items = [items]
|
|
80
|
+
if not items:
|
|
81
|
+
raise ValueError("trodo: append_dataset(ref, items) requires items")
|
|
82
|
+
|
|
83
|
+
body = {
|
|
84
|
+
"items": [_clean_item(i) for i in items],
|
|
85
|
+
"create": bool(create),
|
|
86
|
+
}
|
|
87
|
+
return self._http.append_dataset_items(ref, body)
|
|
88
|
+
|
|
89
|
+
def export(
|
|
90
|
+
self,
|
|
91
|
+
ref: str,
|
|
92
|
+
token: str,
|
|
93
|
+
labels: str = "all",
|
|
94
|
+
include_unlabelled: bool = False,
|
|
95
|
+
version: Optional[int] = None,
|
|
96
|
+
since: Optional[str] = None,
|
|
97
|
+
) -> Dict[str, Any]:
|
|
98
|
+
"""Read the dataset *ref*'s labelled export.
|
|
99
|
+
|
|
100
|
+
Each row is the dataset's training shape: ``input``, ``output``,
|
|
101
|
+
``expected``, ``evaluations`` (the Evals v3 verdicts snapshotted onto
|
|
102
|
+
the row), ``label_source`` / ``label_status`` / ``label_weight``,
|
|
103
|
+
``source``. *token* is an export token from Settings → Datasets
|
|
104
|
+
(``trodo_dx_…``); the site id cannot read exports. *labels* is one of
|
|
105
|
+
``all, confirmed, explicit, derived, implicit, proposed, unlabelled,
|
|
106
|
+
failed_only, passed_only``; *version* pins a snapshot.
|
|
107
|
+
|
|
108
|
+
Returns ``{"rows": [...], "version": int | None}``.
|
|
109
|
+
|
|
110
|
+
:raises ValueError: if *ref* or *token* is empty.
|
|
111
|
+
:raises RuntimeError: on a non-2xx response (the message carries the status and body).
|
|
112
|
+
"""
|
|
113
|
+
if not ref:
|
|
114
|
+
raise ValueError("trodo: export_dataset(ref) requires a dataset ref")
|
|
115
|
+
if not token:
|
|
116
|
+
raise ValueError("trodo: export_dataset requires an export token (Settings → Datasets)")
|
|
117
|
+
params: Dict[str, Any] = {"labels": labels}
|
|
118
|
+
if include_unlabelled:
|
|
119
|
+
params["include_unlabelled"] = 1
|
|
120
|
+
if version is not None:
|
|
121
|
+
params["version"] = int(version)
|
|
122
|
+
if since:
|
|
123
|
+
params["since"] = since
|
|
124
|
+
res = self._http.export_dataset(ref, params, token)
|
|
125
|
+
if res.get("__error"):
|
|
126
|
+
raise RuntimeError(
|
|
127
|
+
f"trodo: export of {ref!r} failed: HTTP {res.get('status')} {res.get('message') or res.get('error') or ''}".strip()
|
|
128
|
+
)
|
|
129
|
+
rows: List[Dict[str, Any]] = []
|
|
130
|
+
for line in (res.get("text") or "").split("\n"):
|
|
131
|
+
line = line.strip()
|
|
132
|
+
if line:
|
|
133
|
+
rows.append(json.loads(line))
|
|
134
|
+
return {"rows": rows, "version": res.get("version")}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
SDK_VERSION = "2.25.0"
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"""Dataset ingest — append evaluation items to a Trodo dataset.
|
|
2
|
-
|
|
3
|
-
Datasets are the fixed inputs an experiment runs against: each item is a set of
|
|
4
|
-
variable values (``input``) plus an optional gold answer (``expected_output``)
|
|
5
|
-
and free-form ``metadata``. This manager appends items to a dataset addressed by
|
|
6
|
-
UUID or by name; when a name is used and no such dataset exists it is
|
|
7
|
-
auto-created (``create=True``, the default), so a first append bootstraps the
|
|
8
|
-
dataset without a separate create call.
|
|
9
|
-
|
|
10
|
-
Mirrors ``sdks/trodo-node-sdk/src/managers/DatasetManager.ts`` in shape.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
from typing import Any, Dict, List, Union
|
|
16
|
-
|
|
17
|
-
__all__ = ["DatasetManager"]
|
|
18
|
-
|
|
19
|
-
# The optional per-item fields, in wire order. ``input`` is always required.
|
|
20
|
-
_ITEM_OPTIONAL = ("expected_output", "metadata", "source_trace_ref")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _clean_item(item: Dict[str, Any]) -> Dict[str, Any]:
|
|
24
|
-
if not isinstance(item, dict):
|
|
25
|
-
raise ValueError("trodo: each dataset item must be a dict")
|
|
26
|
-
if "input" not in item or item["input"] is None:
|
|
27
|
-
raise ValueError("trodo: each dataset item requires an 'input' dict")
|
|
28
|
-
out: Dict[str, Any] = {"input": item["input"]}
|
|
29
|
-
for key in _ITEM_OPTIONAL:
|
|
30
|
-
if item.get(key) is not None:
|
|
31
|
-
out[key] = item[key]
|
|
32
|
-
return out
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class DatasetManager:
|
|
36
|
-
"""Append items to the team's evaluation datasets."""
|
|
37
|
-
|
|
38
|
-
def __init__(self, http_client: Any) -> None:
|
|
39
|
-
self._http = http_client
|
|
40
|
-
|
|
41
|
-
def append(
|
|
42
|
-
self,
|
|
43
|
-
ref: str,
|
|
44
|
-
items: Union[Dict[str, Any], List[Dict[str, Any]]],
|
|
45
|
-
create: bool = True,
|
|
46
|
-
) -> Dict[str, Any]:
|
|
47
|
-
"""Append one or more items to the dataset *ref* (a UUID or a name).
|
|
48
|
-
|
|
49
|
-
*items* is a list of dicts shaped ``{"input": {<var>: value, ...},
|
|
50
|
-
"expected_output"?: str, "metadata"?: dict, "source_trace_ref"?: dict}``;
|
|
51
|
-
a single dict is accepted and wrapped into a one-item list. ``None``
|
|
52
|
-
optional fields are dropped from the wire payload.
|
|
53
|
-
|
|
54
|
-
When *ref* is a name that doesn't exist yet, ``create=True`` (default)
|
|
55
|
-
auto-creates the dataset; pass ``create=False`` to require it to exist.
|
|
56
|
-
|
|
57
|
-
Returns the parsed response dict:
|
|
58
|
-
``{"dataset_id", "dataset_name", "appended", "item_count"}``.
|
|
59
|
-
|
|
60
|
-
:raises ValueError: if *ref* is empty, *items* is empty, or an item has
|
|
61
|
-
no ``input``.
|
|
62
|
-
"""
|
|
63
|
-
if not ref:
|
|
64
|
-
raise ValueError("trodo: append_dataset(ref) requires a dataset ref")
|
|
65
|
-
if isinstance(items, dict):
|
|
66
|
-
items = [items]
|
|
67
|
-
if not items:
|
|
68
|
-
raise ValueError("trodo: append_dataset(ref, items) requires items")
|
|
69
|
-
|
|
70
|
-
body = {
|
|
71
|
-
"items": [_clean_item(i) for i in items],
|
|
72
|
-
"create": bool(create),
|
|
73
|
-
}
|
|
74
|
-
return self._http.append_dataset_items(ref, body)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|