agentx-python 0.8.2__tar.gz → 0.8.4__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 (86) hide show
  1. {agentx_python-0.8.2/agentx_python.egg-info → agentx_python-0.8.4}/PKG-INFO +1 -1
  2. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/models.py +11 -3
  3. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/runner.py +15 -1
  4. agentx_python-0.8.4/agentx/tracing/eval_scope.py +44 -0
  5. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/tracing/tracer.py +20 -2
  6. agentx_python-0.8.4/agentx/version.py +1 -0
  7. {agentx_python-0.8.2 → agentx_python-0.8.4/agentx_python.egg-info}/PKG-INFO +1 -1
  8. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx_python.egg-info/SOURCES.txt +2 -0
  9. agentx_python-0.8.4/tests/test_eval_scope.py +116 -0
  10. agentx_python-0.8.2/agentx/version.py +0 -1
  11. {agentx_python-0.8.2 → agentx_python-0.8.4}/LICENSE +0 -0
  12. {agentx_python-0.8.2 → agentx_python-0.8.4}/README.md +0 -0
  13. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/__init__.py +0 -0
  14. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/agentx.py +0 -0
  15. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/cli.py +0 -0
  16. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/__init__.py +0 -0
  17. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/_term.py +0 -0
  18. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/adapters/__init__.py +0 -0
  19. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/adapters/http_endpoint.py +0 -0
  20. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/adapters/precomputed.py +0 -0
  21. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/adapters/raw.py +0 -0
  22. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/client.py +0 -0
  23. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/datasets.py +0 -0
  24. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/evaluation_settings.py +0 -0
  25. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/prompts.py +0 -0
  26. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/reporting.py +0 -0
  27. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/results.py +0 -0
  28. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/tool_schemas.py +0 -0
  29. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/evaluations/tracing.py +0 -0
  30. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/exceptions.py +0 -0
  31. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/export.py +0 -0
  32. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/feedback.py +0 -0
  33. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/__init__.py +0 -0
  34. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/_traced_call.py +0 -0
  35. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/anthropic.py +0 -0
  36. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/autogen.py +0 -0
  37. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/crewai.py +0 -0
  38. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/databricks.py +0 -0
  39. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/google_adk.py +0 -0
  40. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/google_genai.py +0 -0
  41. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/langchain.py +0 -0
  42. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/litellm.py +0 -0
  43. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/llamaindex.py +0 -0
  44. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/moveworks.py +0 -0
  45. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/openai.py +0 -0
  46. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/integrations/openai_agents.py +0 -0
  47. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/__init__.py +0 -0
  48. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/agents.py +0 -0
  49. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/client.py +0 -0
  50. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/judge_scorers.py +0 -0
  51. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/models.py +0 -0
  52. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/online_evaluators.py +0 -0
  53. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/patterns.py +0 -0
  54. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/profile.py +0 -0
  55. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/scorers.py +0 -0
  56. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/sessions.py +0 -0
  57. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/monitor/signals.py +0 -0
  58. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/outcomes.py +0 -0
  59. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/projects.py +0 -0
  60. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/py.typed +0 -0
  61. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/resources/__init__.py +0 -0
  62. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/resources/agent.py +0 -0
  63. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/resources/conversation.py +0 -0
  64. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/resources/workforce.py +0 -0
  65. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/testing.py +0 -0
  66. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/traces.py +0 -0
  67. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/tracing/__init__.py +0 -0
  68. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/tracing/ci_types.py +0 -0
  69. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/tracing/ingest_client.py +0 -0
  70. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx/util.py +0 -0
  71. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx_python.egg-info/dependency_links.txt +0 -0
  72. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx_python.egg-info/entry_points.txt +0 -0
  73. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx_python.egg-info/not-zip-safe +0 -0
  74. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx_python.egg-info/requires.txt +0 -0
  75. {agentx_python-0.8.2 → agentx_python-0.8.4}/agentx_python.egg-info/top_level.txt +0 -0
  76. {agentx_python-0.8.2 → agentx_python-0.8.4}/setup.cfg +0 -0
  77. {agentx_python-0.8.2 → agentx_python-0.8.4}/setup.py +0 -0
  78. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_deep_dive_fixes.py +0 -0
  79. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_docs_match_sdk.py +0 -0
  80. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_integration.py +0 -0
  81. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_integrations.py +0 -0
  82. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_judge_scorers.py +0 -0
  83. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_pairwise.py +0 -0
  84. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_selfhost_analysis_fallback.py +0 -0
  85. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_span_tree.py +0 -0
  86. {agentx_python-0.8.2 → agentx_python-0.8.4}/tests/test_testing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
5
  Home-page: https://github.com/AgentX-ai/AgentX-python
6
6
  Author: Robin Wang and AgentX Team
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import Any, Dict, List, Literal, Optional, Union
4
- from pydantic import BaseModel, Field, model_validator
4
+ from pydantic import AliasChoices, BaseModel, Field, model_validator
5
5
 
6
6
  # ---------------------------------------------------------------------------
7
7
  # Observable trace
@@ -418,7 +418,11 @@ class RunResultRow(BaseModel):
418
418
  latency_ms: Optional[float] = Field(default=None, alias="latencyMs")
419
419
  input_tokens: Optional[int] = Field(default=None, alias="inputTokens")
420
420
  output_tokens: Optional[int] = Field(default=None, alias="outputTokens")
421
- cosine_similarity: Optional[float] = Field(default=None, alias="cosineSimilarity")
421
+ # Self-host sends vectorSimilarity, the hosted platform cosineSimilarity - accept both
422
+ # (row.cosine_similarity was silently None forever on self-host before this).
423
+ cosine_similarity: Optional[float] = Field(
424
+ default=None, validation_alias=AliasChoices("cosineSimilarity", "vectorSimilarity")
425
+ )
422
426
  jaccard_similarity: Optional[float] = Field(default=None, alias="jaccardSimilarity")
423
427
  bleu_score: Optional[float] = Field(default=None, alias="bleuScore")
424
428
  rouge_score: Optional[float] = Field(default=None, alias="rougeScore")
@@ -468,7 +472,11 @@ class ReportStatistics(BaseModel):
468
472
  average_rating: float = Field(default=0.0, alias="averageRating")
469
473
  min_rating: float = Field(default=0.0, alias="minRating")
470
474
  max_rating: float = Field(default=0.0, alias="maxRating")
471
- cosine_similarity: Optional[float] = Field(default=None, alias="cosineSimilarity")
475
+ # Self-host sends vectorSimilarity, the hosted platform cosineSimilarity - accept both
476
+ # (row.cosine_similarity was silently None forever on self-host before this).
477
+ cosine_similarity: Optional[float] = Field(
478
+ default=None, validation_alias=AliasChoices("cosineSimilarity", "vectorSimilarity")
479
+ )
472
480
  jaccard_similarity: Optional[float] = Field(default=None, alias="jaccardSimilarity")
473
481
  bleu_score: Optional[float] = Field(default=None, alias="bleuScore")
474
482
  rouge_score: Optional[float] = Field(default=None, alias="rougeScore")
@@ -119,7 +119,21 @@ class EvaluationRunContext:
119
119
  # ------------------------------------------------------------------
120
120
 
121
121
  def execute(self, adapter: AdapterLike) -> "EvaluationRunContext":
122
- """Run all cases locally and submit batches to AgentX."""
122
+ """Run all cases locally and submit batches to AgentX.
123
+
124
+ The whole loop runs inside the eval-run scope (tracing/eval_scope.py): any trace the
125
+ agent function creates is stamped source="eval-run" + monitor=False automatically, so
126
+ eval traffic never skews production monitoring and no one has to remember a flag.
127
+ """
128
+ from agentx.tracing.eval_scope import enter_eval_run, exit_eval_run
129
+
130
+ scope_token = enter_eval_run(self._run.run_id)
131
+ try:
132
+ return self._execute_inner(adapter)
133
+ finally:
134
+ exit_eval_run(scope_token)
135
+
136
+ def _execute_inner(self, adapter: AdapterLike) -> "EvaluationRunContext":
123
137
  normalized = _wrap_adapter(adapter)
124
138
  cases = _build_cases(self._dataset, self._run, self._evaluation_settings)
125
139
  max_batch = self._run.limits.max_batch_size
@@ -0,0 +1,44 @@
1
+ """The eval-run scope: how traces created inside an evaluation stop passing as production.
2
+
3
+ An offline run executes the user's own agent function, and an instrumented agent traces itself -
4
+ which is exactly what makes trajectory matching and retrieval-context extraction work. But those
5
+ traces are not production traffic, and before this scope existed the burden of saying so sat on
6
+ every caller: remember ``monitor=False`` on every ``tracer.trace(...)`` inside an eval, or the
7
+ engine would double-judge each case, raise signals on synthetic questions, and count the run's
8
+ latencies into production KPIs. Nobody remembered - including our own samples.
9
+
10
+ ``EvaluationRunContext.execute()`` enters this scope around the whole run. While it is active,
11
+ every trace the tracer sends is stamped:
12
+
13
+ - ``source="eval-run"`` - the engine files it as eval traffic (excluded from monitor KPIs,
14
+ metrics, sessions and the Live Traces default view; cost keeps it, split out)
15
+ - ``monitor=False`` - unless the caller explicitly passed ``monitor=True``, which is
16
+ respected as a deliberate choice
17
+ - ``metadata.evalRunId`` - so a trace can always be walked back to the run that produced it
18
+
19
+ A ``contextvars.ContextVar`` rather than tracer state: it nests correctly, cannot leak across
20
+ concurrent runs in async code, and costs nothing when no run is active. The one known limit is
21
+ threads the agent function spawns itself - a context var does not cross a bare ``Thread()`` -
22
+ which matches the tracer's existing documented posture for user-managed threads.
23
+ """
24
+
25
+ from contextvars import ContextVar
26
+ from typing import Optional
27
+
28
+ EVAL_RUN_SOURCE = "eval-run"
29
+
30
+ _current_eval_run_id: ContextVar[Optional[str]] = ContextVar("agentx_eval_run_id", default=None)
31
+
32
+
33
+ def enter_eval_run(run_id: str):
34
+ """Mark the current context as inside an eval run. Returns the token for ``exit_eval_run``."""
35
+ return _current_eval_run_id.set(run_id)
36
+
37
+
38
+ def exit_eval_run(token) -> None:
39
+ _current_eval_run_id.reset(token)
40
+
41
+
42
+ def current_eval_run_id() -> Optional[str]:
43
+ """The run id when inside ``execute()``, else None."""
44
+ return _current_eval_run_id.get()
@@ -13,6 +13,7 @@ from uuid import uuid4
13
13
  from agentx.exceptions import CIGateFailure
14
14
  from agentx.tracing.ingest_client import IngestClient
15
15
  from agentx.tracing.ci_types import CIRun, CIRunResult, CIRunStatus, CIQuestionScore
16
+ from agentx.tracing.eval_scope import EVAL_RUN_SOURCE, current_eval_run_id
16
17
 
17
18
  F = TypeVar("F", bound=Callable[..., Any])
18
19
 
@@ -167,9 +168,21 @@ class _TraceSpan:
167
168
  # flush() uses; child-only spans keep their async fire-and-forget behavior.
168
169
  self._tracer.flush(timeout=5.0)
169
170
 
171
+ # Inside an eval run (evaluations' execute()), every trace states what it is: eval
172
+ # traffic. monitor=False unless the caller explicitly said True; the run id rides in
173
+ # metadata so the trace can be walked back to its run. See tracing/eval_scope.py.
174
+ eval_run_id = current_eval_run_id()
175
+ monitor = self._monitor
176
+ metadata = self._metadata
177
+ source = None
178
+ if eval_run_id is not None:
179
+ source = EVAL_RUN_SOURCE
180
+ if monitor is not True:
181
+ monitor = False
182
+ metadata = {**(metadata or {}), "evalRunId": eval_run_id}
170
183
  self._trace_id = self._tracer._send(
171
184
  sync=self._sync,
172
- monitor=self._monitor,
185
+ monitor=monitor,
173
186
  pattern_ids=self._pattern_ids,
174
187
  name=self.name,
175
188
  agent_id=self._agent_id,
@@ -177,7 +190,7 @@ class _TraceSpan:
177
190
  output=_safe_serialize(self.output) if self.output is not None else None,
178
191
  latency_ms=latency_ms,
179
192
  error=self._error,
180
- metadata=self._metadata,
193
+ metadata=metadata,
181
194
  framework=self._framework or self._captured_framework,
182
195
  model=self._model or self._captured_model,
183
196
  tool_calls=self.tool_calls or None,
@@ -189,6 +202,7 @@ class _TraceSpan:
189
202
  span_id=self._span_id,
190
203
  parent_span_id=self._parent_span_id,
191
204
  span_kind=self._span_kind,
205
+ source=source,
192
206
  started_at_unix_nano=str(int(self._start * 1_000_000_000)) if self._start else None,
193
207
  )
194
208
  return False # never suppress exceptions
@@ -332,6 +346,8 @@ class _TraceSpan:
332
346
  wire["span_kind"] = span_kind
333
347
  if child._session_id:
334
348
  wire["session_id"] = child._session_id
349
+ if current_eval_run_id() is not None:
350
+ wire["source"] = EVAL_RUN_SOURCE
335
351
  wire["span_id"] = child._span_id
336
352
  if child._parent_span_id:
337
353
  wire["parent_span_id"] = child._parent_span_id
@@ -1148,6 +1164,8 @@ class Tracer:
1148
1164
  wire["started_at_unix_nano"] = payload["started_at_unix_nano"]
1149
1165
  if "agent_id" in payload:
1150
1166
  wire["agent_id"] = payload["agent_id"]
1167
+ if "source" in payload:
1168
+ wire["source"] = payload["source"]
1151
1169
  if "span_kind" in payload:
1152
1170
  wire["span_kind"] = payload["span_kind"]
1153
1171
 
@@ -0,0 +1 @@
1
+ VERSION = "0.8.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
5
  Home-page: https://github.com/AgentX-ai/AgentX-python
6
6
  Author: Robin Wang and AgentX Team
@@ -61,6 +61,7 @@ agentx/resources/conversation.py
61
61
  agentx/resources/workforce.py
62
62
  agentx/tracing/__init__.py
63
63
  agentx/tracing/ci_types.py
64
+ agentx/tracing/eval_scope.py
64
65
  agentx/tracing/ingest_client.py
65
66
  agentx/tracing/tracer.py
66
67
  agentx_python.egg-info/PKG-INFO
@@ -72,6 +73,7 @@ agentx_python.egg-info/requires.txt
72
73
  agentx_python.egg-info/top_level.txt
73
74
  tests/test_deep_dive_fixes.py
74
75
  tests/test_docs_match_sdk.py
76
+ tests/test_eval_scope.py
75
77
  tests/test_integration.py
76
78
  tests/test_integrations.py
77
79
  tests/test_judge_scorers.py
@@ -0,0 +1,116 @@
1
+ """The eval-run scope: traces created inside execute() stop passing as production.
2
+
3
+ Before this, the burden sat on every caller: remember monitor=False on every trace inside an
4
+ eval or the engine double-judges, raises signals on synthetic questions, and counts eval
5
+ latencies into production KPIs. These pin that the stamping is automatic, respects an explicit
6
+ monitor=True, and vanishes completely outside the scope.
7
+ """
8
+
9
+ from agentx.tracing.eval_scope import current_eval_run_id, enter_eval_run, exit_eval_run
10
+ from agentx.tracing.tracer import Tracer
11
+
12
+
13
+ class _CaptureTracer(Tracer):
14
+ """A tracer whose network is a list."""
15
+
16
+ def __init__(self): # noqa: D401 - bypass real client setup
17
+ self.sent = []
18
+ self._active_spans = []
19
+ self._pending_tool_calls = []
20
+ self._pending_retrievals = []
21
+
22
+ def _send(self, sync=False, **kwargs):
23
+ self.sent.append({k: v for k, v in kwargs.items() if v is not None})
24
+ return "trace-1"
25
+
26
+ def _dispatch(self, wire, *, sync=False):
27
+ self.sent.append(wire)
28
+ return "trace-child"
29
+
30
+ # The bits of Tracer the span touches.
31
+ def _push_active_span(self, span):
32
+ self._active_spans.append(span)
33
+
34
+ def _pop_active_span(self, span):
35
+ if span in self._active_spans:
36
+ self._active_spans.remove(span)
37
+
38
+ @property
39
+ def current_span(self):
40
+ return self._active_spans[-1] if self._active_spans else None
41
+
42
+ def flush(self, timeout=5.0):
43
+ return True
44
+
45
+
46
+ def test_outside_the_scope_nothing_changes():
47
+ tracer = _CaptureTracer()
48
+ with tracer.trace("prod-agent", input={"q": "hi"}) as span:
49
+ span.output = "hello"
50
+ wire = tracer.sent[-1]
51
+ assert "source" not in wire
52
+ assert "monitor" not in wire # None is filtered out, same as before
53
+ assert current_eval_run_id() is None
54
+
55
+
56
+ def test_inside_the_scope_traces_are_stamped():
57
+ tracer = _CaptureTracer()
58
+ token = enter_eval_run("run-42")
59
+ try:
60
+ with tracer.trace("agent-under-eval", input={"q": "case 1"}) as span:
61
+ span.output = "answer"
62
+ finally:
63
+ exit_eval_run(token)
64
+ wire = tracer.sent[-1]
65
+ assert wire["source"] == "eval-run"
66
+ assert wire["monitor"] is False
67
+ assert wire["metadata"]["evalRunId"] == "run-42"
68
+
69
+
70
+ def test_explicit_monitor_true_is_respected():
71
+ # Someone deliberately pointing checks at eval traffic is a choice, not a mistake.
72
+ tracer = _CaptureTracer()
73
+ token = enter_eval_run("run-42")
74
+ try:
75
+ with tracer.trace("agent-under-eval", monitor=True) as span:
76
+ span.output = "x"
77
+ finally:
78
+ exit_eval_run(token)
79
+ wire = tracer.sent[-1]
80
+ assert wire["source"] == "eval-run"
81
+ assert wire["monitor"] is True
82
+
83
+
84
+ def test_child_spans_are_eval_traffic_too():
85
+ tracer = _CaptureTracer()
86
+ token = enter_eval_run("run-42")
87
+ try:
88
+ with tracer.trace("agent-under-eval") as span:
89
+ span.child_span("kb_search", output=["chunk"])
90
+ span.output = "x"
91
+ finally:
92
+ exit_eval_run(token)
93
+ child = next(w for w in tracer.sent if w.get("name") == "kb_search")
94
+ assert child["source"] == "eval-run"
95
+
96
+
97
+ def test_the_scope_does_not_leak():
98
+ tracer = _CaptureTracer()
99
+ token = enter_eval_run("run-42")
100
+ exit_eval_run(token)
101
+ with tracer.trace("prod-again") as span:
102
+ span.output = "y"
103
+ assert "source" not in tracer.sent[-1]
104
+
105
+
106
+ def test_caller_metadata_survives_the_stamp():
107
+ tracer = _CaptureTracer()
108
+ token = enter_eval_run("run-42")
109
+ try:
110
+ with tracer.trace("agent", metadata={"promptName": "support-v3"}) as span:
111
+ span.output = "x"
112
+ finally:
113
+ exit_eval_run(token)
114
+ md = tracer.sent[-1]["metadata"]
115
+ assert md["promptName"] == "support-v3"
116
+ assert md["evalRunId"] == "run-42"
@@ -1 +0,0 @@
1
- VERSION = "0.8.2"
File without changes
File without changes
File without changes
File without changes