Python: Add type annotations for AgentRunEvent and AgentRunUpdateEvent data attributes (#2589)

* Add type annotations for AgentRunEvent and AgentRunUpdateEvent data attributes

* Fix unnecessary cast after typing improvement

* Mcp pkg update introduced type change. Fix it.

* update opentelemetry deps to 1.39.0 and use LogRecordExporter for type compatibility
This commit is contained in:
Evan Mattson
2025-12-04 09:40:49 +09:00
committed by GitHub
Unverified
parent 9c2e800189
commit a597a925cb
7 changed files with 102 additions and 55 deletions
@@ -244,7 +244,7 @@ class WorkflowAgent(BaseAgent):
case AgentRunUpdateEvent(data=update):
# Direct pass-through of update in an agent streaming event
if update:
return cast(AgentRunResponseUpdate, update)
return update
return None
case RequestInfoEvent(request_id=request_id):
@@ -367,6 +367,8 @@ class ExecutorFailedEvent(ExecutorEvent):
class AgentRunUpdateEvent(ExecutorEvent):
"""Event triggered when an agent is streaming messages."""
data: AgentRunResponseUpdate | None
def __init__(self, executor_id: str, data: AgentRunResponseUpdate | None = None):
"""Initialize the agent streaming event."""
super().__init__(executor_id, data)
@@ -379,6 +381,8 @@ class AgentRunUpdateEvent(ExecutorEvent):
class AgentRunEvent(ExecutorEvent):
"""Event triggered when an agent run is completed."""
data: AgentRunResponse | None
def __init__(self, executor_id: str, data: AgentRunResponse | None = None):
"""Initialize the agent run event."""
super().__init__(executor_id, data)
@@ -20,7 +20,7 @@ from .exceptions import AgentInitializationError, ChatClientInitializationError
if TYPE_CHECKING: # pragma: no cover
from azure.core.credentials import TokenCredential
from opentelemetry.sdk._logs._internal.export import LogExporter
from opentelemetry.sdk._logs._internal.export import LogRecordExporter
from opentelemetry.sdk.metrics.export import MetricExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace.export import SpanExporter
@@ -259,13 +259,13 @@ FINISH_REASON_MAP = {
# region Telemetry utils
def _get_otlp_exporters(endpoints: list[str]) -> list["LogExporter | SpanExporter | MetricExporter"]:
def _get_otlp_exporters(endpoints: list[str]) -> list["LogRecordExporter | SpanExporter | MetricExporter"]:
"""Create standard OTLP Exporters for the supplied endpoints."""
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
exporters: list["LogExporter | SpanExporter | MetricExporter"] = []
exporters: list["LogRecordExporter | SpanExporter | MetricExporter"] = []
for endpoint in endpoints:
exporters.append(OTLPLogExporter(endpoint=endpoint))
@@ -277,7 +277,7 @@ def _get_otlp_exporters(endpoints: list[str]) -> list["LogExporter | SpanExporte
def _get_azure_monitor_exporters(
connection_strings: list[str],
credential: "TokenCredential | None" = None,
) -> list["LogExporter | SpanExporter | MetricExporter"]:
) -> list["LogRecordExporter | SpanExporter | MetricExporter"]:
"""Create Azure Monitor Exporters, based on the connection strings and optionally the credential."""
try:
from azure.monitor.opentelemetry.exporter import (
@@ -291,7 +291,7 @@ def _get_azure_monitor_exporters(
"Install it with: pip install azure-monitor-opentelemetry-exporter>=1.0.0b41"
) from e
exporters: list["LogExporter | SpanExporter | MetricExporter"] = []
exporters: list["LogRecordExporter | SpanExporter | MetricExporter"] = []
for conn_string in connection_strings:
exporters.append(AzureMonitorLogExporter(connection_string=conn_string, credential=credential))
exporters.append(AzureMonitorTraceExporter(connection_string=conn_string, credential=credential))
@@ -303,7 +303,7 @@ def get_exporters(
otlp_endpoints: list[str] | None = None,
connection_strings: list[str] | None = None,
credential: "TokenCredential | None" = None,
) -> list["LogExporter | SpanExporter | MetricExporter"]:
) -> list["LogRecordExporter | SpanExporter | MetricExporter"]:
"""Add additional exporters to the existing configuration.
If you supply exporters, those will be added to the relevant providers directly.
@@ -319,7 +319,7 @@ def get_exporters(
connection_strings: A list of Azure Monitor connection strings. Default is None.
credential: The credential to use for Azure Monitor Entra ID authentication. Default is None.
"""
new_exporters: list["LogExporter | SpanExporter | MetricExporter"] = []
new_exporters: list["LogRecordExporter | SpanExporter | MetricExporter"] = []
if otlp_endpoints:
new_exporters.extend(_get_otlp_exporters(endpoints=otlp_endpoints))
@@ -429,7 +429,7 @@ class ObservabilitySettings(AFBaseSettings):
def _configure(
self,
credential: "TokenCredential | None" = None,
additional_exporters: list["LogExporter | SpanExporter | MetricExporter"] | None = None,
additional_exporters: list["LogRecordExporter | SpanExporter | MetricExporter"] | None = None,
) -> None:
"""Configure application-wide observability based on the settings.
@@ -444,7 +444,7 @@ class ObservabilitySettings(AFBaseSettings):
if not self.ENABLED or self._executed_setup:
return
exporters: list["LogExporter | SpanExporter | MetricExporter"] = additional_exporters or []
exporters: list["LogRecordExporter | SpanExporter | MetricExporter"] = additional_exporters or []
if self.otlp_endpoint:
exporters.extend(
_get_otlp_exporters(
@@ -489,11 +489,11 @@ class ObservabilitySettings(AFBaseSettings):
else [self.applicationinsights_connection_string]
)
def _configure_providers(self, exporters: list["LogExporter | MetricExporter | SpanExporter"]) -> None:
def _configure_providers(self, exporters: list["LogRecordExporter | MetricExporter | SpanExporter"]) -> None:
"""Configure tracing, logging, events and metrics with the provided exporters."""
from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs._internal.export import LogExporter
from opentelemetry.sdk._logs._internal.export import LogRecordExporter
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import MetricExporter, PeriodicExportingMetricReader
@@ -518,7 +518,7 @@ class ObservabilitySettings(AFBaseSettings):
logger_provider = LoggerProvider(resource=self.resource)
should_add_console_exporter = True
for exporter in exporters:
if isinstance(exporter, LogExporter):
if isinstance(exporter, LogRecordExporter):
logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
should_add_console_exporter = False
if should_add_console_exporter:
@@ -667,7 +667,7 @@ def setup_observability(
otlp_endpoint: str | list[str] | None = None,
applicationinsights_connection_string: str | list[str] | None = None,
credential: "TokenCredential | None" = None,
exporters: list["LogExporter | SpanExporter | MetricExporter"] | None = None,
exporters: list["LogRecordExporter | SpanExporter | MetricExporter"] | None = None,
vs_code_extension_port: int | None = None,
) -> None:
"""Setup observability for the application with OpenTelemetry.
@@ -749,7 +749,7 @@ def setup_observability(
OBSERVABILITY_SETTINGS.vs_code_extension_port = vs_code_extension_port
# Create exporters, after checking if they are already configured through the env.
new_exporters: list["LogExporter | SpanExporter | MetricExporter"] = exporters or []
new_exporters: list["LogRecordExporter | SpanExporter | MetricExporter"] = exporters or []
if otlp_endpoint:
if isinstance(otlp_endpoint, str):
otlp_endpoint = [otlp_endpoint]
+3 -3
View File
@@ -28,9 +28,9 @@ dependencies = [
"pydantic>=2,<3",
"pydantic-settings>=2,<3",
# telemetry
"opentelemetry-api>=1.24",
"opentelemetry-sdk>=1.24",
"opentelemetry-exporter-otlp-proto-grpc>=1.36.0",
"opentelemetry-api>=1.39.0",
"opentelemetry-sdk>=1.39.0",
"opentelemetry-exporter-otlp-proto-grpc>=1.39.0",
"opentelemetry-semantic-conventions-ai>=0.4.13",
# connectors and functions
"openai>=1.99.0",
@@ -0,0 +1,43 @@
# Copyright (c) Microsoft. All rights reserved.
"""Tests for AgentRunEvent and AgentRunUpdateEvent type annotations."""
from agent_framework import AgentRunResponse, AgentRunResponseUpdate, ChatMessage, Role
from agent_framework._workflows._events import AgentRunEvent, AgentRunUpdateEvent
def test_agent_run_event_data_type() -> None:
"""Verify AgentRunEvent.data is typed as AgentRunResponse | None."""
response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Hello")])
event = AgentRunEvent(executor_id="test", data=response)
# This assignment should pass type checking without a cast
data: AgentRunResponse | None = event.data
assert data is not None
assert data.text == "Hello"
def test_agent_run_event_data_none() -> None:
"""Verify AgentRunEvent.data can be None."""
event = AgentRunEvent(executor_id="test")
data: AgentRunResponse | None = event.data
assert data is None
def test_agent_run_update_event_data_type() -> None:
"""Verify AgentRunUpdateEvent.data is typed as AgentRunResponseUpdate | None."""
update = AgentRunResponseUpdate()
event = AgentRunUpdateEvent(executor_id="test", data=update)
# This assignment should pass type checking without a cast
data: AgentRunResponseUpdate | None = event.data
assert data is not None
def test_agent_run_update_event_data_none() -> None:
"""Verify AgentRunUpdateEvent.data can be None."""
event = AgentRunUpdateEvent(executor_id="test")
data: AgentRunResponseUpdate | None = event.data
assert data is None
+1 -1
View File
@@ -29,7 +29,7 @@ dependencies = [
# GAIA benchmark module dependencies
gaia = [
"pydantic>=2.0.0",
"opentelemetry-api>=1.24.0",
"opentelemetry-api>=1.39.0",
"tqdm>=4.60.0",
"huggingface-hub>=0.20.0",
"orjson>=3.8.0",