microsoft-agents-a365-observability-core 0.1.0.dev30__tar.gz → 0.2.1.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/PKG-INFO +5 -3
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/README.md +2 -2
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/__init__.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/agent_details.py +7 -4
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/config.py +71 -26
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/constants.py +5 -2
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/execute_tool_scope.py +15 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/execution_type.py +2 -1
- microsoft_agents_a365_observability_core-0.2.1.dev0/microsoft_agents_a365/observability/core/exporters/__init__.py +8 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +55 -7
- microsoft_agents_a365_observability_core-0.2.1.dev0/microsoft_agents_a365/observability/core/exporters/agent365_exporter_options.py +39 -0
- microsoft_agents_a365_observability_core-0.2.1.dev0/microsoft_agents_a365/observability/core/exporters/utils.py +204 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/inference_call_details.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/inference_operation_type.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/inference_scope.py +9 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/invoke_agent_details.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/invoke_agent_scope.py +12 -3
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/middleware/__init__.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/middleware/baggage_builder.py +52 -28
- microsoft_agents_a365_observability_core-0.2.1.dev0/microsoft_agents_a365/observability/core/models/operation_source.py +21 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/opentelemetry_scope.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/request.py +2 -2
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/source_metadata.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/tenant_details.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/tool_call_details.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/tool_type.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/trace_processor/__init__.py +2 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/trace_processor/span_processor.py +13 -3
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/trace_processor/util.py +10 -5
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/utils.py +53 -4
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365_observability_core.egg-info/PKG-INFO +5 -3
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365_observability_core.egg-info/SOURCES.txt +6 -1
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365_observability_core.egg-info/top_level.txt +1 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/pyproject.toml +4 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/setup.py +1 -1
- microsoft_agents_a365_observability_core-0.1.0.dev30/microsoft_agents_a365/observability/core/exporters/utils.py +0 -72
- microsoft_agents_a365_observability_core-0.1.0.dev30/microsoft_agents_a365/observability/core/middleware/turn_context_baggage.py +0 -193
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/models/__init__.py +0 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/models/agent_type.py +0 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365/observability/core/models/caller_details.py +0 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365_observability_core.egg-info/dependency_links.txt +0 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/microsoft_agents_a365_observability_core.egg-info/requires.txt +0 -0
- {microsoft_agents_a365_observability_core-0.1.0.dev30 → microsoft_agents_a365_observability_core-0.2.1.dev0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-agents-a365-observability-core
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.2.1.dev0
|
|
4
4
|
Summary: Telemetry, tracing, and monitoring components for AI agents
|
|
5
5
|
Author-email: Microsoft <support@microsoft.com>
|
|
6
6
|
License: MIT
|
|
@@ -20,6 +20,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
20
20
|
Classifier: Topic :: System :: Monitoring
|
|
21
21
|
Requires-Python: >=3.11
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
23
24
|
Requires-Dist: opentelemetry-api>=1.36.0
|
|
24
25
|
Requires-Dist: opentelemetry-sdk>=1.36.0
|
|
25
26
|
Requires-Dist: opentelemetry-exporter-otlp>=1.36.0
|
|
@@ -41,6 +42,7 @@ Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
|
41
42
|
Provides-Extra: test
|
|
42
43
|
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
43
44
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
|
|
45
|
+
Dynamic: license-file
|
|
44
46
|
|
|
45
47
|
# microsoft-agents-a365-observability-core
|
|
46
48
|
|
|
@@ -64,7 +66,7 @@ For usage examples and detailed documentation, see the [Observability documentat
|
|
|
64
66
|
For issues, questions, or feedback:
|
|
65
67
|
|
|
66
68
|
- File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
|
|
67
|
-
- See the [main documentation](
|
|
69
|
+
- See the [main documentation](../../README.md) for more information
|
|
68
70
|
|
|
69
71
|
## Trademarks
|
|
70
72
|
|
|
@@ -74,5 +76,5 @@ For issues, questions, or feedback:
|
|
|
74
76
|
|
|
75
77
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
76
78
|
|
|
77
|
-
Licensed under the MIT License - see the [LICENSE](
|
|
79
|
+
Licensed under the MIT License - see the [LICENSE](../../LICENSE.md) file for details.
|
|
78
80
|
|
|
@@ -20,7 +20,7 @@ For usage examples and detailed documentation, see the [Observability documentat
|
|
|
20
20
|
For issues, questions, or feedback:
|
|
21
21
|
|
|
22
22
|
- File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
|
|
23
|
-
- See the [main documentation](
|
|
23
|
+
- See the [main documentation](../../README.md) for more information
|
|
24
24
|
|
|
25
25
|
## Trademarks
|
|
26
26
|
|
|
@@ -30,5 +30,5 @@ For issues, questions, or feedback:
|
|
|
30
30
|
|
|
31
31
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
32
32
|
|
|
33
|
-
Licensed under the MIT License - see the [LICENSE](
|
|
33
|
+
Licensed under the MIT License - see the [LICENSE](../../LICENSE.md) file for details.
|
|
34
34
|
|
|
@@ -21,22 +21,25 @@ class AgentDetails:
|
|
|
21
21
|
"""A description of the AI agent's purpose or capabilities."""
|
|
22
22
|
|
|
23
23
|
agent_auid: Optional[str] = None
|
|
24
|
-
"""
|
|
24
|
+
"""Agentic User ID for the agent."""
|
|
25
25
|
|
|
26
26
|
agent_upn: Optional[str] = None
|
|
27
|
-
"""
|
|
27
|
+
"""User Principal Name (UPN) for the agentic user."""
|
|
28
28
|
|
|
29
29
|
agent_blueprint_id: Optional[str] = None
|
|
30
|
-
"""
|
|
30
|
+
"""Blueprint/Application ID for the agent."""
|
|
31
31
|
|
|
32
32
|
agent_type: Optional[AgentType] = None
|
|
33
33
|
"""The agent type."""
|
|
34
34
|
|
|
35
35
|
tenant_id: Optional[str] = None
|
|
36
|
-
"""
|
|
36
|
+
"""Tenant ID for the agent."""
|
|
37
37
|
|
|
38
38
|
conversation_id: Optional[str] = None
|
|
39
39
|
"""Optional conversation ID for compatibility."""
|
|
40
40
|
|
|
41
41
|
icon_uri: Optional[str] = None
|
|
42
42
|
"""Optional icon URI for the agent."""
|
|
43
|
+
|
|
44
|
+
agent_client_ip: Optional[str] = None
|
|
45
|
+
"""Client IP address of the agent user."""
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
import logging
|
|
4
5
|
import threading
|
|
@@ -10,7 +11,8 @@ from opentelemetry.sdk.resources import SERVICE_NAME, SERVICE_NAMESPACE, Resourc
|
|
|
10
11
|
from opentelemetry.sdk.trace import TracerProvider
|
|
11
12
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
|
|
12
13
|
|
|
13
|
-
from .exporters.agent365_exporter import
|
|
14
|
+
from .exporters.agent365_exporter import _Agent365Exporter
|
|
15
|
+
from .exporters.agent365_exporter_options import Agent365ExporterOptions
|
|
14
16
|
from .exporters.utils import is_agent365_exporter_enabled
|
|
15
17
|
from .trace_processor.span_processor import SpanProcessor
|
|
16
18
|
|
|
@@ -51,6 +53,8 @@ class TelemetryManager:
|
|
|
51
53
|
logger_name: str = DEFAULT_LOGGER_NAME,
|
|
52
54
|
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
53
55
|
cluster_category: str = "prod",
|
|
56
|
+
exporter_options: Optional[Agent365ExporterOptions] = None,
|
|
57
|
+
suppress_invoke_agent_input: bool = False,
|
|
54
58
|
**kwargs: Any,
|
|
55
59
|
) -> bool:
|
|
56
60
|
"""
|
|
@@ -59,8 +63,13 @@ class TelemetryManager:
|
|
|
59
63
|
:param service_name: The name of the service.
|
|
60
64
|
:param service_namespace: The namespace of the service.
|
|
61
65
|
:param logger_name: The name of the logger to collect telemetry from.
|
|
62
|
-
:param token_resolver: Callable that returns an auth token for a given agent + tenant.
|
|
63
|
-
|
|
66
|
+
:param token_resolver: (Deprecated) Callable that returns an auth token for a given agent + tenant.
|
|
67
|
+
Use exporter_options instead.
|
|
68
|
+
:param cluster_category: (Deprecated) Environment / cluster category (e.g. "prod").
|
|
69
|
+
Use exporter_options instead.
|
|
70
|
+
:param exporter_options: Agent365ExporterOptions instance for configuring the exporter.
|
|
71
|
+
If provided, exporter_options takes precedence. If exporter_options is None, the token_resolver and cluster_category parameters are used as fallback/legacy support to construct a default Agent365ExporterOptions instance.
|
|
72
|
+
:param suppress_invoke_agent_input: If True, suppress input messages for spans that are children of InvokeAgent spans.
|
|
64
73
|
:return: True if configuration succeeded, False otherwise.
|
|
65
74
|
"""
|
|
66
75
|
try:
|
|
@@ -71,6 +80,8 @@ class TelemetryManager:
|
|
|
71
80
|
logger_name,
|
|
72
81
|
token_resolver,
|
|
73
82
|
cluster_category,
|
|
83
|
+
exporter_options,
|
|
84
|
+
suppress_invoke_agent_input,
|
|
74
85
|
**kwargs,
|
|
75
86
|
)
|
|
76
87
|
except Exception as e:
|
|
@@ -84,10 +95,19 @@ class TelemetryManager:
|
|
|
84
95
|
logger_name: str,
|
|
85
96
|
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
86
97
|
cluster_category: str = "prod",
|
|
98
|
+
exporter_options: Optional[Agent365ExporterOptions] = None,
|
|
99
|
+
suppress_invoke_agent_input: bool = False,
|
|
87
100
|
**kwargs: Any,
|
|
88
101
|
) -> bool:
|
|
89
102
|
"""Internal configuration method - not thread-safe, must be called with lock."""
|
|
90
103
|
|
|
104
|
+
# Check if a365 observability is already configured
|
|
105
|
+
if self._tracer_provider is not None:
|
|
106
|
+
self._logger.warning(
|
|
107
|
+
"a365 observability already configured. Ignoring repeated configure() call."
|
|
108
|
+
)
|
|
109
|
+
return True
|
|
110
|
+
|
|
91
111
|
# Create resource with service information
|
|
92
112
|
resource = Resource.create(
|
|
93
113
|
{
|
|
@@ -96,30 +116,47 @@ class TelemetryManager:
|
|
|
96
116
|
}
|
|
97
117
|
)
|
|
98
118
|
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
# Check if there's an existing TracerProvider (from app's OTEL setup)
|
|
120
|
+
tracer_provider = trace.get_tracer_provider()
|
|
121
|
+
|
|
122
|
+
# Determine if we should use existing provider or create new one
|
|
123
|
+
# Check if it's a real TracerProvider with a resource (not a proxy/no-op)
|
|
124
|
+
if getattr(tracer_provider, "resource", None):
|
|
125
|
+
# Use existing provider from application's OTEL setup
|
|
126
|
+
self._logger.info(
|
|
127
|
+
"Detected existing TracerProvider with resource. "
|
|
128
|
+
"Adding a365 observability processors to it."
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
# Create new TracerProvider with our resource
|
|
132
|
+
self._logger.info("Creating new TracerProvider for a365 observability.")
|
|
133
|
+
tracer_provider = TracerProvider(resource=resource)
|
|
134
|
+
trace.set_tracer_provider(tracer_provider)
|
|
135
|
+
|
|
136
|
+
# Store reference
|
|
116
137
|
self._tracer_provider = tracer_provider
|
|
117
138
|
|
|
118
|
-
if
|
|
119
|
-
|
|
120
|
-
|
|
139
|
+
# Use exporter_options if provided, otherwise create default options with legacy parameters
|
|
140
|
+
if exporter_options is None:
|
|
141
|
+
exporter_options = Agent365ExporterOptions(
|
|
121
142
|
cluster_category=cluster_category,
|
|
122
|
-
|
|
143
|
+
token_resolver=token_resolver,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Extract configuration for BatchSpanProcessor
|
|
147
|
+
batch_processor_kwargs = {
|
|
148
|
+
"max_queue_size": exporter_options.max_queue_size,
|
|
149
|
+
"schedule_delay_millis": exporter_options.scheduled_delay_ms,
|
|
150
|
+
"export_timeout_millis": exporter_options.exporter_timeout_ms,
|
|
151
|
+
"max_export_batch_size": exporter_options.max_export_batch_size,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if is_agent365_exporter_enabled() and exporter_options.token_resolver is not None:
|
|
155
|
+
exporter = _Agent365Exporter(
|
|
156
|
+
token_resolver=exporter_options.token_resolver,
|
|
157
|
+
cluster_category=exporter_options.cluster_category,
|
|
158
|
+
use_s2s_endpoint=exporter_options.use_s2s_endpoint,
|
|
159
|
+
suppress_invoke_agent_input=suppress_invoke_agent_input,
|
|
123
160
|
)
|
|
124
161
|
else:
|
|
125
162
|
exporter = ConsoleSpanExporter()
|
|
@@ -130,7 +167,7 @@ class TelemetryManager:
|
|
|
130
167
|
# Add span processors
|
|
131
168
|
|
|
132
169
|
# Create BatchSpanProcessor with optimized settings
|
|
133
|
-
batch_processor = BatchSpanProcessor(exporter)
|
|
170
|
+
batch_processor = BatchSpanProcessor(exporter, **batch_processor_kwargs)
|
|
134
171
|
agent_processor = SpanProcessor()
|
|
135
172
|
|
|
136
173
|
tracer_provider.add_span_processor(batch_processor)
|
|
@@ -197,6 +234,7 @@ def configure(
|
|
|
197
234
|
logger_name: str = DEFAULT_LOGGER_NAME,
|
|
198
235
|
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
199
236
|
cluster_category: str = "prod",
|
|
237
|
+
exporter_options: Optional[Agent365ExporterOptions] = None,
|
|
200
238
|
**kwargs: Any,
|
|
201
239
|
) -> bool:
|
|
202
240
|
"""
|
|
@@ -205,6 +243,12 @@ def configure(
|
|
|
205
243
|
:param service_name: The name of the service.
|
|
206
244
|
:param service_namespace: The namespace of the service.
|
|
207
245
|
:param logger_name: The name of the logger to collect telemetry from.
|
|
246
|
+
:param token_resolver: (Deprecated) Callable that returns an auth token for a given agent + tenant.
|
|
247
|
+
Use exporter_options instead.
|
|
248
|
+
:param cluster_category: (Deprecated) Environment / cluster category (e.g. "prod").
|
|
249
|
+
Use exporter_options instead.
|
|
250
|
+
:param exporter_options: Agent365ExporterOptions instance for configuring the exporter.
|
|
251
|
+
If provided, exporter_options takes precedence. If exporter_options is None, the token_resolver and cluster_category parameters are used as fallback/legacy support to construct a default Agent365ExporterOptions instance.
|
|
208
252
|
:return: True if configuration succeeded, False otherwise.
|
|
209
253
|
"""
|
|
210
254
|
return _telemetry_manager.configure(
|
|
@@ -213,6 +257,7 @@ def configure(
|
|
|
213
257
|
logger_name,
|
|
214
258
|
token_resolver,
|
|
215
259
|
cluster_category,
|
|
260
|
+
exporter_options,
|
|
216
261
|
**kwargs,
|
|
217
262
|
)
|
|
218
263
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
# Constants for SDK OpenTelemetry implementation.
|
|
4
5
|
|
|
@@ -68,6 +69,7 @@ GEN_AI_CALLER_TENANT_ID_KEY = "gen_ai.caller.tenantid"
|
|
|
68
69
|
GEN_AI_CALLER_ID_KEY = "gen_ai.caller.id"
|
|
69
70
|
GEN_AI_CALLER_NAME_KEY = "gen_ai.caller.name"
|
|
70
71
|
GEN_AI_CALLER_UPN_KEY = "gen_ai.caller.upn"
|
|
72
|
+
GEN_AI_CALLER_CLIENT_IP_KEY = "gen_ai.caller.client.ip"
|
|
71
73
|
|
|
72
74
|
# Agent to Agent caller agent dimensions
|
|
73
75
|
GEN_AI_CALLER_AGENT_USER_ID_KEY = "gen_ai.caller.agent.userid"
|
|
@@ -77,6 +79,7 @@ GEN_AI_CALLER_AGENT_NAME_KEY = "gen_ai.caller.agent.name"
|
|
|
77
79
|
GEN_AI_CALLER_AGENT_ID_KEY = "gen_ai.caller.agent.id"
|
|
78
80
|
GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = "gen_ai.caller.agent.applicationid"
|
|
79
81
|
GEN_AI_CALLER_AGENT_TYPE_KEY = "gen_ai.caller.agent.type"
|
|
82
|
+
GEN_AI_CALLER_AGENT_USER_CLIENT_IP = "gen_ai.caller.agent.user.client.ip"
|
|
80
83
|
|
|
81
84
|
# Agent-specific dimensions
|
|
82
85
|
AGENT_ID_KEY = "gen_ai.agent.id"
|
|
@@ -92,13 +95,13 @@ GEN_AI_AGENT_UPN_KEY = "gen_ai.agent.upn"
|
|
|
92
95
|
GEN_AI_AGENT_BLUEPRINT_ID_KEY = "gen_ai.agent.applicationid"
|
|
93
96
|
CORRELATION_ID_KEY = "correlation.id"
|
|
94
97
|
HIRING_MANAGER_ID_KEY = "hiring.manager.id"
|
|
98
|
+
SESSION_DESCRIPTION_KEY = "session.description"
|
|
95
99
|
|
|
96
100
|
# Execution context dimensions
|
|
97
101
|
GEN_AI_EXECUTION_TYPE_KEY = "gen_ai.execution.type"
|
|
98
102
|
GEN_AI_EXECUTION_PAYLOAD_KEY = "gen_ai.execution.payload"
|
|
99
103
|
|
|
100
104
|
# Source metadata dimensions
|
|
101
|
-
GEN_AI_EXECUTION_SOURCE_ID_KEY = "gen_ai.execution.sourceMetadata.id"
|
|
102
105
|
GEN_AI_EXECUTION_SOURCE_NAME_KEY = "gen_ai.channel.name"
|
|
103
106
|
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY = "gen_ai.channel.link"
|
|
104
107
|
|
|
@@ -5,6 +5,8 @@ from .agent_details import AgentDetails
|
|
|
5
5
|
from .constants import (
|
|
6
6
|
EXECUTE_TOOL_OPERATION_NAME,
|
|
7
7
|
GEN_AI_EVENT_CONTENT,
|
|
8
|
+
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY,
|
|
9
|
+
GEN_AI_EXECUTION_SOURCE_NAME_KEY,
|
|
8
10
|
GEN_AI_TOOL_ARGS_KEY,
|
|
9
11
|
GEN_AI_TOOL_CALL_ID_KEY,
|
|
10
12
|
GEN_AI_TOOL_DESCRIPTION_KEY,
|
|
@@ -14,6 +16,7 @@ from .constants import (
|
|
|
14
16
|
SERVER_PORT_KEY,
|
|
15
17
|
)
|
|
16
18
|
from .opentelemetry_scope import OpenTelemetryScope
|
|
19
|
+
from .request import Request
|
|
17
20
|
from .tenant_details import TenantDetails
|
|
18
21
|
from .tool_call_details import ToolCallDetails
|
|
19
22
|
|
|
@@ -26,6 +29,7 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
26
29
|
details: ToolCallDetails,
|
|
27
30
|
agent_details: AgentDetails,
|
|
28
31
|
tenant_details: TenantDetails,
|
|
32
|
+
request: Request | None = None,
|
|
29
33
|
) -> "ExecuteToolScope":
|
|
30
34
|
"""Creates and starts a new scope for tool execution tracing.
|
|
31
35
|
|
|
@@ -33,17 +37,19 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
33
37
|
details: The details of the tool call
|
|
34
38
|
agent_details: The details of the agent making the call
|
|
35
39
|
tenant_details: The details of the tenant
|
|
40
|
+
request: Optional request details for additional context
|
|
36
41
|
|
|
37
42
|
Returns:
|
|
38
43
|
A new ExecuteToolScope instance
|
|
39
44
|
"""
|
|
40
|
-
return ExecuteToolScope(details, agent_details, tenant_details)
|
|
45
|
+
return ExecuteToolScope(details, agent_details, tenant_details, request)
|
|
41
46
|
|
|
42
47
|
def __init__(
|
|
43
48
|
self,
|
|
44
49
|
details: ToolCallDetails,
|
|
45
50
|
agent_details: AgentDetails,
|
|
46
51
|
tenant_details: TenantDetails,
|
|
52
|
+
request: Request | None = None,
|
|
47
53
|
):
|
|
48
54
|
"""Initialize the tool execution scope.
|
|
49
55
|
|
|
@@ -51,6 +57,7 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
51
57
|
details: The details of the tool call
|
|
52
58
|
agent_details: The details of the agent making the call
|
|
53
59
|
tenant_details: The details of the tenant
|
|
60
|
+
request: Optional request details for additional context
|
|
54
61
|
"""
|
|
55
62
|
super().__init__(
|
|
56
63
|
kind="Internal",
|
|
@@ -79,6 +86,13 @@ class ExecuteToolScope(OpenTelemetryScope):
|
|
|
79
86
|
if endpoint.port and endpoint.port != 443:
|
|
80
87
|
self.set_tag_maybe(SERVER_PORT_KEY, endpoint.port)
|
|
81
88
|
|
|
89
|
+
# Set request metadata if provided
|
|
90
|
+
if request and request.source_metadata:
|
|
91
|
+
self.set_tag_maybe(GEN_AI_EXECUTION_SOURCE_NAME_KEY, request.source_metadata.name)
|
|
92
|
+
self.set_tag_maybe(
|
|
93
|
+
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, request.source_metadata.description
|
|
94
|
+
)
|
|
95
|
+
|
|
82
96
|
def record_response(self, response: str) -> None:
|
|
83
97
|
"""Records response information for telemetry tracking.
|
|
84
98
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from .agent365_exporter_options import Agent365ExporterOptions
|
|
5
|
+
|
|
6
|
+
# Agent365Exporter is not exported intentionally.
|
|
7
|
+
# It should only be used internally by the observability core module.
|
|
8
|
+
__all__ = ["Agent365ExporterOptions"]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
# pip install opentelemetry-sdk opentelemetry-api requests
|
|
4
5
|
|
|
@@ -9,7 +10,8 @@ import logging
|
|
|
9
10
|
import threading
|
|
10
11
|
import time
|
|
11
12
|
from collections.abc import Callable, Sequence
|
|
12
|
-
from typing import Any
|
|
13
|
+
from typing import Any, final
|
|
14
|
+
from urllib.parse import urlparse
|
|
13
15
|
|
|
14
16
|
import requests
|
|
15
17
|
from microsoft_agents_a365.runtime.power_platform_api_discovery import PowerPlatformApiDiscovery
|
|
@@ -17,12 +19,19 @@ from opentelemetry.sdk.trace import ReadableSpan
|
|
|
17
19
|
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
|
|
18
20
|
from opentelemetry.trace import StatusCode
|
|
19
21
|
|
|
22
|
+
from ..constants import (
|
|
23
|
+
GEN_AI_INPUT_MESSAGES_KEY,
|
|
24
|
+
GEN_AI_OPERATION_NAME_KEY,
|
|
25
|
+
INVOKE_AGENT_OPERATION_NAME,
|
|
26
|
+
)
|
|
20
27
|
from .utils import (
|
|
28
|
+
get_validated_domain_override,
|
|
21
29
|
hex_span_id,
|
|
22
30
|
hex_trace_id,
|
|
23
31
|
kind_name,
|
|
24
32
|
partition_by_identity,
|
|
25
33
|
status_name,
|
|
34
|
+
truncate_span,
|
|
26
35
|
)
|
|
27
36
|
|
|
28
37
|
# ---- Exporter ---------------------------------------------------------------
|
|
@@ -35,7 +44,8 @@ DEFAULT_MAX_RETRIES = 3
|
|
|
35
44
|
logger = logging.getLogger(__name__)
|
|
36
45
|
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
@final
|
|
48
|
+
class _Agent365Exporter(SpanExporter):
|
|
39
49
|
"""
|
|
40
50
|
Agent 365 span exporter for Agent 365:
|
|
41
51
|
* Partitions spans by (tenantId, agentId)
|
|
@@ -49,6 +59,7 @@ class Agent365Exporter(SpanExporter):
|
|
|
49
59
|
token_resolver: Callable[[str, str], str | None],
|
|
50
60
|
cluster_category: str = "prod",
|
|
51
61
|
use_s2s_endpoint: bool = False,
|
|
62
|
+
suppress_invoke_agent_input: bool = False,
|
|
52
63
|
):
|
|
53
64
|
if token_resolver is None:
|
|
54
65
|
raise ValueError("token_resolver must be provided.")
|
|
@@ -58,6 +69,9 @@ class Agent365Exporter(SpanExporter):
|
|
|
58
69
|
self._token_resolver = token_resolver
|
|
59
70
|
self._cluster_category = cluster_category
|
|
60
71
|
self._use_s2s_endpoint = use_s2s_endpoint
|
|
72
|
+
self._suppress_invoke_agent_input = suppress_invoke_agent_input
|
|
73
|
+
# Read domain override once at initialization
|
|
74
|
+
self._domain_override = get_validated_domain_override()
|
|
61
75
|
|
|
62
76
|
# ------------- SpanExporter API -----------------
|
|
63
77
|
|
|
@@ -84,14 +98,29 @@ class Agent365Exporter(SpanExporter):
|
|
|
84
98
|
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
|
|
85
99
|
|
|
86
100
|
# Resolve endpoint + token
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
if self._domain_override:
|
|
102
|
+
endpoint = self._domain_override
|
|
103
|
+
else:
|
|
104
|
+
discovery = PowerPlatformApiDiscovery(self._cluster_category)
|
|
105
|
+
endpoint = discovery.get_tenant_island_cluster_endpoint(tenant_id)
|
|
106
|
+
|
|
89
107
|
endpoint_path = (
|
|
90
108
|
f"/maven/agent365/service/agents/{agent_id}/traces"
|
|
91
109
|
if self._use_s2s_endpoint
|
|
92
110
|
else f"/maven/agent365/agents/{agent_id}/traces"
|
|
93
111
|
)
|
|
94
|
-
|
|
112
|
+
|
|
113
|
+
# Construct URL - if endpoint has a scheme (http:// or https://), use it as-is
|
|
114
|
+
# Otherwise, prepend https://
|
|
115
|
+
# Note: Check for "://" to distinguish between real protocols and domain:port format
|
|
116
|
+
# (urlparse treats "example.com:8080" as having scheme="example.com")
|
|
117
|
+
parsed = urlparse(endpoint)
|
|
118
|
+
if parsed.scheme and "://" in endpoint:
|
|
119
|
+
# Endpoint is a full URL, append path
|
|
120
|
+
url = f"{endpoint}{endpoint_path}?api-version=1"
|
|
121
|
+
else:
|
|
122
|
+
# Endpoint is just a domain (possibly with port), prepend https://
|
|
123
|
+
url = f"https://{endpoint}{endpoint_path}?api-version=1"
|
|
95
124
|
|
|
96
125
|
# Debug: Log endpoint being used
|
|
97
126
|
logger.info(
|
|
@@ -140,6 +169,8 @@ class Agent365Exporter(SpanExporter):
|
|
|
140
169
|
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
141
170
|
return True
|
|
142
171
|
|
|
172
|
+
# ------------- Helper methods -------------------
|
|
173
|
+
|
|
143
174
|
# ------------- HTTP helper ----------------------
|
|
144
175
|
|
|
145
176
|
@staticmethod
|
|
@@ -256,6 +287,20 @@ class Agent365Exporter(SpanExporter):
|
|
|
256
287
|
|
|
257
288
|
# attributes
|
|
258
289
|
attrs = dict(sp.attributes or {})
|
|
290
|
+
|
|
291
|
+
# Suppress input messages if configured and current span is an InvokeAgent span
|
|
292
|
+
if self._suppress_invoke_agent_input:
|
|
293
|
+
# Check if current span is an InvokeAgent span by:
|
|
294
|
+
# 1. Span name starts with "invoke_agent"
|
|
295
|
+
# 2. Has attribute gen_ai.operation.name set to INVOKE_AGENT_OPERATION_NAME
|
|
296
|
+
operation_name = attrs.get(GEN_AI_OPERATION_NAME_KEY)
|
|
297
|
+
if (
|
|
298
|
+
sp.name.startswith(INVOKE_AGENT_OPERATION_NAME)
|
|
299
|
+
and operation_name == INVOKE_AGENT_OPERATION_NAME
|
|
300
|
+
):
|
|
301
|
+
# Remove input messages attribute
|
|
302
|
+
attrs.pop(GEN_AI_INPUT_MESSAGES_KEY, None)
|
|
303
|
+
|
|
259
304
|
# events
|
|
260
305
|
events = []
|
|
261
306
|
for ev in sp.events:
|
|
@@ -295,7 +340,7 @@ class Agent365Exporter(SpanExporter):
|
|
|
295
340
|
start_ns = sp.start_time
|
|
296
341
|
end_ns = sp.end_time
|
|
297
342
|
|
|
298
|
-
|
|
343
|
+
span_dict = {
|
|
299
344
|
"traceId": hex_trace_id(ctx.trace_id),
|
|
300
345
|
"spanId": hex_span_id(ctx.span_id),
|
|
301
346
|
"parentSpanId": parent_span_id,
|
|
@@ -308,3 +353,6 @@ class Agent365Exporter(SpanExporter):
|
|
|
308
353
|
"links": links,
|
|
309
354
|
"status": status,
|
|
310
355
|
}
|
|
356
|
+
|
|
357
|
+
# Apply truncation if needed
|
|
358
|
+
return truncate_span(span_dict)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from typing import Awaitable, Callable, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Agent365ExporterOptions:
|
|
8
|
+
"""
|
|
9
|
+
Configuration for Agent365Exporter.
|
|
10
|
+
Only cluster_category and token_resolver are required for core operation.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
cluster_category: str = "prod",
|
|
16
|
+
token_resolver: Optional[Callable[[str, str], Awaitable[Optional[str]]]] = None,
|
|
17
|
+
use_s2s_endpoint: bool = False,
|
|
18
|
+
max_queue_size: int = 2048,
|
|
19
|
+
scheduled_delay_ms: int = 5000,
|
|
20
|
+
exporter_timeout_ms: int = 30000,
|
|
21
|
+
max_export_batch_size: int = 512,
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
Args:
|
|
25
|
+
cluster_category: Cluster region argument. Defaults to 'prod'.
|
|
26
|
+
token_resolver: Async callable that resolves the auth token (REQUIRED).
|
|
27
|
+
use_s2s_endpoint: Use the S2S endpoint instead of standard endpoint.
|
|
28
|
+
max_queue_size: Maximum queue size for the batch processor. Default is 2048.
|
|
29
|
+
scheduled_delay_ms: Delay between export batches (ms). Default is 5000.
|
|
30
|
+
exporter_timeout_ms: Timeout for the export operation (ms). Default is 30000.
|
|
31
|
+
max_export_batch_size: Maximum batch size for export operations. Default is 512.
|
|
32
|
+
"""
|
|
33
|
+
self.cluster_category = cluster_category
|
|
34
|
+
self.token_resolver = token_resolver
|
|
35
|
+
self.use_s2s_endpoint = use_s2s_endpoint
|
|
36
|
+
self.max_queue_size = max_queue_size
|
|
37
|
+
self.scheduled_delay_ms = scheduled_delay_ms
|
|
38
|
+
self.exporter_timeout_ms = exporter_timeout_ms
|
|
39
|
+
self.max_export_batch_size = max_export_batch_size
|