Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ All instrumentations use [opentelemetry-util-genai](./util/opentelemetry-util-ge
| [opentelemetry-instrumentation-genai-openai-agents](./instrumentation/opentelemetry-instrumentation-genai-openai-agents) | openai-agents >= 0.3.3, < 1 | [1.3b0](https://pypi.org/project/opentelemetry-instrumentation-genai-openai-agents/) |
| [opentelemetry-instrumentation-genai-portkey](./instrumentation/opentelemetry-instrumentation-genai-portkey) | portkey-ai >= 1.0.0, < 3 | [1.3b0](https://pypi.org/project/opentelemetry-instrumentation-genai-portkey/) |
| [opentelemetry-instrumentation-genai-qwen-agent](./instrumentation/opentelemetry-instrumentation-genai-qwen-agent) | qwen-agent >= 0.0.20, < 1 | [1.3b0](https://pypi.org/project/opentelemetry-instrumentation-genai-qwen-agent/) |
| [opentelemetry-instrumentation-genai-qwenpaw](./instrumentation/opentelemetry-instrumentation-genai-qwenpaw) | qwenpaw >= 1.1.0, < 2.0.0; python_version < '3.14' | [1.3b0](https://pypi.org/project/opentelemetry-instrumentation-genai-qwenpaw/) |
| [opentelemetry-instrumentation-genai-qwenpaw](./instrumentation/opentelemetry-instrumentation-genai-qwenpaw) | qwenpaw >= 1.1.0, < 3.0.0; python_version < '3.14' | [1.3b0](https://pypi.org/project/opentelemetry-instrumentation-genai-qwenpaw/) |
| [opentelemetry-instrumentation-genai-smolagents](./instrumentation/opentelemetry-instrumentation-genai-smolagents) | smolagents >= 1.24.0, < 2 | [1.2b0](https://pypi.org/project/opentelemetry-instrumentation-genai-smolagents/) |
| [opentelemetry-instrumentation-google-genai](./instrumentation/opentelemetry-instrumentation-google-genai) | google-genai >= 1.32.0, <3 | [1.3b0](https://pypi.org/project/opentelemetry-instrumentation-google-genai/) |

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support QwenPaw 2.x runtime turns while retaining compatibility with 1.x.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ OpenTelemetry QwenPaw Instrumentation
This library traces user turns handled by `QwenPaw
<https://github.com/agentscope-ai/QwenPaw>`_, a personal assistant
application built on AgentScope. Each turn that goes through
``AgentRunner.query_handler`` produces one ``invoke_agent`` span following
``AgentRunner.query_handler`` (1.x) or ``Runtime.run`` (2.x) produces one ``invoke_agent`` span following
the OpenTelemetry GenAI semantic conventions, carrying the agent display
name (``gen_ai.agent.name``, when already cached by the runner), the session id
(``gen_ai.conversation.id``), and — when content capture is enabled — the
Expand Down Expand Up @@ -40,11 +40,15 @@ under the ``invoke_agent`` span.
Supported versions
------------------

QwenPaw 1.1.x only. The 2.x `runtime architecture upgrade
<https://github.com/agentscope-ai/QwenPaw/blob/main/website/public/blog/runtime-architecture-upgrade.en.md>`_
refactored away ``AgentRunner.query_handler``, the seam this package
patches, so the dependency is capped at ``< 2.0.0`` and 2.x support will
be added separately against the new runtime entry point.
QwenPaw >= 1.1.0, < 3.0.0. Version 1.x uses the agent runner; version
2.x uses the runtime request entry point and captures text, reasoning,
tool calls/results, and image/audio/video/file content from request messages and
streamed SSE envelopes. Request normalization and span creation remain lazy;
SDK-generated session IDs propagate to nested GenAI invocations.
Version 2.x support requires ``opentelemetry-util-genai`` 1.4b0 or newer.
QwenPaw 2.x requires Python >= 3.11,
< 3.14. Both entry points produce one internal ``invoke_agent`` span per
turn; model and tool execution remain owned by the underlying libraries.

Installation
------------
Expand Down Expand Up @@ -113,6 +117,33 @@ Conformance
Semantic-convention conformance scenarios live in `tests/conformance
<tests/conformance>`_.

The same ``QwenPawInstrumentor`` selects the entry point for the installed
QwenPaw major version. The 1.x runner instrumentation remains supported.
Tests cover both the oldest and latest releases of each runtime generation:

.. list-table::
:header-rows: 1

* - QwenPaw
- Entry point
- Conformance tox environment
* - 1.1.0
- ``AgentRunner.query_handler``
- ``py311-test-instrumentation-genai-qwenpaw-oldest-conformance``
* - Latest 1.x
- ``AgentRunner.query_handler``
- ``py313-test-instrumentation-genai-qwenpaw-v1-conformance``
* - 2.0.0
- ``Runtime.run``
- ``py311-test-instrumentation-genai-qwenpaw-v2-conformance``
* - Latest 2.x
- ``Runtime.run``
- ``py313-test-instrumentation-genai-qwenpaw-conformance``

Each version runs successful and failed turns through Weaver live-check.
The 2.x scenarios additionally cover reasoning, tool calls/results, and
multimodal request messages.

References
----------

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
qwenpaw>=1.1.12.post3,<2.0.0
qwenpaw>=2.0,<3.0

opentelemetry-sdk~=1.45.2
opentelemetry-exporter-otlp-proto-grpc~=1.45.2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
qwenpaw>=1.1.12.post3,<2.0.0
qwenpaw>=2.0,<3.0

opentelemetry-sdk~=1.45.2
opentelemetry-distro~=0.64b0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies = [
[project.optional-dependencies]
# qwenpaw itself requires Python <3.14, so the marker keeps the extra
# installable (as a no-op) on newer interpreters used for tooling.
instruments = ["qwenpaw >= 1.1.0, < 2.0.0; python_version < '3.14'"]
instruments = ["qwenpaw >= 1.1.0, < 3.0.0; python_version < '3.14'"]

[project.entry-points.opentelemetry_instrumentor]
qwenpaw = "opentelemetry.instrumentation.genai.qwenpaw:QwenPawInstrumentor"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
Instrumentation for `QwenPaw <https://github.com/agentscope-ai/QwenPaw>`_,
a personal assistant application built on AgentScope.

Each user turn handled by ``AgentRunner.query_handler`` is traced as one
Each user turn handled by ``AgentRunner.query_handler`` (1.x) or
``Runtime.run`` (2.x) is traced as one
``invoke_agent`` span following the OpenTelemetry GenAI semantic
conventions.

Expand Down Expand Up @@ -51,6 +52,7 @@
import logging
from collections.abc import Collection
from importlib import import_module
from importlib.util import find_spec
from typing import Any

from wrapt import wrap_function_wrapper
Expand All @@ -62,19 +64,30 @@

from .package import _instruments
from .patch import make_query_handler_wrapper
from .runtime import make_runtime_wrapper, observe_context_build
from .version import __version__

logger = logging.getLogger(__name__)

__all__ = ["QwenPawInstrumentor"]

_RUNNER_MODULE = "qwenpaw.app.runner.runner"
_RUNTIME_MODULE = "qwenpaw.runtime.runtime"


def _runtime_v2() -> bool:
Comment thread
123liuziming marked this conversation as resolved.
Resolved
try:
return find_spec(_RUNTIME_MODULE) is not None
except ModuleNotFoundError as error:
if error.name not in ("qwenpaw", "qwenpaw.runtime"):
raise
return False


class QwenPawInstrumentor(BaseInstrumentor):
"""An instrumentor for the QwenPaw application runner.

Traces ``AgentRunner.query_handler`` as an ``invoke_agent`` span and
Traces the 1.x agent runner or 2.x runtime as an ``invoke_agent`` span and
optionally captures the turn's input/output messages. Model and tool
calls are delegated to AgentScope and are not instrumented here.
"""
Expand All @@ -83,7 +96,7 @@ def instrumentation_dependencies(self) -> Collection[str]:
return _instruments

def _instrument(self, **kwargs: Any) -> None:
"""Enable the ``AgentRunner.query_handler`` instrumentation.
"""Enable instrumentation for the installed QwenPaw runtime.

Args:
**kwargs: Optional arguments
Expand All @@ -102,20 +115,28 @@ def _instrument(self, **kwargs: Any) -> None:
completion_hook=kwargs.get("completion_hook")
or load_completion_hook(),
)
wrap_function_wrapper(
_RUNNER_MODULE,
"AgentRunner.query_handler",
make_query_handler_wrapper(handler),
)
logger.debug(
"Instrumented %s.AgentRunner.query_handler", _RUNNER_MODULE
)
if _runtime_v2():
wrap_function_wrapper(
_RUNTIME_MODULE,
"Runtime._build_context",
observe_context_build,
)
wrap_function_wrapper(
_RUNTIME_MODULE, "Runtime.run", make_runtime_wrapper(handler)
)
else:
wrap_function_wrapper(
_RUNNER_MODULE,
"AgentRunner.query_handler",
make_query_handler_wrapper(handler),
)

def _uninstrument(self, **kwargs: Any) -> None:
"""Disable the ``AgentRunner.query_handler`` instrumentation."""
"""Disable instrumentation for the installed QwenPaw runtime."""
del kwargs
runner_module = import_module(_RUNNER_MODULE)
unwrap(runner_module.AgentRunner, "query_handler")
logger.debug(
"Uninstrumented %s.AgentRunner.query_handler", _RUNNER_MODULE
)
if _runtime_v2():
runtime = import_module(_RUNTIME_MODULE).Runtime
unwrap(runtime, "run")
unwrap(runtime, "_build_context")
else:
unwrap(import_module(_RUNNER_MODULE).AgentRunner, "query_handler")
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

_instruments = ("qwenpaw >= 1.1.0, < 2.0.0; python_version < '3.14'",)
_instruments = ("qwenpaw >= 1.1.0, < 3.0.0; python_version < '3.14'",)

_supports_metrics = True
Loading
Loading