trodo-python 2.26.0__tar.gz → 2.26.2__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 (74) hide show
  1. {trodo_python-2.26.0 → trodo_python-2.26.2}/PKG-INFO +1 -1
  2. {trodo_python-2.26.0 → trodo_python-2.26.2}/pyproject.toml +1 -1
  3. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_datasets.py +16 -0
  4. trodo_python-2.26.2/tests/test_end_run_order.py +50 -0
  5. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/__init__.py +1 -1
  6. trodo_python-2.26.2/trodo/_version.py +1 -0
  7. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/managers/dataset_manager.py +16 -4
  8. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/processor.py +18 -1
  9. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo_python.egg-info/PKG-INFO +1 -1
  10. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo_python.egg-info/SOURCES.txt +1 -0
  11. trodo_python-2.26.0/trodo/_version.py +0 -1
  12. {trodo_python-2.26.0 → trodo_python-2.26.2}/README.md +0 -0
  13. {trodo_python-2.26.0 → trodo_python-2.26.2}/setup.cfg +0 -0
  14. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_anon_distinct_id.py +0 -0
  15. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_auto_instrument_fixes.py +0 -0
  16. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_cross_process_session.py +0 -0
  17. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_end_run.py +0 -0
  18. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_error_enrichment.py +0 -0
  19. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_experiments.py +0 -0
  20. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_framework_deference.py +0 -0
  21. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_get_prompt_version_types.py +0 -0
  22. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_instrumentor_resolution.py +0 -0
  23. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_llm_usage_cost.py +0 -0
  24. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_multi_agent_shape.py +0 -0
  25. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_processor_methods.py +0 -0
  26. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_prompt_fingerprint.py +0 -0
  27. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_prompt_trace.py +0 -0
  28. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_prompts.py +0 -0
  29. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_register_otel.py +0 -0
  30. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_sender.py +0 -0
  31. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_start_run.py +0 -0
  32. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_user_traits.py +0 -0
  33. {trodo_python-2.26.0 → trodo_python-2.26.2}/tests/test_wrap_agent_unchanged.py +0 -0
  34. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/api/__init__.py +0 -0
  35. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/api/_retry.py +0 -0
  36. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/api/async_client.py +0 -0
  37. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/api/endpoints.py +0 -0
  38. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/api/http_client.py +0 -0
  39. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/auto/__init__.py +0 -0
  40. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/auto/auto_event_manager.py +0 -0
  41. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/client.py +0 -0
  42. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/delivery/__init__.py +0 -0
  43. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/delivery/sender.py +0 -0
  44. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/managers/__init__.py +0 -0
  45. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/managers/experiment_manager.py +0 -0
  46. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/managers/group_manager.py +0 -0
  47. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/managers/people_manager.py +0 -0
  48. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/managers/prompt_manager.py +0 -0
  49. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/managers/user_manager.py +0 -0
  50. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/__init__.py +0 -0
  51. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/auto_instrument.py +0 -0
  52. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/context.py +0 -0
  53. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/helpers.py +0 -0
  54. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/prompt_trace.py +0 -0
  55. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/register.py +0 -0
  56. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/transport.py +0 -0
  57. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/otel/wrap_agent.py +0 -0
  58. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/prompts/__init__.py +0 -0
  59. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/prompts/compile.py +0 -0
  60. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/prompts/template.py +0 -0
  61. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/prompts/types.py +0 -0
  62. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/queue/__init__.py +0 -0
  63. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/queue/batch_flusher.py +0 -0
  64. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/queue/event_queue.py +0 -0
  65. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/session/__init__.py +0 -0
  66. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/session/server_session.py +0 -0
  67. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/session/session_manager.py +0 -0
  68. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/types.py +0 -0
  69. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/user_context.py +0 -0
  70. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/util/__init__.py +0 -0
  71. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo/util/lru.py +0 -0
  72. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo_python.egg-info/dependency_links.txt +0 -0
  73. {trodo_python-2.26.0 → trodo_python-2.26.2}/trodo_python.egg-info/requires.txt +0 -0
  74. {trodo_python-2.26.0 → trodo_python-2.26.2}/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.26.0
3
+ Version: 2.26.2
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.26.0"
7
+ version = "2.26.2"
8
8
  description = "Trodo Analytics SDK for Python — server-side event tracking"
9
9
  readme = "README.md"
10
10
  license = { text = "ISC" }
@@ -128,3 +128,19 @@ def test_export_requires_a_token_and_raises_on_http_errors():
128
128
  DatasetManager(FakeExportHttp({})).export("ds", "")
129
129
  with pytest.raises(RuntimeError, match="HTTP 404"):
130
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
+ ]
@@ -0,0 +1,50 @@
1
+ """end_run sends the run's queued spans before the end itself."""
2
+ from __future__ import annotations
3
+
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from trodo.otel.processor import TrodoSpanProcessor, TrodoSpan
7
+
8
+
9
+ class _Http:
10
+ def __init__(self, order: List[str]) -> None:
11
+ self.order = order
12
+
13
+ def post_run_end(self, run_id: str, payload: Dict[str, Any]) -> None:
14
+ self.order.append("end")
15
+
16
+
17
+ class _Sender:
18
+ def __init__(self, order: List[str]) -> None:
19
+ self.order = order
20
+ self.queued: List[Any] = []
21
+
22
+ def enqueue(self, item: Any) -> None:
23
+ self.queued.append(item)
24
+
25
+ def flush(self, timeout_s: Optional[float] = None) -> None:
26
+ self.order.append(f"flush:{len(self.queued)}")
27
+ self.queued.clear()
28
+
29
+
30
+ def _span(run_id: str, span_id: str) -> TrodoSpan:
31
+ return TrodoSpan(span_id=span_id, run_id=run_id, parent_span_id=None, kind="tool", name=span_id, status="ok", started_at="t")
32
+
33
+
34
+ def test_joined_spans_are_sent_before_the_end():
35
+ order: List[str] = []
36
+ proc = TrodoSpanProcessor(http_client=_Http(order), sender=_Sender(order))
37
+ proc.mark_joined("r1")
38
+ proc.enqueue_span(_span("r1", "s1"))
39
+ proc.enqueue_span(_span("r1", "s2"))
40
+ proc.end_run("r1", {"status": "ok"})
41
+ assert order == ["flush:2", "end"]
42
+
43
+
44
+ def test_buffered_spans_of_an_unjoined_run_are_not_lost():
45
+ order: List[str] = []
46
+ sender = _Sender(order)
47
+ proc = TrodoSpanProcessor(http_client=_Http(order), sender=sender)
48
+ proc.enqueue_span(_span("r2", "s1"))
49
+ proc.end_run("r2", {"status": "ok"})
50
+ assert order == ["flush:1", "end"]
@@ -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.26.0"
44
+ __version__ = "2.26.2"
45
45
 
46
46
  from typing import Any, Callable, Dict, List, Optional, Union
47
47
 
@@ -0,0 +1 @@
1
+ SDK_VERSION = "2.26.2"
@@ -17,16 +17,28 @@ from typing import Any, Dict, List, Optional, Union
17
17
 
18
18
  __all__ = ["DatasetManager"]
19
19
 
20
- # The optional per-item fields, in wire order. ``input`` is always required.
21
- _ITEM_OPTIONAL = ("expected_output", "metadata", "source_trace_ref")
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")
22
27
 
23
28
 
24
29
  def _clean_item(item: Dict[str, Any]) -> Dict[str, Any]:
25
30
  if not isinstance(item, dict):
26
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
27
39
  if "input" not in item or item["input"] is None:
28
- raise ValueError("trodo: each dataset item requires an 'input' dict")
29
- out: Dict[str, Any] = {"input": item["input"]}
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"]}
30
42
  for key in _ITEM_OPTIONAL:
31
43
  if item.get(key) is not None:
32
44
  out[key] = item[key]
@@ -11,6 +11,9 @@ import threading
11
11
  from dataclasses import dataclass, asdict
12
12
  from typing import Any, Dict, List, Optional, Union
13
13
 
14
+ # How long end_run waits for the run's queued spans to send before sending the end.
15
+ END_RUN_FLUSH_S = 5.0
16
+
14
17
 
15
18
  @dataclass
16
19
  class TrodoRun:
@@ -189,7 +192,21 @@ class TrodoSpanProcessor:
189
192
  pass
190
193
 
191
194
  def end_run(self, run_id: str, payload: Dict[str, Any]) -> None:
192
- """Finalise a Run opened by start_run."""
195
+ """Finalise a Run opened by start_run.
196
+
197
+ The run's spans go first: joined spans wait in the sender's queue, and
198
+ an end that overtakes them gets the run evaluated on a partial trace.
199
+ Bounded, and a failure here never holds up the end.
200
+ """
201
+ try:
202
+ with self._lock:
203
+ buffered = self._pending.pop(run_id, None) or []
204
+ if buffered:
205
+ self._send_spans(run_id, [s.to_dict() for s in buffered])
206
+ if self._sender is not None:
207
+ self._sender.flush(END_RUN_FLUSH_S)
208
+ except Exception:
209
+ pass
193
210
  try:
194
211
  self._http.post_run_end(run_id, payload)
195
212
  except Exception:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trodo-python
3
- Version: 2.26.0
3
+ Version: 2.26.2
4
4
  Summary: Trodo Analytics SDK for Python — server-side event tracking
5
5
  License: ISC
6
6
  Keywords: analytics,tracking,trodo,server-side
@@ -5,6 +5,7 @@ tests/test_auto_instrument_fixes.py
5
5
  tests/test_cross_process_session.py
6
6
  tests/test_datasets.py
7
7
  tests/test_end_run.py
8
+ tests/test_end_run_order.py
8
9
  tests/test_error_enrichment.py
9
10
  tests/test_experiments.py
10
11
  tests/test_framework_deference.py
@@ -1 +0,0 @@
1
- SDK_VERSION = "2.26.0"
File without changes
File without changes