Repository navigation
feat(ag2): add AG2 instrumentation package - #3510
mikeldking wants to merge 5 commits into
Conversation
| ignore: | ||
| - dependency-name: '*' | ||
| update-types: | ||
| - version-update:semver-major | ||
| - version-update:semver-minor | ||
| - version-update:semver-patch | ||
| package-ecosystem: pip |
There was a problem hiding this comment.
CLAUDE.md violation — do not hand-edit dependabot.yml
python/CLAUDE.md explicitly states:
Do not hand-edit
.github/dependabot.yml— it is generated from package manifests byscripts/generate_dependabot.pyvia CI.
This block was added manually. It should be removed from this PR and instead generated by running scripts/generate_dependabot.py (or letting CI regenerate it automatically after the package is merged).
| return tracer.start_span( | ||
| name, | ||
| attributes={ | ||
| SpanAttributes.OPENINFERENCE_SPAN_KIND: kind.value, | ||
| **get_input_attributes(_json_value(input_value), mime_type=JSON), | ||
| **attributes, | ||
| **dict(get_attributes_from_context()), | ||
| }, |
There was a problem hiding this comment.
CLAUDE.md violation — get_attributes_from_context() is injected automatically by OITracer
python/CLAUDE.md states:
Context propagation —
OITracer.start_span()injectsget_attributes_from_context()(session, user, metadata, tags) at span start automatically; do not also fetch and set them manually.
Since self._tracer is an OITracer instance (constructed in __init__.py), calling get_attributes_from_context() here results in context attributes (session ID, user ID, metadata, tags) being applied twice — once manually here, and again automatically inside OITracer.start_span(). The manual call should be removed:
| return tracer.start_span( | |
| name, | |
| attributes={ | |
| SpanAttributes.OPENINFERENCE_SPAN_KIND: kind.value, | |
| **get_input_attributes(_json_value(input_value), mime_type=JSON), | |
| **attributes, | |
| **dict(get_attributes_from_context()), | |
| }, | |
| def _start_span( | |
| tracer: trace_api.Tracer, | |
| name: str, | |
| kind: OpenInferenceSpanKindValues, | |
| input_value: Any, | |
| attributes: Mapping[str, Any], | |
| ) -> trace_api.Span: | |
| return tracer.start_span( | |
| name, | |
| attributes={ | |
| SpanAttributes.OPENINFERENCE_SPAN_KIND: kind.value, | |
| **get_input_attributes(_json_value(input_value), mime_type=JSON), | |
| **attributes, | |
| }, | |
| ) |
- Restore the legacy SpanAttributes class on the autogen compat package - Track instrumentation ownership in the facade so uninstrument() no longer tears down instrumentation installed independently via AG2Instrumentor - Accept the legacy autogen/pyautogen distribution names in the dependency check so instrument() is not a silent no-op, and drop the facade's skip_dep_check bypass and dead instrumentation_dependencies - Bound the autogen instruments extra to <1.0.0 - Normalize string func_call arguments before delegating to execute_function - Record the chat span's output.value as the final message content and emit the root chat span as AGENT kind, matching the legacy instrumentor - Disable use_span's own exception recording to avoid duplicate exception events on error spans - Replace the custom _json_value serializer with the shared get_input_attributes/get_output_attributes helpers
|
Closing as duplicate of #3379 — this branch is the head of that PR, and the review fixes have been pushed there. |
| - cooldown: | ||
| default-days: 7 | ||
| directory: python/instrumentation/openinference-instrumentation-ag2 | ||
| ignore: | ||
| - dependency-name: '*' | ||
| update-types: | ||
| - version-update:semver-major | ||
| - version-update:semver-minor | ||
| - version-update:semver-patch | ||
| package-ecosystem: pip | ||
| schedule: | ||
| interval: weekly |
There was a problem hiding this comment.
CLAUDE.md violation: do not hand-edit dependabot.yml
python/CLAUDE.md explicitly states:
Do not hand-edit
.github/dependabot.yml— it is generated from package manifests byscripts/generate_dependabot.pyvia CI.
This entry was added manually. The generator script (scripts/generate_dependabot.py) exists and is the correct way to update this file — it should be regenerated by CI after the new package is registered in the manifests, rather than edited by hand. A subsequent CI run may overwrite or conflict with this manual edit.
| from __future__ import annotations | ||
|
|
||
| import json | ||
| from collections.abc import Awaitable, Callable, Mapping | ||
| from contextlib import AbstractContextManager | ||
| from inspect import signature | ||
| from typing import Any | ||
|
|
||
| from opentelemetry import context as context_api | ||
| from opentelemetry import trace as trace_api | ||
| from opentelemetry.util.types import AttributeValue | ||
|
|
||
| from openinference.instrumentation import ( | ||
| get_input_attributes, | ||
| get_output_attributes, | ||
| safe_json_dumps, | ||
| ) | ||
| from openinference.semconv.trace import ( | ||
| OpenInferenceSpanKindValues, | ||
| SpanAttributes, | ||
| ToolCallAttributes, | ||
| ) |
There was a problem hiding this comment.
CLAUDE.md violation: missing stdlib logging setup
python/CLAUDE.md requires:
Use stdlib logging:
logger = logging.getLogger(__name__)pluslogger.addHandler(logging.NullHandler())at module top.
Every sibling instrumentor's _wrappers.py (autogen-agentchat, crewai, groq, anthropic, etc.) includes this boilerplate. This file has none of it. Please add:
import logging
# ... (other imports)
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())This is also a prerequisite for fixing the silent exception swallowing in _io_attributes (see related comment below).
| def _io_attributes( | ||
| value: Any, get_attributes: Callable[[Any], dict[str, AttributeValue]] | ||
| ) -> dict[str, AttributeValue]: | ||
| try: | ||
| return get_attributes(value) | ||
| except Exception: | ||
| return get_attributes("<unserializable>") |
There was a problem hiding this comment.
CLAUDE.md violation: silent exception swallowing without logger.exception()
python/CLAUDE.md requires:
Instrumentation must never raise into user code: wrap attribute extraction and span finalization in
try/exceptwithlogger.exception(...), and make sure the span still ends on failure.
The except Exception block here silently discards serialization failures with no diagnostic signal — operators have no way to know that attribute extraction failed. All sibling instrumentors (crewai, groq, anthropic, etc.) call logger.exception(...) in their equivalent fallback blocks.
Fix (requires adding logger from the related logging-setup comment above):
except Exception:
logger.exception("failed to serialize value for span attribute; falling back to placeholder")
return get_attributes("<unserializable>")
Summary
Adds a first-class
openinference-instrumentation-ag2package for the AG2 multi-agent framework, and converts the existingopeninference-instrumentation-autogenpackage into a thin deprecated compatibility facade that delegates to the new instrumentor.New package:
openinference-instrumentation-ag2AG2Instrumentor(aBaseInstrumentor) wrapsConversableAgent.initiate_chat/generate_reply/execute_functionand their async counterparts viawraptusing_attributes), andTraceConfigmasking viaOITraceropentelemetry_instrumentorandopeninference_instrumentorentry pointsopeninference-instrumentation-autogen(deprecated)AutogenInstrumentoris now a compatibility facade over the AG2 instrumentor singleton for users of the legacyautogendistributionopeninference-instrumentation-ag2Repo plumbing
ag2,ag2-latest; autogen env now installs both packages)Testing
tox run -e ruff-ag2,mypy-ag2✅tox run -e test-ag2✅ (12 passed)tox run -e ruff-mypy-test-autogen✅