trodo-python 2.18.0__tar.gz → 2.18.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.
Files changed (63) hide show
  1. {trodo_python-2.18.0 → trodo_python-2.18.1}/PKG-INFO +1 -1
  2. {trodo_python-2.18.0 → trodo_python-2.18.1}/pyproject.toml +1 -1
  3. trodo_python-2.18.1/tests/test_multi_agent_shape.py +106 -0
  4. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/__init__.py +14 -1
  5. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/client.py +4 -0
  6. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo_python.egg-info/PKG-INFO +1 -1
  7. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo_python.egg-info/SOURCES.txt +1 -0
  8. {trodo_python-2.18.0 → trodo_python-2.18.1}/README.md +0 -0
  9. {trodo_python-2.18.0 → trodo_python-2.18.1}/setup.cfg +0 -0
  10. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_anon_distinct_id.py +0 -0
  11. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_auto_instrument_fixes.py +0 -0
  12. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_cross_process_session.py +0 -0
  13. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_datasets.py +0 -0
  14. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_end_run.py +0 -0
  15. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_error_enrichment.py +0 -0
  16. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_experiments.py +0 -0
  17. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_llm_usage_cost.py +0 -0
  18. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_processor_methods.py +0 -0
  19. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_prompt_trace.py +0 -0
  20. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_prompts.py +0 -0
  21. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_register_otel.py +0 -0
  22. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_start_run.py +0 -0
  23. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_user_traits.py +0 -0
  24. {trodo_python-2.18.0 → trodo_python-2.18.1}/tests/test_wrap_agent_unchanged.py +0 -0
  25. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/api/__init__.py +0 -0
  26. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/api/async_client.py +0 -0
  27. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/api/endpoints.py +0 -0
  28. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/api/http_client.py +0 -0
  29. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/auto/__init__.py +0 -0
  30. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/auto/auto_event_manager.py +0 -0
  31. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/managers/__init__.py +0 -0
  32. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/managers/dataset_manager.py +0 -0
  33. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/managers/experiment_manager.py +0 -0
  34. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/managers/group_manager.py +0 -0
  35. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/managers/people_manager.py +0 -0
  36. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/managers/prompt_manager.py +0 -0
  37. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/managers/user_manager.py +0 -0
  38. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/__init__.py +0 -0
  39. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/auto_instrument.py +0 -0
  40. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/context.py +0 -0
  41. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/helpers.py +0 -0
  42. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/processor.py +0 -0
  43. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/prompt_trace.py +0 -0
  44. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/register.py +0 -0
  45. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/transport.py +0 -0
  46. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/otel/wrap_agent.py +0 -0
  47. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/prompts/__init__.py +0 -0
  48. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/prompts/compile.py +0 -0
  49. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/prompts/template.py +0 -0
  50. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/prompts/types.py +0 -0
  51. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/queue/__init__.py +0 -0
  52. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/queue/batch_flusher.py +0 -0
  53. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/queue/event_queue.py +0 -0
  54. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/session/__init__.py +0 -0
  55. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/session/server_session.py +0 -0
  56. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/session/session_manager.py +0 -0
  57. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/types.py +0 -0
  58. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/user_context.py +0 -0
  59. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/util/__init__.py +0 -0
  60. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo/util/lru.py +0 -0
  61. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo_python.egg-info/dependency_links.txt +0 -0
  62. {trodo_python-2.18.0 → trodo_python-2.18.1}/trodo_python.egg-info/requires.txt +0 -0
  63. {trodo_python-2.18.0 → trodo_python-2.18.1}/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.18.0
3
+ Version: 2.18.1
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.18.0"
7
+ version = "2.18.1"
8
8
  description = "Trodo Analytics SDK for Python — server-side event tracking"
9
9
  readme = "README.md"
10
10
  license = { text = "ISC" }
@@ -0,0 +1,106 @@
1
+ """Multi-agent run shape — one run, sub-agents as ``agent``-kind child spans.
2
+
3
+ The shape the ``trodo-tracing`` skill teaches: a request that fans out to
4
+ sub-agents produces ONE run whose tree carries an ``agent`` span per sub-agent,
5
+ with each sub-agent's own work nested underneath it. The failure mode this
6
+ guards against is a ``wrap_agent`` per sub-agent, which shatters one request
7
+ into N disconnected runs and loses whole-request cost, latency and the
8
+ delegation tree.
9
+
10
+ Also covers the module-level ``user`` / ``parent_run_id`` kwargs, which the
11
+ skill's guidance writes and which the top-level wrappers used to silently drop.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import inspect
17
+
18
+ import trodo
19
+ from trodo.otel.wrap_agent import span as span_ctx
20
+ from trodo.otel.wrap_agent import wrap_agent as wrap_agent_ctx
21
+
22
+
23
+ def _tree(payload):
24
+ """{span_id: (name, kind, parent_span_id)} for one ingested run payload."""
25
+ return {s["span_id"]: (s["name"], s["kind"], s["parent_span_id"]) for s in payload["spans"]}
26
+
27
+
28
+ def test_sub_agents_nest_as_agent_spans_in_one_run(processor, http):
29
+ """A supervisor with two workers and a writer = 1 run, 3 agent spans."""
30
+ with wrap_agent_ctx(
31
+ processor=processor, team_site_id="site", agent_name="research_assistant",
32
+ distinct_id="u_1", conversation_id="thread_1",
33
+ ) as run:
34
+ run.set_input([{"role": "user", "content": "why did revenue drop"}])
35
+
36
+ with span_ctx("planner", kind="agent") as s:
37
+ s.set_output({"subtasks": ["pricing", "churn"]})
38
+
39
+ for topic in ("pricing", "churn"):
40
+ with span_ctx(f"researcher:{topic}", kind="agent") as s:
41
+ s.set_attribute("topic", topic)
42
+ # The sub-agent's own tool call nests under the sub-agent, not the run.
43
+ with span_ctx("web_search", kind="tool") as t:
44
+ t.set_tool("web_search")
45
+ t.set_output({"hits": 3})
46
+ s.set_output({"topic": topic})
47
+
48
+ run.set_output({"answer": "pricing change in March"})
49
+
50
+ # Exactly ONE run — not one per sub-agent.
51
+ assert len(http.run_ingest) == 1
52
+ payload = http.run_ingest[0]
53
+ assert payload["run"]["agent_name"] == "research_assistant"
54
+ assert payload["run"]["conversation_id"] == "thread_1"
55
+
56
+ tree = _tree(payload)
57
+ by_name = {v[0]: (sid, v[1], v[2]) for sid, v in tree.items()}
58
+
59
+ # Three agent-kind sub-agent spans, all siblings directly under the run root.
60
+ parents = set()
61
+ for name in ("planner", "researcher:pricing", "researcher:churn"):
62
+ sid, kind, parent = by_name[name]
63
+ assert kind == "agent", f"{name} should be an agent span, got {kind}"
64
+ parents.add(parent)
65
+ assert len(parents) == 1, "sub-agents should be siblings under one run root"
66
+
67
+ # Each tool span nests under its own researcher, preserving the delegation tree.
68
+ tool_parents = {v[2] for sid, v in tree.items() if v[1] == "tool"}
69
+ researcher_ids = {by_name[n][0] for n in ("researcher:pricing", "researcher:churn")}
70
+ assert tool_parents == researcher_ids
71
+
72
+ # The run rolls up the whole request, which is the point of keeping it one run.
73
+ assert payload["run"]["span_count"] == 5
74
+ assert payload["run"]["tool_count"] == 2
75
+
76
+
77
+ def test_independently_triggered_sub_agent_links_via_parent_run_id(processor, http):
78
+ """The other shape: separate runs, linked — for independently-triggered work."""
79
+ with wrap_agent_ctx(
80
+ processor=processor, team_site_id="site", agent_name="intake", distinct_id="u_1",
81
+ ) as parent:
82
+ parent.set_output({"job": "enrich"})
83
+
84
+ with wrap_agent_ctx(
85
+ processor=processor, team_site_id="site", agent_name="enrichment",
86
+ distinct_id="u_1", parent_run_id=parent.run_id,
87
+ ) as child:
88
+ child.set_output({"done": True})
89
+
90
+ assert len(http.run_ingest) == 2
91
+ assert http.run_ingest[1]["run"]["parent_run_id"] == parent.run_id
92
+
93
+
94
+ def test_module_level_wrappers_forward_user_and_parent_run_id():
95
+ """``trodo.wrap_agent(..., user=...)`` is what the skill tells people to write.
96
+
97
+ The module-level wrappers used to drop ``user`` on the floor (and ``agent``
98
+ also dropped ``parent_run_id``), so the documented call raised TypeError.
99
+ """
100
+ for fn, expected in (
101
+ (trodo.wrap_agent, {"user", "parent_run_id", "conversation_id", "metadata"}),
102
+ (trodo.agent, {"user", "parent_run_id", "conversation_id", "metadata"}),
103
+ (trodo.start_run, {"user", "parent_run_id", "conversation_id", "metadata", "input"}),
104
+ ):
105
+ params = set(inspect.signature(fn).parameters)
106
+ assert expected <= params, f"{fn.__name__} is missing {expected - params}"
@@ -524,14 +524,21 @@ def wrap_agent(
524
524
  conversation_id: Optional[str] = None,
525
525
  parent_run_id: Optional[str] = None,
526
526
  metadata: Optional[Dict[str, Any]] = None,
527
+ user: Optional[Dict[str, Any]] = None,
527
528
  ) -> _wrap_agent_ctx:
528
- """Wrap a block as an agent run — returns a context manager yielding RunHandle."""
529
+ """Wrap a block as an agent run — returns a context manager yielding RunHandle.
530
+
531
+ ``user`` carries profile traits alongside the run — ``{"properties": ...,
532
+ "set_once": ..., "fixed_properties": ...}`` — so a request handler can
533
+ attribute the run and enrich the profile in one round trip.
534
+ """
529
535
  return _get_client().wrap_agent(
530
536
  agent_name,
531
537
  distinct_id=distinct_id,
532
538
  conversation_id=conversation_id,
533
539
  parent_run_id=parent_run_id,
534
540
  metadata=metadata,
541
+ user=user,
535
542
  )
536
543
 
537
544
 
@@ -540,14 +547,18 @@ def agent(
540
547
  *,
541
548
  distinct_id: Optional[str] = None,
542
549
  conversation_id: Optional[str] = None,
550
+ parent_run_id: Optional[str] = None,
543
551
  metadata: Optional[Dict[str, Any]] = None,
552
+ user: Optional[Dict[str, Any]] = None,
544
553
  ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
545
554
  """Decorator form of wrap_agent."""
546
555
  return _get_client().agent(
547
556
  agent_name,
548
557
  distinct_id=distinct_id,
549
558
  conversation_id=conversation_id,
559
+ parent_run_id=parent_run_id,
550
560
  metadata=metadata,
561
+ user=user,
551
562
  )
552
563
 
553
564
 
@@ -591,6 +602,7 @@ def start_run(
591
602
  parent_run_id: Optional[str] = None,
592
603
  metadata: Optional[Dict[str, Any]] = None,
593
604
  input: Any = None,
605
+ user: Optional[Dict[str, Any]] = None,
594
606
  ) -> str:
595
607
  """Open a Run record without a context manager.
596
608
 
@@ -607,6 +619,7 @@ def start_run(
607
619
  parent_run_id=parent_run_id,
608
620
  metadata=metadata,
609
621
  input=input,
622
+ user=user,
610
623
  )
611
624
 
612
625
 
@@ -357,7 +357,9 @@ class TrodoClient:
357
357
  *,
358
358
  distinct_id: Optional[str] = None,
359
359
  conversation_id: Optional[str] = None,
360
+ parent_run_id: Optional[str] = None,
360
361
  metadata: Optional[Dict[str, Any]] = None,
362
+ user: Optional[Dict[str, Any]] = None,
361
363
  ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
362
364
  """Decorator form of wrap_agent."""
363
365
 
@@ -368,7 +370,9 @@ class TrodoClient:
368
370
  agent_name,
369
371
  distinct_id=distinct_id,
370
372
  conversation_id=conversation_id,
373
+ parent_run_id=parent_run_id,
371
374
  metadata=metadata,
375
+ user=user,
372
376
  ) as run:
373
377
  result = fn(*args, **kwargs)
374
378
  run.set_output(result)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trodo-python
3
- Version: 2.18.0
3
+ Version: 2.18.1
4
4
  Summary: Trodo Analytics SDK for Python — server-side event tracking
5
5
  License: ISC
6
6
  Keywords: analytics,tracking,trodo,server-side
@@ -8,6 +8,7 @@ tests/test_end_run.py
8
8
  tests/test_error_enrichment.py
9
9
  tests/test_experiments.py
10
10
  tests/test_llm_usage_cost.py
11
+ tests/test_multi_agent_shape.py
11
12
  tests/test_processor_methods.py
12
13
  tests/test_prompt_trace.py
13
14
  tests/test_prompts.py
File without changes
File without changes