Skip to content

Commit c7b2497

Browse files
CopilotnikhilNava
andauthored
Add parent_id support to ExecuteToolScope and InferenceScope (#177)
* Initial plan * Add parent_id parameter to ExecuteToolScope and InferenceScope for parent span linking Co-authored-by: nikhilNava <211831449+nikhilNava@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nikhilNava <211831449+nikhilNava@users.noreply.github.com>
1 parent 8c9ae53 commit c7b2497

File tree

4 files changed

+74
-2
lines changed

4 files changed

+74
-2
lines changed

libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/execute_tool_scope.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def start(
3030
agent_details: AgentDetails,
3131
tenant_details: TenantDetails,
3232
request: Request | None = None,
33+
parent_id: str | None = None,
3334
) -> "ExecuteToolScope":
3435
"""Creates and starts a new scope for tool execution tracing.
3536
@@ -38,18 +39,21 @@ def start(
3839
agent_details: The details of the agent making the call
3940
tenant_details: The details of the tenant
4041
request: Optional request details for additional context
42+
parent_id: Optional parent Activity ID used to link this span to an upstream
43+
operation
4144
4245
Returns:
4346
A new ExecuteToolScope instance
4447
"""
45-
return ExecuteToolScope(details, agent_details, tenant_details, request)
48+
return ExecuteToolScope(details, agent_details, tenant_details, request, parent_id)
4649

4750
def __init__(
4851
self,
4952
details: ToolCallDetails,
5053
agent_details: AgentDetails,
5154
tenant_details: TenantDetails,
5255
request: Request | None = None,
56+
parent_id: str | None = None,
5357
):
5458
"""Initialize the tool execution scope.
5559
@@ -58,13 +62,16 @@ def __init__(
5862
agent_details: The details of the agent making the call
5963
tenant_details: The details of the tenant
6064
request: Optional request details for additional context
65+
parent_id: Optional parent Activity ID used to link this span to an upstream
66+
operation
6167
"""
6268
super().__init__(
6369
kind="Internal",
6470
operation_name=EXECUTE_TOOL_OPERATION_NAME,
6571
activity_name=f"{EXECUTE_TOOL_OPERATION_NAME} {details.tool_name}",
6672
agent_details=agent_details,
6773
tenant_details=tenant_details,
74+
parent_id=parent_id,
6875
)
6976

7077
# Extract details using deconstruction-like approach

libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/inference_scope.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def start(
3434
agent_details: AgentDetails,
3535
tenant_details: TenantDetails,
3636
request: Request | None = None,
37+
parent_id: str | None = None,
3738
) -> "InferenceScope":
3839
"""Creates and starts a new scope for inference tracing.
3940
@@ -42,18 +43,21 @@ def start(
4243
agent_details: The details of the agent making the call
4344
tenant_details: The details of the tenant
4445
request: Optional request details for additional context
46+
parent_id: Optional parent Activity ID used to link this span to an upstream
47+
operation
4548
4649
Returns:
4750
A new InferenceScope instance
4851
"""
49-
return InferenceScope(details, agent_details, tenant_details, request)
52+
return InferenceScope(details, agent_details, tenant_details, request, parent_id)
5053

5154
def __init__(
5255
self,
5356
details: InferenceCallDetails,
5457
agent_details: AgentDetails,
5558
tenant_details: TenantDetails,
5659
request: Request | None = None,
60+
parent_id: str | None = None,
5761
):
5862
"""Initialize the inference scope.
5963
@@ -62,6 +66,8 @@ def __init__(
6266
agent_details: The details of the agent making the call
6367
tenant_details: The details of the tenant
6468
request: Optional request details for additional context
69+
parent_id: Optional parent Activity ID used to link this span to an upstream
70+
operation
6571
"""
6672

6773
super().__init__(
@@ -70,6 +76,7 @@ def __init__(
7076
activity_name=f"{details.operationName.value} {details.model}",
7177
agent_details=agent_details,
7278
tenant_details=tenant_details,
79+
parent_id=parent_id,
7380
)
7481

7582
if request:

tests/observability/core/test_execute_tool_scope.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,32 @@ def test_request_metadata_set_on_span(self):
131131
request.source_metadata.description,
132132
)
133133

134+
def test_execute_tool_scope_with_parent_id(self):
135+
"""Test ExecuteToolScope uses parent_id to link span to parent context."""
136+
parent_trace_id = "1234567890abcdef1234567890abcdef"
137+
parent_span_id = "abcdefabcdef1234"
138+
parent_id = f"00-{parent_trace_id}-{parent_span_id}-01"
139+
140+
with ExecuteToolScope.start(
141+
self.tool_details, self.agent_details, self.tenant_details, parent_id=parent_id
142+
):
143+
pass
144+
145+
finished_spans = self.span_exporter.get_finished_spans()
146+
self.assertTrue(finished_spans, "Expected at least one span to be created")
147+
148+
span = finished_spans[-1]
149+
150+
# Verify span inherits parent's trace_id
151+
span_trace_id = f"{span.context.trace_id:032x}"
152+
self.assertEqual(span_trace_id, parent_trace_id)
153+
154+
# Verify span's parent_span_id matches
155+
self.assertIsNotNone(span.parent, "Expected span to have a parent")
156+
self.assertTrue(hasattr(span.parent, "span_id"), "Expected parent to have span_id")
157+
span_parent_id = f"{span.parent.span_id:016x}"
158+
self.assertEqual(span_parent_id, parent_span_id)
159+
134160

135161
if __name__ == "__main__":
136162
# Run pytest only on the current file

tests/observability/core/test_inference_scope.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,38 @@ def test_record_thought_process(self):
340340
# Should not raise an exception
341341
self.assertTrue(hasattr(scope, "record_thought_process"))
342342

343+
def test_inference_scope_with_parent_id(self):
344+
"""Test InferenceScope uses parent_id to link span to parent context."""
345+
details = InferenceCallDetails(
346+
operationName=InferenceOperationType.CHAT,
347+
model="gpt-4",
348+
providerName="openai",
349+
)
350+
351+
parent_trace_id = "1234567890abcdef1234567890abcdef"
352+
parent_span_id = "abcdefabcdef1234"
353+
parent_id = f"00-{parent_trace_id}-{parent_span_id}-01"
354+
355+
with InferenceScope.start(
356+
details, self.agent_details, self.tenant_details, parent_id=parent_id
357+
):
358+
pass
359+
360+
finished_spans = self.span_exporter.get_finished_spans()
361+
self.assertTrue(finished_spans, "Expected at least one span to be created")
362+
363+
span = finished_spans[-1]
364+
365+
# Verify span inherits parent's trace_id
366+
span_trace_id = f"{span.context.trace_id:032x}"
367+
self.assertEqual(span_trace_id, parent_trace_id)
368+
369+
# Verify span's parent_span_id matches
370+
self.assertIsNotNone(span.parent, "Expected span to have a parent")
371+
self.assertTrue(hasattr(span.parent, "span_id"), "Expected parent to have span_id")
372+
span_parent_id = f"{span.parent.span_id:016x}"
373+
self.assertEqual(span_parent_id, parent_span_id)
374+
343375

344376
if __name__ == "__main__":
345377
# Run pytest only on the current file

0 commit comments

Comments
 (0)