Skip to content

Commit 6a0cf23

Browse files
committed
fix(tracing): address greptile review findings
- Extract _build_sgp_span and _add_source_to_span to module-level helpers so the sync and async processors share a single implementation, removing byte-for-byte duplication that risked drifting between the two classes. - Strengthen test_sgp_span_input_and_output_propagated_on_end to actually assert the updated input/output reach create_span on the end call, matching what the test name and docstring promise.
1 parent bb6f046 commit 6a0cf23

2 files changed

Lines changed: 43 additions & 67 deletions

File tree

src/agentex/lib/core/tracing/processors/sgp_tracing_processor.py

Lines changed: 37 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,39 @@ def _get_span_type(span: Span) -> str:
2727
return "STANDALONE"
2828

2929

30+
def _add_source_to_span(span: Span, env_vars: EnvironmentVariables) -> None:
31+
if span.data is None:
32+
span.data = {}
33+
if isinstance(span.data, dict):
34+
span.data["__source__"] = "agentex"
35+
if env_vars.ACP_TYPE is not None:
36+
span.data["__acp_type__"] = env_vars.ACP_TYPE
37+
if env_vars.AGENT_NAME is not None:
38+
span.data["__agent_name__"] = env_vars.AGENT_NAME
39+
if env_vars.AGENT_ID is not None:
40+
span.data["__agent_id__"] = env_vars.AGENT_ID
41+
42+
43+
def _build_sgp_span(span: Span, env_vars: EnvironmentVariables) -> SGPSpan:
44+
"""Build an SGPSpan from an agentex Span. Idempotent on span_id at the SGP backend."""
45+
_add_source_to_span(span, env_vars)
46+
sgp_span = cast(
47+
SGPSpan,
48+
create_span(
49+
name=span.name,
50+
span_type=_get_span_type(span),
51+
span_id=span.id,
52+
parent_id=span.parent_id,
53+
trace_id=span.trace_id,
54+
input=span.input,
55+
output=span.output,
56+
metadata=span.data,
57+
),
58+
)
59+
sgp_span.start_time = span.start_time.isoformat() # type: ignore[union-attr]
60+
return sgp_span
61+
62+
3063
class SGPSyncTracingProcessor(SyncTracingProcessor):
3164
def __init__(self, config: SGPTracingProcessorConfig):
3265
disabled = config.sgp_api_key == "" or config.sgp_account_id == ""
@@ -40,45 +73,14 @@ def __init__(self, config: SGPTracingProcessorConfig):
4073
)
4174
self.env_vars = EnvironmentVariables.refresh()
4275

43-
def _add_source_to_span(self, span: Span) -> None:
44-
if span.data is None:
45-
span.data = {}
46-
if isinstance(span.data, dict):
47-
span.data["__source__"] = "agentex"
48-
if self.env_vars.ACP_TYPE is not None:
49-
span.data["__acp_type__"] = self.env_vars.ACP_TYPE
50-
if self.env_vars.AGENT_NAME is not None:
51-
span.data["__agent_name__"] = self.env_vars.AGENT_NAME
52-
if self.env_vars.AGENT_ID is not None:
53-
span.data["__agent_id__"] = self.env_vars.AGENT_ID
54-
55-
def _build_sgp_span(self, span: Span) -> SGPSpan:
56-
"""Build an SGPSpan from an agentex Span. Idempotent on span_id at the SGP backend."""
57-
self._add_source_to_span(span)
58-
sgp_span = cast(
59-
SGPSpan,
60-
create_span(
61-
name=span.name,
62-
span_type=_get_span_type(span),
63-
span_id=span.id,
64-
parent_id=span.parent_id,
65-
trace_id=span.trace_id,
66-
input=span.input,
67-
output=span.output,
68-
metadata=span.data,
69-
),
70-
)
71-
sgp_span.start_time = span.start_time.isoformat() # type: ignore[union-attr]
72-
return sgp_span
73-
7476
@override
7577
def on_span_start(self, span: Span) -> None:
76-
sgp_span = self._build_sgp_span(span)
78+
sgp_span = _build_sgp_span(span, self.env_vars)
7779
sgp_span.flush(blocking=False)
7880

7981
@override
8082
def on_span_end(self, span: Span) -> None:
81-
sgp_span = self._build_sgp_span(span)
83+
sgp_span = _build_sgp_span(span, self.env_vars)
8284
sgp_span.end_time = span.end_time.isoformat() # type: ignore[union-attr]
8385
sgp_span.flush(blocking=False)
8486

@@ -108,37 +110,6 @@ def __init__(self, config: SGPTracingProcessorConfig):
108110
)
109111
self.env_vars = EnvironmentVariables.refresh()
110112

111-
def _add_source_to_span(self, span: Span) -> None:
112-
if span.data is None:
113-
span.data = {}
114-
if isinstance(span.data, dict):
115-
span.data["__source__"] = "agentex"
116-
if self.env_vars.ACP_TYPE is not None:
117-
span.data["__acp_type__"] = self.env_vars.ACP_TYPE
118-
if self.env_vars.AGENT_NAME is not None:
119-
span.data["__agent_name__"] = self.env_vars.AGENT_NAME
120-
if self.env_vars.AGENT_ID is not None:
121-
span.data["__agent_id__"] = self.env_vars.AGENT_ID
122-
123-
def _build_sgp_span(self, span: Span) -> SGPSpan:
124-
"""Build an SGPSpan from an agentex Span. Idempotent on span_id at the SGP backend."""
125-
self._add_source_to_span(span)
126-
sgp_span = cast(
127-
SGPSpan,
128-
create_span(
129-
name=span.name,
130-
span_type=_get_span_type(span),
131-
span_id=span.id,
132-
parent_id=span.parent_id,
133-
trace_id=span.trace_id,
134-
input=span.input,
135-
output=span.output,
136-
metadata=span.data,
137-
),
138-
)
139-
sgp_span.start_time = span.start_time.isoformat() # type: ignore[union-attr]
140-
return sgp_span
141-
142113
@override
143114
async def on_span_start(self, span: Span) -> None:
144115
await self.on_spans_start([span])
@@ -152,7 +123,7 @@ async def on_spans_start(self, spans: list[Span]) -> None:
152123
if not spans:
153124
return
154125

155-
sgp_spans = [self._build_sgp_span(span) for span in spans]
126+
sgp_spans = [_build_sgp_span(span, self.env_vars) for span in spans]
156127

157128
if self.disabled:
158129
logger.warning("SGP is disabled, skipping span upsert")
@@ -168,7 +139,7 @@ async def on_spans_end(self, spans: list[Span]) -> None:
168139

169140
sgp_spans: list[SGPSpan] = []
170141
for span in spans:
171-
sgp_span = self._build_sgp_span(span)
142+
sgp_span = _build_sgp_span(span, self.env_vars)
172143
sgp_span.end_time = span.end_time.isoformat() # type: ignore[union-attr]
173144
sgp_spans.append(sgp_span)
174145

tests/lib/core/tracing/processors/test_sgp_tracing_processor.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ def capture_create_span(**kwargs):
180180
captured.append(sgp_span)
181181
return sgp_span
182182

183-
with patch(f"{MODULE}.create_span", side_effect=capture_create_span):
183+
mock_create_span = MagicMock(side_effect=capture_create_span)
184+
with patch(f"{MODULE}.create_span", mock_create_span):
184185
span = _make_span()
185186
span.input = {"messages": [{"role": "user", "content": "hello"}]}
186187
await processor.on_span_start(span)
@@ -199,6 +200,10 @@ def capture_create_span(**kwargs):
199200
# The end-time SGPSpan should have end_time populated.
200201
end_span = captured[-1]
201202
assert end_span.end_time is not None
203+
# Verify the updated input/output reached create_span on the end call.
204+
end_call_kwargs = mock_create_span.call_args_list[-1].kwargs
205+
assert end_call_kwargs["input"]["messages"][-1]["role"] == "assistant"
206+
assert end_call_kwargs["output"] == {"response": "hi"}
202207

203208
async def test_on_spans_start_sends_single_upsert_for_batch(self):
204209
"""Given N spans at once, on_spans_start should make ONE upsert_batch HTTP call."""

0 commit comments

Comments
 (0)