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 instrumentation-loongsuite/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

| Instrumentation | Supported Packages | Metrics support | Semconv status |
| --------------- | ------------------ | --------------- | -------------- |
| [loongsuite-instrumentation-agentscope](./loongsuite-instrumentation-agentscope) | agentscope >= 1.0.0 | No | development
| [loongsuite-instrumentation-agentscope](./loongsuite-instrumentation-agentscope) | agentscope >= 1.0.0, < 3.0.0 | No | development
| [loongsuite-instrumentation-agno](./loongsuite-instrumentation-agno) | agno >= 2.0.0, < 3 | No | development
| [loongsuite-instrumentation-algotune](./loongsuite-instrumentation-algotune) | algotune | No | development
| [loongsuite-instrumentation-bfclv4](./loongsuite-instrumentation-bfclv4) | bfcl-eval >= 4.0.0 | No | development
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- Add version-aware AgentScope v2 middleware instrumentation while preserving
AgentScope v1 compatibility.

## Version 0.5.0 (2026-05-11)

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,23 @@ dependencies = [
"opentelemetry-instrumentation >= 0.58b0",
"opentelemetry-semantic-conventions >= 0.58b0",
"opentelemetry-util-genai",
"packaging >= 18.0",
"wrapt >= 1.17.3, < 2.0.0",
]

[project.optional-dependencies]
instruments = [
"agentscope >= 1.0.0",
"agentscope >= 1.0.0, < 3.0.0",
]

test = [
"pytest ~= 8.0",
"pytest-asyncio ~= 0.23.0",
"pytest-cov ~= 4.1.0",
"pytest-vcr ~= 1.0.2",
"vcrpy ~= 5.1.0",
"vcrpy >= 8.1.1",
"pyyaml ~= 6.0",
"agentscope >= 1.0.0",
"agentscope >= 1.0.0, < 3.0.0",
]

[project.entry-points.opentelemetry_instrumentor]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

"""
AgentScope instrumentation supporting `agentscope >= 1.0.0`.
AgentScope instrumentation supporting `agentscope >= 1.0.0, < 3.0.0`.

Usage
-----
Expand Down Expand Up @@ -49,25 +49,22 @@ async def call_model():
from __future__ import annotations

import logging
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as metadata_version
from typing import Any, Collection

from wrapt import wrap_function_wrapper

from opentelemetry import trace as trace_api
from opentelemetry.instrumentation.agentscope.package import _instruments
from opentelemetry.instrumentation.agentscope.package import (
get_installed_instrumentation_dependencies,
)
from opentelemetry.instrumentation.agentscope.version import __version__
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.utils import unwrap
from opentelemetry.semconv.schemas import Schemas
from opentelemetry.util.genai.extended_handler import ExtendedTelemetryHandler

from ._wrapper import (
AgentScopeAgentWrapper,
AgentScopeChatModelWrapper,
AgentScopeEmbeddingModelWrapper,
)
from .patch import wrap_formatter_format, wrap_tool_call

logger = logging.getLogger(__name__)

_MODEL_MODULE = "agentscope.model"
Expand All @@ -94,9 +91,10 @@ def __init__(self):
self._handler = (
None # ExtendedTelemetryHandler handles all other operations
)
self._agentscope_major = None

def instrumentation_dependencies(self) -> Collection[str]:
return _instruments
return get_installed_instrumentation_dependencies()

def _setup_tracing_patch(self, wrapped, instance, args, kwargs):
"""Replace setup_tracing with no-op to use OTEL instead."""
Expand Down Expand Up @@ -127,6 +125,23 @@ def _instrument(self, **kwargs: Any) -> None:
schema_url=Schemas.V1_37_0.value,
)

self._agentscope_major = _get_agentscope_major()
if self._agentscope_major >= 2:
self._instrument_v2()
else:
self._instrument_v1()

def _instrument_v1(self) -> None:
from ._wrapper import ( # noqa: PLC0415
AgentScopeAgentWrapper,
AgentScopeChatModelWrapper,
AgentScopeEmbeddingModelWrapper,
)
from .patch import ( # noqa: PLC0415
wrap_formatter_format,
wrap_tool_call,
)

# Instrument ChatModelBase
try:
chat_wrapper = AgentScopeChatModelWrapper(handler=self._handler)
Expand Down Expand Up @@ -224,21 +239,48 @@ def wrap_formatter_with_tracer(wrapped, instance, args, kwargs):

def _uninstrument(self, **kwargs: Any) -> None:
"""Disable AgentScope instrumentation."""
del kwargs
if self._agentscope_major is None:
self._agentscope_major = _get_agentscope_major()
if self._agentscope_major >= 2:
self._uninstrument_v2()
else:
self._uninstrument_v1()
self._handler = None
self._tracer = None
self._agentscope_major = None

def _uninstrument_v1(self) -> None:
try:
AgentScopeChatModelWrapper.restore_original_methods()
logger.debug("Restored ChatModelBase methods")
from ._wrapper import ( # noqa: PLC0415
AgentScopeAgentWrapper,
AgentScopeChatModelWrapper,
AgentScopeEmbeddingModelWrapper,
)
except Exception as e:
logger.warning(f"Failed to import AgentScope wrappers: {e}")
AgentScopeAgentWrapper = None
AgentScopeChatModelWrapper = None
AgentScopeEmbeddingModelWrapper = None

try:
if AgentScopeChatModelWrapper is not None:
AgentScopeChatModelWrapper.restore_original_methods()
logger.debug("Restored ChatModelBase methods")
except Exception as e:
logger.warning(f"Failed to restore ChatModelBase: {e}")

try:
AgentScopeAgentWrapper.restore_original_methods()
logger.debug("Restored AgentBase methods")
if AgentScopeAgentWrapper is not None:
AgentScopeAgentWrapper.restore_original_methods()
logger.debug("Restored AgentBase methods")
except Exception as e:
logger.warning(f"Failed to restore AgentBase: {e}")

try:
AgentScopeEmbeddingModelWrapper.restore_original_methods()
logger.debug("Restored EmbeddingModelBase methods")
if AgentScopeEmbeddingModelWrapper is not None:
AgentScopeEmbeddingModelWrapper.restore_original_methods()
logger.debug("Restored EmbeddingModelBase methods")
except Exception as e:
logger.warning(f"Failed to restore EmbeddingModelBase: {e}")

Expand Down Expand Up @@ -301,3 +343,50 @@ def _uninstrument(self, **kwargs: Any) -> None:
logger.warning(
f"Failed to uninstrument _check_tracing_enabled: {e}"
)

def _instrument_v2(self) -> None:
from ._v2_middleware import ( # noqa: PLC0415
AgentScopeV2Middleware,
append_loongsuite_middleware,
)

try:

def wrap_agent_init(wrapped, instance, args, kwargs):
args, kwargs = append_loongsuite_middleware(
args,
kwargs,
AgentScopeV2Middleware(handler=lambda: self._handler),
)
return wrapped(*args, **kwargs)

wrap_function_wrapper(
module=_AGENT_MODULE,
name="Agent.__init__",
wrapper=wrap_agent_init,
)
logger.debug("Instrumented AgentScope v2 Agent")
except Exception as e:
logger.warning(f"Failed to instrument AgentScope v2 Agent: {e}")

def _uninstrument_v2(self) -> None:
try:
import agentscope.agent # noqa: PLC0415

unwrap(agentscope.agent.Agent, "__init__")
logger.debug("Uninstrumented AgentScope v2 Agent")
except Exception as e:
logger.warning(f"Failed to uninstrument AgentScope v2 Agent: {e}")


def _get_agentscope_major() -> int:
try:
installed_version = metadata_version("agentscope")
except PackageNotFoundError:
return 1

major_text = installed_version.split(".", 1)[0]
try:
return int(major_text)
except ValueError:
return 1
Loading
Loading