trodo-python 2.25.0__tar.gz → 2.26.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 (73) hide show
  1. {trodo_python-2.25.0 → trodo_python-2.26.0}/PKG-INFO +1 -1
  2. {trodo_python-2.25.0 → trodo_python-2.26.0}/pyproject.toml +1 -1
  3. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_datasets.py +28 -0
  4. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/__init__.py +26 -1
  5. trodo_python-2.26.0/trodo/_version.py +1 -0
  6. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/api/http_client.py +39 -0
  7. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/managers/dataset_manager.py +49 -1
  8. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo_python.egg-info/PKG-INFO +1 -1
  9. trodo_python-2.25.0/trodo/_version.py +0 -1
  10. {trodo_python-2.25.0 → trodo_python-2.26.0}/README.md +0 -0
  11. {trodo_python-2.25.0 → trodo_python-2.26.0}/setup.cfg +0 -0
  12. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_anon_distinct_id.py +0 -0
  13. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_auto_instrument_fixes.py +0 -0
  14. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_cross_process_session.py +0 -0
  15. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_end_run.py +0 -0
  16. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_error_enrichment.py +0 -0
  17. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_experiments.py +0 -0
  18. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_framework_deference.py +0 -0
  19. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_get_prompt_version_types.py +0 -0
  20. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_instrumentor_resolution.py +0 -0
  21. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_llm_usage_cost.py +0 -0
  22. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_multi_agent_shape.py +0 -0
  23. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_processor_methods.py +0 -0
  24. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_prompt_fingerprint.py +0 -0
  25. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_prompt_trace.py +0 -0
  26. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_prompts.py +0 -0
  27. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_register_otel.py +0 -0
  28. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_sender.py +0 -0
  29. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_start_run.py +0 -0
  30. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_user_traits.py +0 -0
  31. {trodo_python-2.25.0 → trodo_python-2.26.0}/tests/test_wrap_agent_unchanged.py +0 -0
  32. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/api/__init__.py +0 -0
  33. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/api/_retry.py +0 -0
  34. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/api/async_client.py +0 -0
  35. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/api/endpoints.py +0 -0
  36. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/auto/__init__.py +0 -0
  37. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/auto/auto_event_manager.py +0 -0
  38. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/client.py +0 -0
  39. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/delivery/__init__.py +0 -0
  40. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/delivery/sender.py +0 -0
  41. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/managers/__init__.py +0 -0
  42. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/managers/experiment_manager.py +0 -0
  43. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/managers/group_manager.py +0 -0
  44. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/managers/people_manager.py +0 -0
  45. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/managers/prompt_manager.py +0 -0
  46. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/managers/user_manager.py +0 -0
  47. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/__init__.py +0 -0
  48. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/auto_instrument.py +0 -0
  49. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/context.py +0 -0
  50. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/helpers.py +0 -0
  51. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/processor.py +0 -0
  52. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/prompt_trace.py +0 -0
  53. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/register.py +0 -0
  54. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/transport.py +0 -0
  55. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/otel/wrap_agent.py +0 -0
  56. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/prompts/__init__.py +0 -0
  57. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/prompts/compile.py +0 -0
  58. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/prompts/template.py +0 -0
  59. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/prompts/types.py +0 -0
  60. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/queue/__init__.py +0 -0
  61. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/queue/batch_flusher.py +0 -0
  62. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/queue/event_queue.py +0 -0
  63. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/session/__init__.py +0 -0
  64. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/session/server_session.py +0 -0
  65. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/session/session_manager.py +0 -0
  66. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/types.py +0 -0
  67. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/user_context.py +0 -0
  68. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/util/__init__.py +0 -0
  69. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo/util/lru.py +0 -0
  70. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo_python.egg-info/SOURCES.txt +0 -0
  71. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo_python.egg-info/dependency_links.txt +0 -0
  72. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo_python.egg-info/requires.txt +0 -0
  73. {trodo_python-2.25.0 → trodo_python-2.26.0}/trodo_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trodo-python
3
- Version: 2.25.0
3
+ Version: 2.26.0
4
4
  Summary: Trodo Analytics SDK for Python — server-side event tracking
5
5
  License: ISC
6
6
  Keywords: analytics,tracking,trodo,server-side
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "trodo-python"
7
- version = "2.25.0"
7
+ version = "2.26.0"
8
8
  description = "Trodo Analytics SDK for Python — server-side event tracking"
9
9
  readme = "README.md"
10
10
  license = { text = "ISC" }
@@ -100,3 +100,31 @@ 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")
@@ -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.25.0"
44
+ __version__ = "2.26.0"
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
 
@@ -12,7 +12,8 @@ Mirrors ``sdks/trodo-node-sdk/src/managers/DatasetManager.ts`` in shape.
12
12
 
13
13
  from __future__ import annotations
14
14
 
15
- from typing import Any, Dict, List, Union
15
+ import json
16
+ from typing import Any, Dict, List, Optional, Union
16
17
 
17
18
  __all__ = ["DatasetManager"]
18
19
 
@@ -72,3 +73,50 @@ class DatasetManager:
72
73
  "create": bool(create),
73
74
  }
74
75
  return self._http.append_dataset_items(ref, body)
76
+
77
+ def export(
78
+ self,
79
+ ref: str,
80
+ token: str,
81
+ labels: str = "all",
82
+ include_unlabelled: bool = False,
83
+ version: Optional[int] = None,
84
+ since: Optional[str] = None,
85
+ ) -> Dict[str, Any]:
86
+ """Read the dataset *ref*'s labelled export.
87
+
88
+ Each row is the dataset's training shape: ``input``, ``output``,
89
+ ``expected``, ``evaluations`` (the Evals v3 verdicts snapshotted onto
90
+ the row), ``label_source`` / ``label_status`` / ``label_weight``,
91
+ ``source``. *token* is an export token from Settings → Datasets
92
+ (``trodo_dx_…``); the site id cannot read exports. *labels* is one of
93
+ ``all, confirmed, explicit, derived, implicit, proposed, unlabelled,
94
+ failed_only, passed_only``; *version* pins a snapshot.
95
+
96
+ Returns ``{"rows": [...], "version": int | None}``.
97
+
98
+ :raises ValueError: if *ref* or *token* is empty.
99
+ :raises RuntimeError: on a non-2xx response (the message carries the status and body).
100
+ """
101
+ if not ref:
102
+ raise ValueError("trodo: export_dataset(ref) requires a dataset ref")
103
+ if not token:
104
+ raise ValueError("trodo: export_dataset requires an export token (Settings → Datasets)")
105
+ params: Dict[str, Any] = {"labels": labels}
106
+ if include_unlabelled:
107
+ params["include_unlabelled"] = 1
108
+ if version is not None:
109
+ params["version"] = int(version)
110
+ if since:
111
+ params["since"] = since
112
+ res = self._http.export_dataset(ref, params, token)
113
+ if res.get("__error"):
114
+ raise RuntimeError(
115
+ f"trodo: export of {ref!r} failed: HTTP {res.get('status')} {res.get('message') or res.get('error') or ''}".strip()
116
+ )
117
+ rows: List[Dict[str, Any]] = []
118
+ for line in (res.get("text") or "").split("\n"):
119
+ line = line.strip()
120
+ if line:
121
+ rows.append(json.loads(line))
122
+ return {"rows": rows, "version": res.get("version")}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trodo-python
3
- Version: 2.25.0
3
+ Version: 2.26.0
4
4
  Summary: Trodo Analytics SDK for Python — server-side event tracking
5
5
  License: ISC
6
6
  Keywords: analytics,tracking,trodo,server-side
@@ -1 +0,0 @@
1
- SDK_VERSION = "2.25.0"
File without changes
File without changes