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 .github/workflows/loongsuite_test_0.yml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Fixed

- Enable native telemetry on Microsoft Agent Framework 1.0, whose
``enable_instrumentation`` API does not yet accept ``force``.

### Tests

- Add replay-only VCR contracts against the real framework for content modes,
streaming, tools, provider errors, and concurrent trace isolation.

## Version 0.8.0 (2026-07-31)

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

"""OpenTelemetry instrumentation for Microsoft Agent Framework.

This instrumentor enables MAF's built-in OTel telemetry (``enable_instrumentation``
with ``force=True`` so a sticky user-disable does not block us), bridges MAF's
This instrumentor enables MAF's built-in OTel telemetry (using ``force=True``
when the installed MAF version supports it), bridges MAF's
native span helpers through ``opentelemetry-util-genai`` finish helpers, and
registers :class:`~.span_processor.MAFSemanticProcessor` for workflow/MCP
normalization. Microsoft Agent Framework's native counter and histogram
Expand All @@ -36,6 +36,7 @@

from __future__ import annotations

import inspect
import logging
from typing import Any, Collection, Optional

Expand Down Expand Up @@ -88,11 +89,12 @@ def _instrument(self, **kwargs: Any) -> None:
)
meter_provider = kwargs.get("meter_provider")

# 1) Enable MAF's built-in OTel instrumentation. ``force=True`` clears
# any sticky disable previously set by ``disable_instrumentation()``
# so our instrumentation always takes effect. ``enable_sensitive_data``
# is wired to the ARMS_MAF_SENSITIVE_DATA_ENABLED env (default False —
# PII/data redaction by default, per ARMS privacy guardrails).
# 1) Enable MAF's built-in OTel instrumentation. Newer MAF releases
# accept ``force=True`` to clear a sticky user-disable; MAF 1.0 does
# not expose that parameter, even though it is within our declared
# support range. ``enable_sensitive_data`` is wired to the
# ARMS_MAF_SENSITIVE_DATA_ENABLED env (default False — PII/data
# redaction by default, per ARMS privacy guardrails).
sensitive = bool(
kwargs.get(
"enable_sensitive_data",
Expand All @@ -102,7 +104,17 @@ def _instrument(self, **kwargs: Any) -> None:
try:
from agent_framework.observability import enable_instrumentation

enable_instrumentation(enable_sensitive_data=sensitive, force=True)
enable_kwargs = {"enable_sensitive_data": sensitive}
try:
supports_force = (
"force"
in inspect.signature(enable_instrumentation).parameters
)
except (TypeError, ValueError):
supports_force = False
if supports_force:
enable_kwargs["force"] = True
enable_instrumentation(**enable_kwargs)
except (ImportError, AttributeError, TypeError) as exc:
logger.warning(
"Could not enable MAF native instrumentation: %s. "
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
interactions:
- request:
body: '{"messages":[{"role":"system","content":"Return only the requested deterministic
token."},{"role":"user","content":"Reply with exactly MAF_VCR_NONSTREAM_OK"}],"model":"qwen-plus","stream":false}'
headers:
content-type:
- application/json
method: POST
uri: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
response:
body:
string: '{"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"message":{"content":"MAF_VCR_NONSTREAM_OK","role":"assistant"},"index":0,"finish_reason":"stop"}],"created":1700000000,"object":"chat.completion","usage":{"total_tokens":37,"completion_tokens":7,"prompt_tokens":30,"prompt_tokens_details":{"cached_tokens":0}}}'
headers:
content-type:
- application/json
status:
code: 200
message: OK
- request:
body: '{"messages":[{"role":"system","content":"Return only the requested deterministic
token."},{"role":"user","content":"Reply with exactly MAF_VCR_NONSTREAM_OK"}],"model":"qwen-plus","stream":false}'
headers:
content-type:
- application/json
method: POST
uri: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
response:
body:
string: '{"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"message":{"content":"MAF_VCR_NONSTREAM_OK","role":"assistant"},"index":0,"finish_reason":"stop"}],"created":1700000000,"object":"chat.completion","usage":{"total_tokens":37,"completion_tokens":7,"prompt_tokens":30,"prompt_tokens_details":{"cached_tokens":0}}}'
headers:
content-type:
- application/json
status:
code: 200
message: OK
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
interactions:
- request:
body: '{"messages":[{"role":"system","content":"Return only the requested deterministic
token."},{"role":"user","content":"Reply with exactly MAF_VCR_NONSTREAM_OK"}],"model":"qwen-plus","stream":false}'
headers:
content-type:
- application/json
method: POST
uri: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
response:
body:
string: '{"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"message":{"content":"MAF_VCR_NONSTREAM_OK","role":"assistant"},"index":0,"finish_reason":"stop"}],"created":1700000000,"object":"chat.completion","usage":{"total_tokens":37,"completion_tokens":7,"prompt_tokens":30,"prompt_tokens_details":{"cached_tokens":0}}}'
headers:
content-type:
- application/json
status:
code: 200
message: OK
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
interactions:
- request:
body: '{"messages":[{"role":"system","content":"Return only the requested deterministic
token."},{"role":"user","content":"Reply with exactly MAF_VCR_NONSTREAM_OK"}],"model":"qwen-plus","stream":false}'
headers:
content-type:
- application/json
method: POST
uri: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
response:
body:
string: '{"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"message":{"content":"MAF_VCR_NONSTREAM_OK","role":"assistant"},"index":0,"finish_reason":"stop"}],"created":1700000000,"object":"chat.completion","usage":{"total_tokens":37,"completion_tokens":7,"prompt_tokens":30,"prompt_tokens_details":{"cached_tokens":0}}}'
headers:
content-type:
- application/json
status:
code: 200
message: OK
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
interactions:
- request:
body: '{"messages":[{"role":"system","content":"Return a response."},{"role":"user","content":"Trigger
the provider failure."}],"model":"qwen-plus","stream":false}'
headers:
content-type:
- application/json
method: POST
uri: http://127.0.0.1:18765/v1/chat/completions
response:
body:
string: '{"error":{"message":"synthetic provider failure","type":"server_error","code":"maf_vcr_failure"}}'
headers:
content-type:
- application/json
status:
code: 500
message: Internal Server Error
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
interactions:
- request:
body: '{"messages":[{"role":"system","content":"Return only the requested deterministic
token."},{"role":"user","content":"Reply with exactly MAF_VCR_STREAM_OK"}],"model":"qwen-plus","stream":true,"stream_options":{"include_usage":true}}'
headers:
content-type:
- application/json
method: POST
uri: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
response:
body:
string: 'data: {"model":"qwen-plus","id":"maf-vcr-response-id","created":1700000000,"object":"chat.completion.chunk","usage":null,"choices":[{"logprobs":null,"index":0,"delta":{"content":"","role":"assistant"},"finish_reason":null}]}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":"MA"},"index":0,"finish_reason":null,"logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":"F"},"index":0,"finish_reason":null,"logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":"_VCR_STREAM"},"index":0,"finish_reason":null,"logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":"_OK"},"index":0,"finish_reason":null,"logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":""},"index":0,"finish_reason":"stop","logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[],"created":1700000000,"object":"chat.completion.chunk","usage":{"total_tokens":35,"completion_tokens":6,"prompt_tokens":29,"prompt_tokens_details":{"cached_tokens":0}}}


data: [DONE]

'
headers:
content-type:
- text/event-stream;charset=utf-8
status:
code: 200
message: OK
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
interactions:
- request:
body: '{"messages":[{"role":"system","content":"Return only the requested deterministic
token."},{"role":"user","content":"Reply with exactly MAF_VCR_STREAM_OK"}],"model":"qwen-plus","stream":true,"stream_options":{"include_usage":true}}'
headers:
content-type:
- application/json
method: POST
uri: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
response:
body:
string: 'data: {"model":"qwen-plus","id":"maf-vcr-response-id","created":1700000000,"object":"chat.completion.chunk","usage":null,"choices":[{"logprobs":null,"index":0,"delta":{"content":"","role":"assistant"},"finish_reason":null}]}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":"MA"},"index":0,"finish_reason":null,"logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":"F"},"index":0,"finish_reason":null,"logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":"_VCR_STREAM"},"index":0,"finish_reason":null,"logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":"_OK"},"index":0,"finish_reason":null,"logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[{"delta":{"content":""},"index":0,"finish_reason":"stop","logprobs":null}],"created":1700000000,"object":"chat.completion.chunk","usage":null}


data: {"model":"qwen-plus","id":"maf-vcr-response-id","choices":[],"created":1700000000,"object":"chat.completion.chunk","usage":{"total_tokens":35,"completion_tokens":6,"prompt_tokens":29,"prompt_tokens_details":{"cached_tokens":0}}}


data: [DONE]

'
headers:
content-type:
- text/event-stream;charset=utf-8
status:
code: 200
message: OK
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
interactions:
- request:
body: '{"messages":[{"role":"system","content":"Always call get_weather, then
return exactly MAF_VCR_TOOL_OK."},{"role":"user","content":"Check weather in
Hangzhou."}],"model":"qwen-plus","stream":false,"tool_choice":"auto","tools":[{"type":"function","function":{"name":"get_weather","description":"Return
deterministic weather for a city.","parameters":{"properties":{"city":{"title":"City","type":"string"}},"required":["city"],"title":"get_weather_input","type":"object"}}}]}'
headers:
content-type:
- application/json
method: POST
uri: http://127.0.0.1:18765/v1/chat/completions
response:
body:
string: '{"id":"maf-vcr-response-id","object":"chat.completion","created":1700000000,"model":"qwen-plus","choices":[{"index":0,"message":{"role":"assistant","content":null,"tool_calls":[{"id":"call_maf_weather","type":"function","function":{"name":"get_weather","arguments":"{\"city\":\"Hangzhou\"}"}}]},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":40,"completion_tokens":12,"total_tokens":52}}'
headers:
content-type:
- application/json
status:
code: 200
message: OK
- request:
body: '{"messages":[{"role":"system","content":"Always call get_weather, then
return exactly MAF_VCR_TOOL_OK."},{"role":"user","content":"Check weather in
Hangzhou."},{"role":"assistant","tool_calls":[{"id":"call_maf_weather","type":"function","function":{"name":"get_weather","arguments":"{\"city\":\"Hangzhou\"}"}}]},{"role":"tool","tool_call_id":"call_maf_weather","content":"Hangzhou:
sunny"}],"model":"qwen-plus","stream":false,"tool_choice":"auto","tools":[{"type":"function","function":{"name":"get_weather","description":"Return
deterministic weather for a city.","parameters":{"properties":{"city":{"title":"City","type":"string"}},"required":["city"],"title":"get_weather_input","type":"object"}}}]}'
headers:
content-type:
- application/json
method: POST
uri: http://127.0.0.1:18765/v1/chat/completions
response:
body:
string: '{"id":"maf-vcr-response-id","object":"chat.completion","created":1700000000,"model":"qwen-plus","choices":[{"index":0,"message":{"role":"assistant","content":"MAF_VCR_TOOL_OK"},"finish_reason":"stop"}],"usage":{"prompt_tokens":60,"completion_tokens":6,"total_tokens":66}}'
headers:
content-type:
- application/json
status:
code: 200
message: OK
version: 1
Loading
Loading