From 9c09f9c56036ab43e120d311cf70087a853f4e38 Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Tue, 9 Jun 2026 20:54:56 +0530 Subject: [PATCH 01/14] Add fallback log enrichment for Foundry agent metadata Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure/ai/agentserver/core/_tracing.py | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py index f9ab0ff60750..47f799605eaa 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py @@ -182,7 +182,15 @@ def _configure_tracing(connection_string: Optional[str] = None, enable_sensitive agent_tenant_id=agent_tenant_id, ), ] - log_record_processors = [_BaggageLogRecordProcessor()] # type: ignore[list-item] + session_id = os.environ.get(_config._ENV_FOUNDRY_AGENT_SESSION_ID, "") or None # pylint: disable=protected-access + log_record_processors = [ # type: ignore[list-item] + _BaggageLogRecordProcessor(), + _FoundryEnrichmentLogRecordProcessor( + agent_name=agent_name, + agent_version=agent_version, + session_id=session_id, + ), + ] try: _setup_distro_export( @@ -543,6 +551,53 @@ def force_flush(self, timeout_millis: int = 30000) -> bool: # pylint: disable=u return True +class _FoundryEnrichmentLogRecordProcessor: + """Populate stable Foundry dimensions on log records. + + This ensures key metadata (agent name/version/session) is present even for + logs emitted when no active span is available (for example, post-response + access logs where operation_Id can be zeroed). + """ + + def __init__( + self, + *, + agent_name: Optional[str] = None, + agent_version: Optional[str] = None, + session_id: Optional[str] = None, + ) -> None: + self.agent_name = agent_name + self.agent_version = agent_version + self.session_id = session_id + + def on_emit(self, log_data: Any) -> None: # pylint: disable=unused-argument + try: + if not hasattr(log_data, "log_record") or not log_data.log_record: + return + + attrs = log_data.log_record.attributes # type: ignore[assignment] + + if self.agent_name and _ATTR_GEN_AI_AGENT_NAME not in attrs: + attrs[_ATTR_GEN_AI_AGENT_NAME] = self.agent_name + if self.agent_version and _ATTR_GEN_AI_AGENT_VERSION not in attrs: + attrs[_ATTR_GEN_AI_AGENT_VERSION] = self.agent_version + + # Prefer request-scoped baggage session ID; fallback to platform env. + ctx = _otel_context.get_current() + bag_session = _otel_baggage.get_baggage(_BAGGAGE_SESSION_ID, context=ctx) + resolved_session = bag_session or self.session_id + if resolved_session and _ATTR_SESSION_ID not in attrs: + attrs[_ATTR_SESSION_ID] = resolved_session + except Exception: # pylint: disable=broad-except + pass + + def shutdown(self) -> None: + pass + + def force_flush(self, timeout_millis: int = 30000) -> bool: # pylint: disable=unused-argument + return True + + # ====================================================================== # Internal: resource, provider, exporters # ====================================================================== From 4afcb78b98ef054149d9f0991ca3ff831db2a11d Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Tue, 9 Jun 2026 21:08:29 +0530 Subject: [PATCH 02/14] Preserve request context for access-log enrichment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure/ai/agentserver/core/_tracing.py | 19 ++++++++++-------- .../ai/agentserver/invocations/_invocation.py | 20 +++++++------------ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py index 47f799605eaa..5916f5164aab 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py @@ -302,14 +302,11 @@ async def __call__(self, scope: Any, receive: Any, send: Any) -> None: "x_request_id", x_request_id, context=ctx, ) - token = _otel_context.attach(ctx) - try: - await self.app(scope, receive, send) - finally: - try: - _otel_context.detach(token) - except ValueError: - pass + # Attach request context and intentionally do not detach here. + # Hypercorn emits access logs after the ASGI app returns but within + # the same request task; detaching early causes zeroed operation_Id. + _otel_context.attach(ctx) + await self.app(scope, receive, send) def end_span(span: Any, exc: Optional[BaseException] = None) -> None: @@ -579,8 +576,12 @@ def on_emit(self, log_data: Any) -> None: # pylint: disable=unused-argument if self.agent_name and _ATTR_GEN_AI_AGENT_NAME not in attrs: attrs[_ATTR_GEN_AI_AGENT_NAME] = self.agent_name + if self.agent_name and "agent_name" not in attrs: + attrs["agent_name"] = self.agent_name if self.agent_version and _ATTR_GEN_AI_AGENT_VERSION not in attrs: attrs[_ATTR_GEN_AI_AGENT_VERSION] = self.agent_version + if self.agent_version and "agent_version" not in attrs: + attrs["agent_version"] = self.agent_version # Prefer request-scoped baggage session ID; fallback to platform env. ctx = _otel_context.get_current() @@ -588,6 +589,8 @@ def on_emit(self, log_data: Any) -> None: # pylint: disable=unused-argument resolved_session = bag_session or self.session_id if resolved_session and _ATTR_SESSION_ID not in attrs: attrs[_ATTR_SESSION_ID] = resolved_session + if resolved_session and "agent_session_id" not in attrs: + attrs["agent_session_id"] = resolved_session except Exception: # pylint: disable=broad-except pass diff --git a/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py b/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py index a096047f0b66..58aeb9db6eba 100644 --- a/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py +++ b/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py @@ -386,12 +386,12 @@ async def _create_invocation_endpoint(self, request: Request) -> Response: ctx = _otel_baggage.set_baggage( "azure.ai.agentserver.session_id", session_id, context=ctx, ) - baggage_token = _otel_context.attach(ctx) + _otel_context.attach(ctx) # Set structured logging context (concurrency-safe via contextvars) _ensure_log_filter() - inv_token = _invocation_id_var.set(invocation_id) - session_token = _session_id_var.set(session_id) + _invocation_id_var.set(invocation_id) + _session_id_var.set(session_id) try: response = await self._dispatch_invoke(request) response.headers[InvocationConstants.INVOCATION_ID_HEADER] = invocation_id @@ -427,12 +427,7 @@ async def _create_invocation_endpoint(self, request: Request) -> Response: ), ) finally: - _invocation_id_var.reset(inv_token) - _session_id_var.reset(session_token) - try: - _otel_context.detach(baggage_token) - except ValueError: - pass + pass return response @@ -450,8 +445,8 @@ async def _traced_invocation_endpoint( session_id = _sanitize_id(raw_session_id, "") if raw_session_id else "" _ensure_log_filter() - inv_token = _invocation_id_var.set(invocation_id) - session_token = _session_id_var.set(session_id) + _invocation_id_var.set(invocation_id) + _session_id_var.set(session_id) try: response = await dispatch(request) response.headers[InvocationConstants.INVOCATION_ID_HEADER] = invocation_id @@ -470,8 +465,7 @@ async def _traced_invocation_endpoint( ), ) finally: - _invocation_id_var.reset(inv_token) - _session_id_var.reset(session_token) + pass async def _get_invocation_endpoint(self, request: Request) -> Response: return await self._traced_invocation_endpoint( From c851f3c70c882131e3aaa0e0ed51100a1fdeb905 Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Wed, 10 Jun 2026 18:34:29 +0530 Subject: [PATCH 03/14] Add UT for deferred trace context lifecycle Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/test_tracing.py | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py index 600b587afab6..7b32b908f484 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py @@ -2,9 +2,11 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # --------------------------------------------------------- """Tests for tracing configuration — not invocation spans (those live in the invocations package).""" +import asyncio import os from unittest import mock +import pytest from opentelemetry import baggage as _otel_baggage, context as _otel_context from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import SimpleSpanProcessor, SpanExporter, SpanExportResult @@ -16,7 +18,7 @@ resolve_agent_version, resolve_appinsights_connection_string, ) -from azure.ai.agentserver.core._tracing import _FoundryEnrichmentSpanProcessor +from azure.ai.agentserver.core._tracing import _FoundryEnrichmentSpanProcessor, TraceContextMiddleware class _CollectorExporter(SpanExporter): @@ -431,4 +433,50 @@ def test_agent_version_default_empty(self) -> None: assert resolve_agent_version() == "" +class TestTraceContextMiddleware: + @pytest.mark.asyncio + async def test_preserves_context_for_post_response_work(self) -> None: + """Context remains attached after app returns so post-response logs keep trace correlation.""" + incoming_trace_id = "0af7651916cd43dd8448eb211c80319c" + traceparent = f"00-{incoming_trace_id}-b7ad6b7169203331-01" + + collector = _CollectorExporter() + provider = TracerProvider(resource=Resource.create({})) + provider.add_span_processor(SimpleSpanProcessor(collector)) + tracer = provider.get_tracer("middleware-test") + + async def app(scope, receive, send): + await send({"type": "http.response.start", "status": 200, "headers": []}) + await send({"type": "http.response.body", "body": b"", "more_body": False}) + + middleware = TraceContextMiddleware(app) + scope = { + "type": "http", + "headers": [ + (b"traceparent", traceparent.encode("ascii")), + (b"x-request-id", b"req-123"), + ], + } + + async def receive(): + return {"type": "http.request", "body": b"", "more_body": False} + + async def send(_message): + return None + + async def run_request_and_post_response_span(): + await middleware(scope, receive, send) + with tracer.start_as_current_span("post-response"): + pass + ctx = _otel_context.get_current() + return _otel_baggage.get_baggage("x_request_id", context=ctx) + + x_request_id = await asyncio.create_task(run_request_and_post_response_span()) + + assert len(collector.spans) == 1 + span = collector.spans[0] + assert span.name == "post-response" + assert format(span.context.trace_id, "032x") == incoming_trace_id + assert x_request_id == "req-123" + From 9b6d60f1d652765def5c994281840a5f193681bb Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Wed, 10 Jun 2026 18:36:45 +0530 Subject: [PATCH 04/14] Revert "Add UT for deferred trace context lifecycle" This reverts commit c851f3c70c882131e3aaa0e0ed51100a1fdeb905. --- .../tests/test_tracing.py | 50 +------------------ 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py index 7b32b908f484..600b587afab6 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py @@ -2,11 +2,9 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # --------------------------------------------------------- """Tests for tracing configuration — not invocation spans (those live in the invocations package).""" -import asyncio import os from unittest import mock -import pytest from opentelemetry import baggage as _otel_baggage, context as _otel_context from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import SimpleSpanProcessor, SpanExporter, SpanExportResult @@ -18,7 +16,7 @@ resolve_agent_version, resolve_appinsights_connection_string, ) -from azure.ai.agentserver.core._tracing import _FoundryEnrichmentSpanProcessor, TraceContextMiddleware +from azure.ai.agentserver.core._tracing import _FoundryEnrichmentSpanProcessor class _CollectorExporter(SpanExporter): @@ -433,50 +431,4 @@ def test_agent_version_default_empty(self) -> None: assert resolve_agent_version() == "" -class TestTraceContextMiddleware: - @pytest.mark.asyncio - async def test_preserves_context_for_post_response_work(self) -> None: - """Context remains attached after app returns so post-response logs keep trace correlation.""" - incoming_trace_id = "0af7651916cd43dd8448eb211c80319c" - traceparent = f"00-{incoming_trace_id}-b7ad6b7169203331-01" - - collector = _CollectorExporter() - provider = TracerProvider(resource=Resource.create({})) - provider.add_span_processor(SimpleSpanProcessor(collector)) - tracer = provider.get_tracer("middleware-test") - - async def app(scope, receive, send): - await send({"type": "http.response.start", "status": 200, "headers": []}) - await send({"type": "http.response.body", "body": b"", "more_body": False}) - - middleware = TraceContextMiddleware(app) - scope = { - "type": "http", - "headers": [ - (b"traceparent", traceparent.encode("ascii")), - (b"x-request-id", b"req-123"), - ], - } - - async def receive(): - return {"type": "http.request", "body": b"", "more_body": False} - - async def send(_message): - return None - - async def run_request_and_post_response_span(): - await middleware(scope, receive, send) - with tracer.start_as_current_span("post-response"): - pass - ctx = _otel_context.get_current() - return _otel_baggage.get_baggage("x_request_id", context=ctx) - - x_request_id = await asyncio.create_task(run_request_and_post_response_span()) - - assert len(collector.spans) == 1 - span = collector.spans[0] - assert span.name == "post-response" - assert format(span.context.trace_id, "032x") == incoming_trace_id - assert x_request_id == "req-123" - From 9170b5faa8a71f5a72bfea7c79ac34b8819207d2 Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Wed, 10 Jun 2026 20:14:45 +0530 Subject: [PATCH 05/14] Remove empty finally blocks and add tracing log enrichment tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/test_tracing.py | 80 ++++++++++++++++++- .../ai/agentserver/invocations/_invocation.py | 6 -- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py index 600b587afab6..fc959f553107 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py @@ -16,7 +16,10 @@ resolve_agent_version, resolve_appinsights_connection_string, ) -from azure.ai.agentserver.core._tracing import _FoundryEnrichmentSpanProcessor +from azure.ai.agentserver.core._tracing import ( + _FoundryEnrichmentLogRecordProcessor, + _FoundryEnrichmentSpanProcessor, +) class _CollectorExporter(SpanExporter): @@ -431,4 +434,79 @@ def test_agent_version_default_empty(self) -> None: assert resolve_agent_version() == "" +class _FakeLogRecord: + def __init__(self, attributes): + self.attributes = attributes + + +class _FakeLogData: + def __init__(self, attributes): + self.log_record = _FakeLogRecord(attributes) + + +class TestFoundryEnrichmentLogRecordProcessor: + def test_adds_agent_and_fallback_session_attributes(self) -> None: + proc = _FoundryEnrichmentLogRecordProcessor( + agent_name="agent-a", + agent_version="1.2.3", + session_id="sess-fallback-1", + ) + log_data = _FakeLogData({}) + + proc.on_emit(log_data) + + attrs = log_data.log_record.attributes + assert attrs["gen_ai.agent.name"] == "agent-a" + assert attrs["agent_name"] == "agent-a" + assert attrs["gen_ai.agent.version"] == "1.2.3" + assert attrs["agent_version"] == "1.2.3" + assert attrs["microsoft.session.id"] == "sess-fallback-1" + assert attrs["agent_session_id"] == "sess-fallback-1" + + def test_prefers_baggage_session_id_over_fallback(self) -> None: + proc = _FoundryEnrichmentLogRecordProcessor( + agent_name="agent-a", + agent_version="1.2.3", + session_id="sess-fallback-1", + ) + log_data = _FakeLogData({}) + + ctx = _otel_baggage.set_baggage( + "azure.ai.agentserver.session_id", "sess-from-baggage", + ) + token = _otel_context.attach(ctx) + try: + proc.on_emit(log_data) + finally: + _otel_context.detach(token) + + attrs = log_data.log_record.attributes + assert attrs["microsoft.session.id"] == "sess-from-baggage" + assert attrs["agent_session_id"] == "sess-from-baggage" + + def test_does_not_overwrite_existing_log_attributes(self) -> None: + proc = _FoundryEnrichmentLogRecordProcessor( + agent_name="agent-a", + agent_version="1.2.3", + session_id="sess-fallback-1", + ) + attrs = { + "gen_ai.agent.name": "existing-name", + "agent_name": "existing-name", + "gen_ai.agent.version": "0.0.1", + "agent_version": "0.0.1", + "microsoft.session.id": "existing-session", + "agent_session_id": "existing-session", + } + log_data = _FakeLogData(attrs) + + proc.on_emit(log_data) + + assert attrs["gen_ai.agent.name"] == "existing-name" + assert attrs["agent_name"] == "existing-name" + assert attrs["gen_ai.agent.version"] == "0.0.1" + assert attrs["agent_version"] == "0.0.1" + assert attrs["microsoft.session.id"] == "existing-session" + assert attrs["agent_session_id"] == "existing-session" + diff --git a/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py b/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py index 58aeb9db6eba..e00dfd72de46 100644 --- a/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py +++ b/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py @@ -426,9 +426,6 @@ async def _create_invocation_endpoint(self, request: Request) -> Response: error_detail, ), ) - finally: - pass - return response async def _traced_invocation_endpoint( @@ -464,9 +461,6 @@ async def _traced_invocation_endpoint( error_detail, ), ) - finally: - pass - async def _get_invocation_endpoint(self, request: Request) -> Response: return await self._traced_invocation_endpoint( request, "get_invocation", self._dispatch_get_invocation From f856308144472e003ab7e1328748b9db08b918b6 Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Wed, 10 Jun 2026 20:40:38 +0530 Subject: [PATCH 06/14] Restore request context and log-context cleanup Reintroduce request-scoped context detach/reset in tracing middleware and invocation handlers to avoid context leakage across requests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure/ai/agentserver/core/_tracing.py | 13 +++++++----- .../ai/agentserver/invocations/_invocation.py | 21 ++++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py index 5916f5164aab..7ff77f45a14e 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py @@ -302,11 +302,14 @@ async def __call__(self, scope: Any, receive: Any, send: Any) -> None: "x_request_id", x_request_id, context=ctx, ) - # Attach request context and intentionally do not detach here. - # Hypercorn emits access logs after the ASGI app returns but within - # the same request task; detaching early causes zeroed operation_Id. - _otel_context.attach(ctx) - await self.app(scope, receive, send) + token = _otel_context.attach(ctx) + try: + await self.app(scope, receive, send) + finally: + try: + _otel_context.detach(token) + except ValueError: + pass def end_span(span: Any, exc: Optional[BaseException] = None) -> None: diff --git a/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py b/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py index e00dfd72de46..9b376c70f9e3 100644 --- a/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py +++ b/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py @@ -386,12 +386,12 @@ async def _create_invocation_endpoint(self, request: Request) -> Response: ctx = _otel_baggage.set_baggage( "azure.ai.agentserver.session_id", session_id, context=ctx, ) - _otel_context.attach(ctx) + baggage_token = _otel_context.attach(ctx) # Set structured logging context (concurrency-safe via contextvars) _ensure_log_filter() - _invocation_id_var.set(invocation_id) - _session_id_var.set(session_id) + inv_token = _invocation_id_var.set(invocation_id) + session_token = _session_id_var.set(session_id) try: response = await self._dispatch_invoke(request) response.headers[InvocationConstants.INVOCATION_ID_HEADER] = invocation_id @@ -426,6 +426,13 @@ async def _create_invocation_endpoint(self, request: Request) -> Response: error_detail, ), ) + finally: + _invocation_id_var.reset(inv_token) + _session_id_var.reset(session_token) + try: + _otel_context.detach(baggage_token) + except ValueError: + pass return response async def _traced_invocation_endpoint( @@ -442,8 +449,8 @@ async def _traced_invocation_endpoint( session_id = _sanitize_id(raw_session_id, "") if raw_session_id else "" _ensure_log_filter() - _invocation_id_var.set(invocation_id) - _session_id_var.set(session_id) + inv_token = _invocation_id_var.set(invocation_id) + session_token = _session_id_var.set(session_id) try: response = await dispatch(request) response.headers[InvocationConstants.INVOCATION_ID_HEADER] = invocation_id @@ -461,6 +468,10 @@ async def _traced_invocation_endpoint( error_detail, ), ) + finally: + _invocation_id_var.reset(inv_token) + _session_id_var.reset(session_token) + async def _get_invocation_endpoint(self, request: Request) -> Response: return await self._traced_invocation_endpoint( request, "get_invocation", self._dispatch_get_invocation From cd352bea295d7f7e82c4da8f786c6ee0fb741374 Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Thu, 11 Jun 2026 09:17:01 +0530 Subject: [PATCH 07/14] Fix cspell errors in tracing tests Rename test session string literals from sess-* to session-* to satisfy cspell in Analyze gate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/test_tracing.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py index fc959f553107..faad87737556 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py @@ -449,7 +449,7 @@ def test_adds_agent_and_fallback_session_attributes(self) -> None: proc = _FoundryEnrichmentLogRecordProcessor( agent_name="agent-a", agent_version="1.2.3", - session_id="sess-fallback-1", + session_id="session-fallback-1", ) log_data = _FakeLogData({}) @@ -460,19 +460,19 @@ def test_adds_agent_and_fallback_session_attributes(self) -> None: assert attrs["agent_name"] == "agent-a" assert attrs["gen_ai.agent.version"] == "1.2.3" assert attrs["agent_version"] == "1.2.3" - assert attrs["microsoft.session.id"] == "sess-fallback-1" - assert attrs["agent_session_id"] == "sess-fallback-1" + assert attrs["microsoft.session.id"] == "session-fallback-1" + assert attrs["agent_session_id"] == "session-fallback-1" def test_prefers_baggage_session_id_over_fallback(self) -> None: proc = _FoundryEnrichmentLogRecordProcessor( agent_name="agent-a", agent_version="1.2.3", - session_id="sess-fallback-1", + session_id="session-fallback-1", ) log_data = _FakeLogData({}) ctx = _otel_baggage.set_baggage( - "azure.ai.agentserver.session_id", "sess-from-baggage", + "azure.ai.agentserver.session_id", "session-from-baggage", ) token = _otel_context.attach(ctx) try: @@ -481,14 +481,14 @@ def test_prefers_baggage_session_id_over_fallback(self) -> None: _otel_context.detach(token) attrs = log_data.log_record.attributes - assert attrs["microsoft.session.id"] == "sess-from-baggage" - assert attrs["agent_session_id"] == "sess-from-baggage" + assert attrs["microsoft.session.id"] == "session-from-baggage" + assert attrs["agent_session_id"] == "session-from-baggage" def test_does_not_overwrite_existing_log_attributes(self) -> None: proc = _FoundryEnrichmentLogRecordProcessor( agent_name="agent-a", agent_version="1.2.3", - session_id="sess-fallback-1", + session_id="session-fallback-1", ) attrs = { "gen_ai.agent.name": "existing-name", @@ -509,4 +509,3 @@ def test_does_not_overwrite_existing_log_attributes(self) -> None: assert attrs["microsoft.session.id"] == "existing-session" assert attrs["agent_session_id"] == "existing-session" - From a94e0808c6d4d2443cefe6cb75367b230d544765 Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Thu, 11 Jun 2026 20:51:23 +0530 Subject: [PATCH 08/14] Re-add blank line after baggage detach Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure/ai/agentserver/invocations/_invocation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py b/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py index 9b376c70f9e3..a096047f0b66 100644 --- a/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py +++ b/sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py @@ -433,6 +433,7 @@ async def _create_invocation_endpoint(self, request: Request) -> Response: _otel_context.detach(baggage_token) except ValueError: pass + return response async def _traced_invocation_endpoint( From 8b5409c11f0421ed351f3c51c9f4cd9fca7c29d4 Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Thu, 11 Jun 2026 22:10:54 +0530 Subject: [PATCH 09/14] Reuse baggage log processor for Foundry enrichment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure/ai/agentserver/core/_base.py | 1 + .../azure/ai/agentserver/core/_tracing.py | 68 ++++++++----------- .../tests/test_tracing.py | 13 ++-- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py index 84a7ccd06c24..5fdb81d96ef9 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py @@ -196,6 +196,7 @@ def __init__( connection_string=_conn_str, log_level=log_level, enable_sensitive_data=_sensitive_data, + session_id=self.config.session_id or None, ) except ValueError: raise # invalid log_level etc. — user should fix their config diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py index 7ff77f45a14e..d78bc7d7e3c3 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py @@ -97,6 +97,7 @@ def configure_observability( connection_string: Optional[str] = None, log_level: Optional[str] = None, enable_sensitive_data: bool = False, + session_id: Optional[str] = None, ) -> None: """Default observability setup: console logging + tracing/OTel export. @@ -117,6 +118,9 @@ def configure_observability( (prompts, tool arguments, results) for Agent Framework SDK instrumentation. Defaults to False. :paramtype enable_sensitive_data: bool + :keyword session_id: Optional session identifier sourced from + :class:`~azure.ai.agentserver.core._config.AgentConfig`. + :paramtype session_id: str or None """ # Console logging on the root logger so user logs are also visible. resolved_level = _config.resolve_log_level(log_level) @@ -146,10 +150,18 @@ def configure_observability( logging.getLogger(_noisy).setLevel(logging.WARNING) # Tracing and OTel export - _configure_tracing(connection_string=connection_string, enable_sensitive_data=enable_sensitive_data) + _configure_tracing( + connection_string=connection_string, + enable_sensitive_data=enable_sensitive_data, + session_id=session_id, + ) -def _configure_tracing(connection_string: Optional[str] = None, enable_sensitive_data: bool = False) -> None: +def _configure_tracing( + connection_string: Optional[str] = None, + enable_sensitive_data: bool = False, + session_id: Optional[str] = None, +) -> None: """Configure OpenTelemetry exporters via the microsoft-opentelemetry distro. Internal helper called by :func:`configure_observability`. @@ -160,6 +172,8 @@ def _configure_tracing(connection_string: Optional[str] = None, enable_sensitive :param enable_sensitive_data: Enable sensitive data recording for Agent Framework SDK instrumentation. :type enable_sensitive_data: bool + :param session_id: Session identifier to enrich log records. + :type session_id: str or None """ resource = _create_resource() if resource is None: @@ -182,13 +196,12 @@ def _configure_tracing(connection_string: Optional[str] = None, enable_sensitive agent_tenant_id=agent_tenant_id, ), ] - session_id = os.environ.get(_config._ENV_FOUNDRY_AGENT_SESSION_ID, "") or None # pylint: disable=protected-access + resolved_session_id = session_id or None log_record_processors = [ # type: ignore[list-item] - _BaggageLogRecordProcessor(), - _FoundryEnrichmentLogRecordProcessor( + _BaggageLogRecordProcessor( agent_name=agent_name, agent_version=agent_version, - session_id=session_id, + session_id=resolved_session_id, ), ] @@ -529,36 +542,6 @@ class _BaggageLogRecordProcessor: for end-to-end correlation. """ - def on_emit(self, log_data: Any) -> None: # pylint: disable=unused-argument - """Copy baggage entries into the log record's attributes. - - :param log_data: The log data being emitted. - :type log_data: any - """ - try: - ctx = _otel_context.get_current() - entries = _otel_baggage.get_all(context=ctx) - if entries and hasattr(log_data, 'log_record') and log_data.log_record: - for key, value in entries.items(): - log_data.log_record.attributes[key] = value # type: ignore[index] - except Exception: # pylint: disable=broad-except - pass - - def shutdown(self) -> None: - pass - - def force_flush(self, timeout_millis: int = 30000) -> bool: # pylint: disable=unused-argument - return True - - -class _FoundryEnrichmentLogRecordProcessor: - """Populate stable Foundry dimensions on log records. - - This ensures key metadata (agent name/version/session) is present even for - logs emitted when no active span is available (for example, post-response - access logs where operation_Id can be zeroed). - """ - def __init__( self, *, @@ -571,12 +554,23 @@ def __init__( self.session_id = session_id def on_emit(self, log_data: Any) -> None: # pylint: disable=unused-argument + """Copy baggage entries into the log record's attributes. + + :param log_data: The log data being emitted. + :type log_data: any + """ try: if not hasattr(log_data, "log_record") or not log_data.log_record: return attrs = log_data.log_record.attributes # type: ignore[assignment] + ctx = _otel_context.get_current() + entries = _otel_baggage.get_all(context=ctx) + if entries: + for key, value in entries.items(): + attrs[key] = value # type: ignore[index] + if self.agent_name and _ATTR_GEN_AI_AGENT_NAME not in attrs: attrs[_ATTR_GEN_AI_AGENT_NAME] = self.agent_name if self.agent_name and "agent_name" not in attrs: @@ -586,8 +580,6 @@ def on_emit(self, log_data: Any) -> None: # pylint: disable=unused-argument if self.agent_version and "agent_version" not in attrs: attrs["agent_version"] = self.agent_version - # Prefer request-scoped baggage session ID; fallback to platform env. - ctx = _otel_context.get_current() bag_session = _otel_baggage.get_baggage(_BAGGAGE_SESSION_ID, context=ctx) resolved_session = bag_session or self.session_id if resolved_session and _ATTR_SESSION_ID not in attrs: diff --git a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py index faad87737556..17ad2e7efb17 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py @@ -17,7 +17,7 @@ resolve_appinsights_connection_string, ) from azure.ai.agentserver.core._tracing import ( - _FoundryEnrichmentLogRecordProcessor, + _BaggageLogRecordProcessor, _FoundryEnrichmentSpanProcessor, ) @@ -78,6 +78,7 @@ def test_observability_receives_constructor_connection_string(self) -> None: connection_string="InstrumentationKey=ctor", log_level=None, enable_sensitive_data=True, + session_id=None, ) def test_observability_disabled_when_none(self) -> None: @@ -165,6 +166,7 @@ def test_constructor_passes_connection_string(self) -> None: connection_string="InstrumentationKey=ctor", log_level=None, enable_sensitive_data=True, + session_id=None, ) @@ -444,9 +446,9 @@ def __init__(self, attributes): self.log_record = _FakeLogRecord(attributes) -class TestFoundryEnrichmentLogRecordProcessor: +class TestBaggageLogRecordProcessor: def test_adds_agent_and_fallback_session_attributes(self) -> None: - proc = _FoundryEnrichmentLogRecordProcessor( + proc = _BaggageLogRecordProcessor( agent_name="agent-a", agent_version="1.2.3", session_id="session-fallback-1", @@ -464,7 +466,7 @@ def test_adds_agent_and_fallback_session_attributes(self) -> None: assert attrs["agent_session_id"] == "session-fallback-1" def test_prefers_baggage_session_id_over_fallback(self) -> None: - proc = _FoundryEnrichmentLogRecordProcessor( + proc = _BaggageLogRecordProcessor( agent_name="agent-a", agent_version="1.2.3", session_id="session-fallback-1", @@ -485,7 +487,7 @@ def test_prefers_baggage_session_id_over_fallback(self) -> None: assert attrs["agent_session_id"] == "session-from-baggage" def test_does_not_overwrite_existing_log_attributes(self) -> None: - proc = _FoundryEnrichmentLogRecordProcessor( + proc = _BaggageLogRecordProcessor( agent_name="agent-a", agent_version="1.2.3", session_id="session-fallback-1", @@ -508,4 +510,3 @@ def test_does_not_overwrite_existing_log_attributes(self) -> None: assert attrs["agent_version"] == "0.0.1" assert attrs["microsoft.session.id"] == "existing-session" assert attrs["agent_session_id"] == "existing-session" - From de63920fec551b5a6831fe7d0814687e3d7c35d9 Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Thu, 11 Jun 2026 22:17:55 +0530 Subject: [PATCH 10/14] Simplify session_id docstring wording Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure/ai/agentserver/core/_tracing.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py index d78bc7d7e3c3..7fc8812d6cb2 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py @@ -118,8 +118,7 @@ def configure_observability( (prompts, tool arguments, results) for Agent Framework SDK instrumentation. Defaults to False. :paramtype enable_sensitive_data: bool - :keyword session_id: Optional session identifier sourced from - :class:`~azure.ai.agentserver.core._config.AgentConfig`. + :keyword session_id: Optional session identifier sourced from AgentConfig. :paramtype session_id: str or None """ # Console logging on the root logger so user logs are also visible. From 8d00d8df75235a6945464a0d266d2d2de90f89eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 11 Jun 2026 20:11:05 +0000 Subject: [PATCH 11/14] Refactor tracing session id resolution Co-authored-by: singankit <30610298+singankit@users.noreply.github.com> --- .../azure/ai/agentserver/core/_base.py | 1 - .../azure/ai/agentserver/core/_config.py | 9 +++++++++ .../azure/ai/agentserver/core/_tracing.py | 10 +--------- .../tests/test_tracing.py | 15 ++++++++++++--- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py index 5fdb81d96ef9..84a7ccd06c24 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py @@ -196,7 +196,6 @@ def __init__( connection_string=_conn_str, log_level=log_level, enable_sensitive_data=_sensitive_data, - session_id=self.config.session_id or None, ) except ValueError: raise # invalid log_level etc. — user should fix their config diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py index f1d8dd3db86f..493f58794776 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py @@ -345,6 +345,15 @@ def resolve_project_id() -> str: return os.environ.get(_ENV_FOUNDRY_PROJECT_ARM_ID, "") +def resolve_session_id() -> str: + """Resolve the default session ID from the ``FOUNDRY_AGENT_SESSION_ID`` environment variable. + + :return: The default session ID, or an empty string if not set. + :rtype: str + """ + return os.environ.get(_ENV_FOUNDRY_AGENT_SESSION_ID, "") + + def resolve_sse_keepalive_interval(interval: Optional[int] = None) -> int: """Resolve the SSE keep-alive interval from argument, env var, or default. diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py index 7fc8812d6cb2..144839e722f6 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py @@ -97,7 +97,6 @@ def configure_observability( connection_string: Optional[str] = None, log_level: Optional[str] = None, enable_sensitive_data: bool = False, - session_id: Optional[str] = None, ) -> None: """Default observability setup: console logging + tracing/OTel export. @@ -118,8 +117,6 @@ def configure_observability( (prompts, tool arguments, results) for Agent Framework SDK instrumentation. Defaults to False. :paramtype enable_sensitive_data: bool - :keyword session_id: Optional session identifier sourced from AgentConfig. - :paramtype session_id: str or None """ # Console logging on the root logger so user logs are also visible. resolved_level = _config.resolve_log_level(log_level) @@ -152,14 +149,12 @@ def configure_observability( _configure_tracing( connection_string=connection_string, enable_sensitive_data=enable_sensitive_data, - session_id=session_id, ) def _configure_tracing( connection_string: Optional[str] = None, enable_sensitive_data: bool = False, - session_id: Optional[str] = None, ) -> None: """Configure OpenTelemetry exporters via the microsoft-opentelemetry distro. @@ -171,8 +166,6 @@ def _configure_tracing( :param enable_sensitive_data: Enable sensitive data recording for Agent Framework SDK instrumentation. :type enable_sensitive_data: bool - :param session_id: Session identifier to enrich log records. - :type session_id: str or None """ resource = _create_resource() if resource is None: @@ -195,12 +188,11 @@ def _configure_tracing( agent_tenant_id=agent_tenant_id, ), ] - resolved_session_id = session_id or None log_record_processors = [ # type: ignore[list-item] _BaggageLogRecordProcessor( agent_name=agent_name, agent_version=agent_version, - session_id=resolved_session_id, + session_id=_config.resolve_session_id() or None, ), ] diff --git a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py index 17ad2e7efb17..c7928dcb169b 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py @@ -15,6 +15,7 @@ resolve_agent_name, resolve_agent_version, resolve_appinsights_connection_string, + resolve_session_id, ) from azure.ai.agentserver.core._tracing import ( _BaggageLogRecordProcessor, @@ -78,7 +79,6 @@ def test_observability_receives_constructor_connection_string(self) -> None: connection_string="InstrumentationKey=ctor", log_level=None, enable_sensitive_data=True, - session_id=None, ) def test_observability_disabled_when_none(self) -> None: @@ -166,7 +166,6 @@ def test_constructor_passes_connection_string(self) -> None: connection_string="InstrumentationKey=ctor", log_level=None, enable_sensitive_data=True, - session_id=None, ) @@ -413,7 +412,7 @@ def test_invocation_id_propagates_to_child_spans(self) -> None: class TestAgentIdentityResolution: - """Tests for resolve_agent_name() and resolve_agent_version().""" + """Tests for agent identity/session resolution helpers.""" def test_agent_name_from_env(self) -> None: with mock.patch.dict(os.environ, {"FOUNDRY_AGENT_NAME": "my-agent"}): @@ -435,6 +434,16 @@ def test_agent_version_default_empty(self) -> None: with mock.patch.dict(os.environ, env, clear=True): assert resolve_agent_version() == "" + def test_session_id_from_env(self) -> None: + with mock.patch.dict(os.environ, {"FOUNDRY_AGENT_SESSION_ID": "session-1"}): + assert resolve_session_id() == "session-1" + + def test_session_id_default_empty(self) -> None: + env = os.environ.copy() + env.pop("FOUNDRY_AGENT_SESSION_ID", None) + with mock.patch.dict(os.environ, env, clear=True): + assert resolve_session_id() == "" + class _FakeLogRecord: def __init__(self, attributes): From 0ffa47f67def721729c6f15a38099dc922f2dcbb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 11 Jun 2026 20:20:03 +0000 Subject: [PATCH 12/14] Remove duplicate log attribute names Only set standardized gen_ai.* and microsoft.* prefixed attributes instead of both prefixed and simple names. Co-authored-by: singankit <30610298+singankit@users.noreply.github.com> --- .../azure/ai/agentserver/core/_tracing.py | 6 ------ .../azure-ai-agentserver-core/tests/test_tracing.py | 10 ---------- 2 files changed, 16 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py index 144839e722f6..b0ed26bbeda1 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py @@ -564,19 +564,13 @@ def on_emit(self, log_data: Any) -> None: # pylint: disable=unused-argument if self.agent_name and _ATTR_GEN_AI_AGENT_NAME not in attrs: attrs[_ATTR_GEN_AI_AGENT_NAME] = self.agent_name - if self.agent_name and "agent_name" not in attrs: - attrs["agent_name"] = self.agent_name if self.agent_version and _ATTR_GEN_AI_AGENT_VERSION not in attrs: attrs[_ATTR_GEN_AI_AGENT_VERSION] = self.agent_version - if self.agent_version and "agent_version" not in attrs: - attrs["agent_version"] = self.agent_version bag_session = _otel_baggage.get_baggage(_BAGGAGE_SESSION_ID, context=ctx) resolved_session = bag_session or self.session_id if resolved_session and _ATTR_SESSION_ID not in attrs: attrs[_ATTR_SESSION_ID] = resolved_session - if resolved_session and "agent_session_id" not in attrs: - attrs["agent_session_id"] = resolved_session except Exception: # pylint: disable=broad-except pass diff --git a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py index c7928dcb169b..bacf0f4d6dea 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py +++ b/sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py @@ -468,11 +468,8 @@ def test_adds_agent_and_fallback_session_attributes(self) -> None: attrs = log_data.log_record.attributes assert attrs["gen_ai.agent.name"] == "agent-a" - assert attrs["agent_name"] == "agent-a" assert attrs["gen_ai.agent.version"] == "1.2.3" - assert attrs["agent_version"] == "1.2.3" assert attrs["microsoft.session.id"] == "session-fallback-1" - assert attrs["agent_session_id"] == "session-fallback-1" def test_prefers_baggage_session_id_over_fallback(self) -> None: proc = _BaggageLogRecordProcessor( @@ -493,7 +490,6 @@ def test_prefers_baggage_session_id_over_fallback(self) -> None: attrs = log_data.log_record.attributes assert attrs["microsoft.session.id"] == "session-from-baggage" - assert attrs["agent_session_id"] == "session-from-baggage" def test_does_not_overwrite_existing_log_attributes(self) -> None: proc = _BaggageLogRecordProcessor( @@ -503,19 +499,13 @@ def test_does_not_overwrite_existing_log_attributes(self) -> None: ) attrs = { "gen_ai.agent.name": "existing-name", - "agent_name": "existing-name", "gen_ai.agent.version": "0.0.1", - "agent_version": "0.0.1", "microsoft.session.id": "existing-session", - "agent_session_id": "existing-session", } log_data = _FakeLogData(attrs) proc.on_emit(log_data) assert attrs["gen_ai.agent.name"] == "existing-name" - assert attrs["agent_name"] == "existing-name" assert attrs["gen_ai.agent.version"] == "0.0.1" - assert attrs["agent_version"] == "0.0.1" assert attrs["microsoft.session.id"] == "existing-session" - assert attrs["agent_session_id"] == "existing-session" From 848c4e1121135d8866fd8e0fcec1897eb4c7accc Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Fri, 12 Jun 2026 10:48:26 +0530 Subject: [PATCH 13/14] Add session id resolver helper Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure/ai/agentserver/core/_config.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py index 493f58794776..fbb6dca34083 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py @@ -293,6 +293,15 @@ def resolve_agent_version() -> str: return os.environ.get(_ENV_FOUNDRY_AGENT_VERSION, "") +def resolve_session_id() -> str: + """Resolve the session ID from the ``FOUNDRY_AGENT_SESSION_ID`` environment variable. + + :return: The session ID, or an empty string if not set. + :rtype: str + """ + return os.environ.get(_ENV_FOUNDRY_AGENT_SESSION_ID, "") + + def resolve_agent_id() -> str: """Resolve the agent ID. From 8cdd4e47fef0736537c1733272d86014407c3a5d Mon Sep 17 00:00:00 2001 From: Harsheet Shah Date: Fri, 12 Jun 2026 10:55:17 +0530 Subject: [PATCH 14/14] Remove duplicate session id resolver Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../azure/ai/agentserver/core/_config.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py index fbb6dca34083..493f58794776 100644 --- a/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py +++ b/sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_config.py @@ -293,15 +293,6 @@ def resolve_agent_version() -> str: return os.environ.get(_ENV_FOUNDRY_AGENT_VERSION, "") -def resolve_session_id() -> str: - """Resolve the session ID from the ``FOUNDRY_AGENT_SESSION_ID`` environment variable. - - :return: The session ID, or an empty string if not set. - :rtype: str - """ - return os.environ.get(_ENV_FOUNDRY_AGENT_SESSION_ID, "") - - def resolve_agent_id() -> str: """Resolve the agent ID.