Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Set up test environment variables
run: |
run: |
echo "POSTGRES_TEST_DSN=postgresql+asyncpg://a2a:a2a_password@localhost:5432/a2a_test" >> $GITHUB_ENV
echo "MYSQL_TEST_DSN=mysql+aiomysql://a2a:a2a_password@localhost:3306/a2a_test" >> $GITHUB_ENV

Expand All @@ -55,7 +55,7 @@ jobs:
run: |
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install dependencies
run: uv sync --dev --extra sql --extra encryption --extra grpc
run: uv sync --dev --extra sql --extra encryption --extra grpc --extra telemetry
- name: Run tests and check coverage
run: uv run pytest --cov=a2a --cov-report=xml --cov-fail-under=89
- name: Show coverage summary in log
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ To install with gRPC support:
uv add "a2a-sdk[grpc]"
```

To install with OpenTelemetry tracing support:

```bash
uv add "a2a-sdk[telemetry]"
```

To install with database support:

```bash
Expand Down Expand Up @@ -69,6 +75,12 @@ To install with gRPC support:
pip install "a2a-sdk[grpc]"
```

To install with OpenTelemetry tracing support:

```bash
pip install "a2a-sdk[telemetry]"
```

To install with database support:

```bash
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ dependencies = [
"fastapi>=0.115.2",
"httpx>=0.28.1",
"httpx-sse>=0.4.0",
"opentelemetry-api>=1.33.0",
"opentelemetry-sdk>=1.33.0",
"pydantic>=2.11.3",
"sse-starlette",
"starlette"
Expand All @@ -38,6 +36,7 @@ sqlite = ["sqlalchemy[asyncio,aiosqlite]>=2.0.0"]
sql = ["sqlalchemy[asyncio,postgresql-asyncpg,aiomysql,aiosqlite]>=2.0.0"]
encryption = ["cryptography>=43.0.0"]
grpc = ["grpcio>=1.60", "grpcio-tools>=1.60", "grpcio_reflection>=1.7.0", "protobuf==5.29.5", "google-api-core>=1.26.0"]
telemetry = ["opentelemetry-api>=1.33.0", "opentelemetry-sdk>=1.33.0", "opentelemetry-semantic-conventions>=0.55b1"]
Comment thread
holtskinner marked this conversation as resolved.
Outdated

[project.urls]
homepage = "https://a2a-protocol.org/"
Expand Down
48 changes: 34 additions & 14 deletions src/a2a/utils/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,53 @@
import logging

from collections.abc import Callable
from typing import Any, TypeAlias
from typing import Any

from opentelemetry import trace
from opentelemetry.trace import SpanKind as _SpanKind
from opentelemetry.trace import StatusCode

logger = logging.getLogger(__name__)

try:
from opentelemetry import trace
from opentelemetry.trace import SpanKind as _SpanKind
from opentelemetry.trace import StatusCode

except ImportError:
logger.debug(
'OpenTelemetry not found. Tracing will be disabled. '
'Install with: \'pip install "a2a-sdk[telemetry]"\''
)

class _NoOp:
"""A no-op object that absorbs all tracing calls when OpenTelemetry is not installed."""

def __call__(self, *args: Any, **kwargs: Any) -> '_NoOp':
return self

def __enter__(self) -> '_NoOp':
return self

SpanKind: TypeAlias = _SpanKind
def __exit__(self, *args: object, **kwargs: Any) -> None:
pass

def __getattr__(self, name: str) -> '_NoOp':
return self

trace = _NoOp()
_SpanKind = _NoOp()
StatusCode = _NoOp()
Comment thread
holtskinner marked this conversation as resolved.

SpanKind = _SpanKind
__all__ = ['SpanKind']

INSTRUMENTING_MODULE_NAME = 'a2a-python-sdk'
INSTRUMENTING_MODULE_VERSION = '1.0.0'

logger = logging.getLogger(__name__)


def trace_function( # noqa: PLR0915
func: Callable | None = None,
*,
span_name: str | None = None,
kind: SpanKind = SpanKind.INTERNAL,

Check failure on line 108 in src/a2a/utils/telemetry.py

View workflow job for this annotation

GitHub Actions / Lint Code Base

Variable not allowed in type expression (reportInvalidTypeForm)
attributes: dict[str, Any] | None = None,
attribute_extractor: Callable | None = None,
) -> Callable:
Expand Down Expand Up @@ -225,7 +252,7 @@
def trace_class(
include_list: list[str] | None = None,
exclude_list: list[str] | None = None,
kind: SpanKind = SpanKind.INTERNAL,

Check failure on line 255 in src/a2a/utils/telemetry.py

View workflow job for this annotation

GitHub Actions / Lint Code Base

Variable not allowed in type expression (reportInvalidTypeForm)
) -> Callable:
"""A class decorator to automatically trace specified methods of a class.

Expand Down Expand Up @@ -280,22 +307,15 @@
exclude_list = exclude_list or []

def decorator(cls: Any) -> Any:
all_methods = {}
for name, method in inspect.getmembers(cls, inspect.isfunction):
# Skip Dunders
if name.startswith('__') and name.endswith('__'):
continue

# Skip if include list is defined but the method not included.
if include_list and name not in include_list:
continue
# Skip if include list is not defined but the method is in excludes.
if not include_list and name in exclude_list:
continue

all_methods[name] = method
span_name = f'{cls.__module__}.{cls.__name__}.{name}'
# Set the decorator on the method.
setattr(
cls,
name,
Expand Down
Loading
Loading