microsoft-agents-a365-observability-core 0.1.0__py3-none-any.whl
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/__init__.py +61 -0
- microsoft_agents_a365/observability/core/agent_details.py +42 -0
- microsoft_agents_a365/observability/core/config.py +246 -0
- microsoft_agents_a365/observability/core/constants.py +107 -0
- microsoft_agents_a365/observability/core/execute_tool_scope.py +88 -0
- microsoft_agents_a365/observability/core/execution_type.py +13 -0
- microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +310 -0
- microsoft_agents_a365/observability/core/exporters/utils.py +72 -0
- microsoft_agents_a365/observability/core/inference_call_details.py +18 -0
- microsoft_agents_a365/observability/core/inference_operation_type.py +11 -0
- microsoft_agents_a365/observability/core/inference_scope.py +140 -0
- microsoft_agents_a365/observability/core/invoke_agent_details.py +17 -0
- microsoft_agents_a365/observability/core/invoke_agent_scope.py +166 -0
- microsoft_agents_a365/observability/core/middleware/__init__.py +7 -0
- microsoft_agents_a365/observability/core/middleware/baggage_builder.py +319 -0
- microsoft_agents_a365/observability/core/middleware/turn_context_baggage.py +193 -0
- microsoft_agents_a365/observability/core/models/__init__.py +2 -0
- microsoft_agents_a365/observability/core/models/agent_type.py +25 -0
- microsoft_agents_a365/observability/core/models/caller_details.py +25 -0
- microsoft_agents_a365/observability/core/opentelemetry_scope.py +250 -0
- microsoft_agents_a365/observability/core/request.py +19 -0
- microsoft_agents_a365/observability/core/source_metadata.py +15 -0
- microsoft_agents_a365/observability/core/tenant_details.py +11 -0
- microsoft_agents_a365/observability/core/tool_call_details.py +18 -0
- microsoft_agents_a365/observability/core/tool_type.py +13 -0
- microsoft_agents_a365/observability/core/trace_processor/__init__.py +13 -0
- microsoft_agents_a365/observability/core/trace_processor/span_processor.py +75 -0
- microsoft_agents_a365/observability/core/trace_processor/util.py +44 -0
- microsoft_agents_a365/observability/core/utils.py +151 -0
- microsoft_agents_a365_observability_core-0.1.0.dist-info/METADATA +78 -0
- microsoft_agents_a365_observability_core-0.1.0.dist-info/RECORD +33 -0
- microsoft_agents_a365_observability_core-0.1.0.dist-info/WHEEL +5 -0
- microsoft_agents_a365_observability_core-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
# Microsoft Agent 365 Python SDK for OpenTelemetry tracing.
|
|
4
|
+
|
|
5
|
+
from .agent_details import AgentDetails
|
|
6
|
+
from .config import (
|
|
7
|
+
configure,
|
|
8
|
+
get_tracer,
|
|
9
|
+
get_tracer_provider,
|
|
10
|
+
is_configured,
|
|
11
|
+
)
|
|
12
|
+
from .execute_tool_scope import ExecuteToolScope
|
|
13
|
+
from .execution_type import ExecutionType
|
|
14
|
+
from .inference_call_details import InferenceCallDetails
|
|
15
|
+
from .inference_operation_type import InferenceOperationType
|
|
16
|
+
from .inference_scope import InferenceScope
|
|
17
|
+
from .invoke_agent_details import InvokeAgentDetails
|
|
18
|
+
from .invoke_agent_scope import InvokeAgentScope
|
|
19
|
+
from .middleware.baggage_builder import BaggageBuilder
|
|
20
|
+
from .opentelemetry_scope import OpenTelemetryScope
|
|
21
|
+
from .request import Request
|
|
22
|
+
from .source_metadata import SourceMetadata
|
|
23
|
+
from .tenant_details import TenantDetails
|
|
24
|
+
from .tool_call_details import ToolCallDetails
|
|
25
|
+
from .tool_type import ToolType
|
|
26
|
+
from .trace_processor.span_processor import SpanProcessor
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Main SDK functions
|
|
30
|
+
"configure",
|
|
31
|
+
"is_configured",
|
|
32
|
+
"get_tracer",
|
|
33
|
+
"get_tracer_provider",
|
|
34
|
+
# Span processor
|
|
35
|
+
"SpanProcessor",
|
|
36
|
+
# Base scope class
|
|
37
|
+
"OpenTelemetryScope",
|
|
38
|
+
# Specific scope classes
|
|
39
|
+
"ExecuteToolScope",
|
|
40
|
+
"InvokeAgentScope",
|
|
41
|
+
"InferenceScope",
|
|
42
|
+
# Middleware
|
|
43
|
+
"BaggageBuilder",
|
|
44
|
+
# Data classes
|
|
45
|
+
"InvokeAgentDetails",
|
|
46
|
+
"AgentDetails",
|
|
47
|
+
"TenantDetails",
|
|
48
|
+
"ToolCallDetails",
|
|
49
|
+
"SourceMetadata",
|
|
50
|
+
"Request",
|
|
51
|
+
"InferenceCallDetails",
|
|
52
|
+
# Enums
|
|
53
|
+
"ExecutionType",
|
|
54
|
+
"InferenceOperationType",
|
|
55
|
+
"ToolType",
|
|
56
|
+
# Constants
|
|
57
|
+
# all constants from constants.py are exported via *
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
# This is a namespace package
|
|
61
|
+
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from .models.agent_type import AgentType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class AgentDetails:
|
|
12
|
+
"""Details about an AI agent in the system."""
|
|
13
|
+
|
|
14
|
+
agent_id: str
|
|
15
|
+
"""The unique identifier for the AI agent."""
|
|
16
|
+
|
|
17
|
+
agent_name: Optional[str] = None
|
|
18
|
+
"""The human-readable name of the AI agent."""
|
|
19
|
+
|
|
20
|
+
agent_description: Optional[str] = None
|
|
21
|
+
"""A description of the AI agent's purpose or capabilities."""
|
|
22
|
+
|
|
23
|
+
agent_auid: Optional[str] = None
|
|
24
|
+
"""Optional Agent User ID for the agent."""
|
|
25
|
+
|
|
26
|
+
agent_upn: Optional[str] = None
|
|
27
|
+
"""Optional User Principal Name (UPN) for the agent."""
|
|
28
|
+
|
|
29
|
+
agent_blueprint_id: Optional[str] = None
|
|
30
|
+
"""Optional Blueprint/Application ID for the agent."""
|
|
31
|
+
|
|
32
|
+
agent_type: Optional[AgentType] = None
|
|
33
|
+
"""The agent type."""
|
|
34
|
+
|
|
35
|
+
tenant_id: Optional[str] = None
|
|
36
|
+
"""Optional Tenant ID for the agent."""
|
|
37
|
+
|
|
38
|
+
conversation_id: Optional[str] = None
|
|
39
|
+
"""Optional conversation ID for compatibility."""
|
|
40
|
+
|
|
41
|
+
icon_uri: Optional[str] = None
|
|
42
|
+
"""Optional icon URI for the agent."""
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import threading
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
|
|
8
|
+
from opentelemetry import trace
|
|
9
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, SERVICE_NAMESPACE, Resource
|
|
10
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
11
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
|
|
12
|
+
|
|
13
|
+
from .exporters.agent365_exporter import Agent365Exporter
|
|
14
|
+
from .exporters.utils import is_agent365_exporter_enabled
|
|
15
|
+
from .trace_processor.span_processor import SpanProcessor
|
|
16
|
+
|
|
17
|
+
DEFAULT_LOGGER_NAME = __name__
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TelemetryManager:
|
|
21
|
+
"""
|
|
22
|
+
Thread-safe singleton manager for telemetry operations.
|
|
23
|
+
|
|
24
|
+
This class encapsulates all telemetry state and operations, providing
|
|
25
|
+
a clean interface for configuration, flushing, and shutdown without
|
|
26
|
+
relying on global variables.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
_instance: Optional["TelemetryManager"] = None
|
|
30
|
+
_lock = threading.Lock()
|
|
31
|
+
|
|
32
|
+
def __new__(cls) -> "TelemetryManager":
|
|
33
|
+
if cls._instance is None:
|
|
34
|
+
with cls._lock:
|
|
35
|
+
if cls._instance is None:
|
|
36
|
+
cls._instance = super().__new__(cls)
|
|
37
|
+
cls._instance._initialized = False
|
|
38
|
+
return cls._instance
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
if not getattr(self, "_initialized", False):
|
|
42
|
+
self._tracer_provider: TracerProvider | None = None
|
|
43
|
+
self._span_processors: dict[str, Any] = {}
|
|
44
|
+
self._logger = logging.getLogger(__name__)
|
|
45
|
+
self._initialized = True
|
|
46
|
+
|
|
47
|
+
def configure(
|
|
48
|
+
self,
|
|
49
|
+
service_name: str,
|
|
50
|
+
service_namespace: str,
|
|
51
|
+
logger_name: str = DEFAULT_LOGGER_NAME,
|
|
52
|
+
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
53
|
+
cluster_category: str = "prod",
|
|
54
|
+
**kwargs: Any,
|
|
55
|
+
) -> bool:
|
|
56
|
+
"""
|
|
57
|
+
Configure telemetry.
|
|
58
|
+
|
|
59
|
+
:param service_name: The name of the service.
|
|
60
|
+
:param service_namespace: The namespace of the service.
|
|
61
|
+
: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
|
+
:param cluster_category: Environment / cluster category (e.g., "preprod", "prod").
|
|
64
|
+
:return: True if configuration succeeded, False otherwise.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
with self._lock:
|
|
68
|
+
return self._configure_internal(
|
|
69
|
+
service_name,
|
|
70
|
+
service_namespace,
|
|
71
|
+
logger_name,
|
|
72
|
+
token_resolver,
|
|
73
|
+
cluster_category,
|
|
74
|
+
**kwargs,
|
|
75
|
+
)
|
|
76
|
+
except Exception as e:
|
|
77
|
+
self._logger.error(f"❌ Failed to configure telemetry: {e}")
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
def _configure_internal(
|
|
81
|
+
self,
|
|
82
|
+
service_name: str,
|
|
83
|
+
service_namespace: str,
|
|
84
|
+
logger_name: str,
|
|
85
|
+
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
86
|
+
cluster_category: str = "prod",
|
|
87
|
+
**kwargs: Any,
|
|
88
|
+
) -> bool:
|
|
89
|
+
"""Internal configuration method - not thread-safe, must be called with lock."""
|
|
90
|
+
|
|
91
|
+
# Create resource with service information
|
|
92
|
+
resource = Resource.create(
|
|
93
|
+
{
|
|
94
|
+
SERVICE_NAMESPACE: service_namespace,
|
|
95
|
+
SERVICE_NAME: service_name,
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Get existing tracer provider or create new one
|
|
100
|
+
try:
|
|
101
|
+
tracer_provider = trace.get_tracer_provider()
|
|
102
|
+
# Check if it's already configured
|
|
103
|
+
if hasattr(tracer_provider, "resource") and tracer_provider.resource:
|
|
104
|
+
# Already configured, just add our span processor
|
|
105
|
+
agent_processor = SpanProcessor()
|
|
106
|
+
tracer_provider.add_span_processor(agent_processor)
|
|
107
|
+
self._tracer_provider = tracer_provider
|
|
108
|
+
self._span_processors["agent"] = agent_processor
|
|
109
|
+
return True
|
|
110
|
+
except Exception:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
# Configure tracer provider
|
|
114
|
+
tracer_provider = TracerProvider(resource=resource)
|
|
115
|
+
trace.set_tracer_provider(tracer_provider)
|
|
116
|
+
self._tracer_provider = tracer_provider
|
|
117
|
+
|
|
118
|
+
if is_agent365_exporter_enabled() and token_resolver is not None:
|
|
119
|
+
exporter = Agent365Exporter(
|
|
120
|
+
token_resolver=token_resolver,
|
|
121
|
+
cluster_category=cluster_category,
|
|
122
|
+
**kwargs,
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
exporter = ConsoleSpanExporter()
|
|
126
|
+
self._logger.warning(
|
|
127
|
+
"is_agent365_exporter_enabled() not enabled or token_resolver not set.Falling back to console exporter."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Add span processors
|
|
131
|
+
|
|
132
|
+
# Create BatchSpanProcessor with optimized settings
|
|
133
|
+
batch_processor = BatchSpanProcessor(exporter)
|
|
134
|
+
agent_processor = SpanProcessor()
|
|
135
|
+
|
|
136
|
+
tracer_provider.add_span_processor(batch_processor)
|
|
137
|
+
tracer_provider.add_span_processor(agent_processor)
|
|
138
|
+
|
|
139
|
+
# Store references for cleanup
|
|
140
|
+
self._span_processors["batch"] = batch_processor
|
|
141
|
+
self._span_processors["agent"] = agent_processor
|
|
142
|
+
|
|
143
|
+
# Configure logging if logger_name is provided
|
|
144
|
+
if logger_name:
|
|
145
|
+
target_logger = logging.getLogger(logger_name)
|
|
146
|
+
target_logger.setLevel(logging.INFO)
|
|
147
|
+
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
def is_configured(self) -> bool:
|
|
151
|
+
"""Check if the telemetry manager is configured."""
|
|
152
|
+
return self._tracer_provider is not None
|
|
153
|
+
|
|
154
|
+
def get_tracer(self, name: str | None = None, version: str | None = None):
|
|
155
|
+
"""
|
|
156
|
+
Return an OpenTelemetry Tracer tied to the TracerProvider configured by agent365.
|
|
157
|
+
|
|
158
|
+
If the telemetry manager is not configured yet, this returns the default
|
|
159
|
+
(no-op) tracer from OpenTelemetry and logs a warning. Callers should prefer
|
|
160
|
+
to call `configure(...)` during application startup so the tracer
|
|
161
|
+
returned is backed by the configured TracerProvider.
|
|
162
|
+
|
|
163
|
+
:param name: Optional tracer name. Defaults to 'microsoft_agents_a365.observability.core' when not provided.
|
|
164
|
+
:param version: Optional tracer version.
|
|
165
|
+
:return: An OpenTelemetry Tracer instance.
|
|
166
|
+
"""
|
|
167
|
+
tracer_name = name or DEFAULT_LOGGER_NAME
|
|
168
|
+
if self._tracer_provider is None:
|
|
169
|
+
# Not configured — return whatever tracer OpenTelemetry provides (no-op)
|
|
170
|
+
self._logger.warning(
|
|
171
|
+
"agent365 telemetry not configured; returning a no-op tracer for '%s'", tracer_name
|
|
172
|
+
)
|
|
173
|
+
return trace.get_tracer(tracer_name, version)
|
|
174
|
+
|
|
175
|
+
# Ensure global tracer provider is set (should already be by configure)
|
|
176
|
+
try:
|
|
177
|
+
return trace.get_tracer(tracer_name, version)
|
|
178
|
+
except Exception as e:
|
|
179
|
+
self._logger.error("Failed to get tracer '%s': %s", tracer_name, e)
|
|
180
|
+
return trace.get_tracer(tracer_name, version)
|
|
181
|
+
|
|
182
|
+
def get_tracer_provider(self):
|
|
183
|
+
"""
|
|
184
|
+
Return the configured TracerProvider instance.
|
|
185
|
+
"""
|
|
186
|
+
return self._tracer_provider
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# Singleton instance
|
|
190
|
+
_telemetry_manager = TelemetryManager()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Public API functions that delegate to the singleton manager
|
|
194
|
+
def configure(
|
|
195
|
+
service_name: str,
|
|
196
|
+
service_namespace: str,
|
|
197
|
+
logger_name: str = DEFAULT_LOGGER_NAME,
|
|
198
|
+
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
199
|
+
cluster_category: str = "prod",
|
|
200
|
+
**kwargs: Any,
|
|
201
|
+
) -> bool:
|
|
202
|
+
"""
|
|
203
|
+
Configure telemetry with OpenTelemetry.
|
|
204
|
+
|
|
205
|
+
:param service_name: The name of the service.
|
|
206
|
+
:param service_namespace: The namespace of the service.
|
|
207
|
+
:param logger_name: The name of the logger to collect telemetry from.
|
|
208
|
+
:return: True if configuration succeeded, False otherwise.
|
|
209
|
+
"""
|
|
210
|
+
return _telemetry_manager.configure(
|
|
211
|
+
service_name,
|
|
212
|
+
service_namespace,
|
|
213
|
+
logger_name,
|
|
214
|
+
token_resolver,
|
|
215
|
+
cluster_category,
|
|
216
|
+
**kwargs,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def is_configured() -> bool:
|
|
221
|
+
"""
|
|
222
|
+
Check if telemetry is currently configured.
|
|
223
|
+
|
|
224
|
+
:return: True if telemetry is configured and ready to use, False otherwise
|
|
225
|
+
"""
|
|
226
|
+
return _telemetry_manager.is_configured()
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def get_tracer(name: str | None = None, version: str | None = None):
|
|
230
|
+
"""
|
|
231
|
+
Return a tracer tied to the TracerProvider configured by the SDK.
|
|
232
|
+
|
|
233
|
+
:param name: Optional tracer name. If omitted, defaults to 'microsoft_agents_a365.observability.core'.
|
|
234
|
+
:param version: Optional tracer version.
|
|
235
|
+
:return: An OpenTelemetry Tracer (may be a no-op tracer if SDK isn't configured).
|
|
236
|
+
"""
|
|
237
|
+
return _telemetry_manager.get_tracer(name, version)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def get_tracer_provider():
|
|
241
|
+
"""
|
|
242
|
+
Return the TracerProvider configured by the SDK.
|
|
243
|
+
|
|
244
|
+
:return: Configured OpenTelemetry TracerProvider
|
|
245
|
+
"""
|
|
246
|
+
return _telemetry_manager.get_tracer_provider()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
# Constants for SDK OpenTelemetry implementation.
|
|
4
|
+
|
|
5
|
+
# Span operation names
|
|
6
|
+
INVOKE_AGENT_OPERATION_NAME = "invoke_agent"
|
|
7
|
+
EXECUTE_TOOL_OPERATION_NAME = "execute_tool"
|
|
8
|
+
|
|
9
|
+
# OpenTelemetry semantic conventions
|
|
10
|
+
ERROR_TYPE_KEY = "error.type"
|
|
11
|
+
ERROR_MESSAGE_KEY = "error.message"
|
|
12
|
+
AZ_NAMESPACE_KEY = "az.namespace"
|
|
13
|
+
SERVER_ADDRESS_KEY = "server.address"
|
|
14
|
+
SERVER_PORT_KEY = "server.port"
|
|
15
|
+
AZURE_RP_NAMESPACE_VALUE = "Microsoft.CognitiveServices"
|
|
16
|
+
SOURCE_NAME = "Agent365Sdk"
|
|
17
|
+
ENABLE_OPENTELEMETRY_SWITCH = "Azure.Experimental.EnableActivitySource"
|
|
18
|
+
TRACE_CONTENTS_SWITCH = "Azure.Experimental.TraceGenAIMessageContent"
|
|
19
|
+
TRACE_CONTENTS_ENVIRONMENT_VARIABLE = "AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"
|
|
20
|
+
ENABLE_OBSERVABILITY = "ENABLE_OBSERVABILITY"
|
|
21
|
+
ENABLE_A365_OBSERVABILITY_EXPORTER = "ENABLE_A365_OBSERVABILITY_EXPORTER"
|
|
22
|
+
ENABLE_A365_OBSERVABILITY = "ENABLE_A365_OBSERVABILITY"
|
|
23
|
+
|
|
24
|
+
# GenAI semantic conventions
|
|
25
|
+
GEN_AI_CLIENT_OPERATION_DURATION_METRIC_NAME = "gen_ai.client.operation.duration"
|
|
26
|
+
GEN_AI_CLIENT_TOKEN_USAGE_METRIC_NAME = "gen_ai.client.token.usage"
|
|
27
|
+
GEN_AI_OPERATION_NAME_KEY = "gen_ai.operation.name"
|
|
28
|
+
GEN_AI_REQUEST_MAX_TOKENS_KEY = "gen_ai.request.max_tokens"
|
|
29
|
+
GEN_AI_REQUEST_MODEL_KEY = "gen_ai.request.model"
|
|
30
|
+
GEN_AI_REQUEST_TEMPERATURE_KEY = "gen_ai.request.temperature"
|
|
31
|
+
GEN_AI_REQUEST_TOP_P_KEY = "gen_ai.request.top_p"
|
|
32
|
+
GEN_AI_RESPONSE_ID_KEY = "gen_ai.response.id"
|
|
33
|
+
GEN_AI_RESPONSE_FINISH_REASONS_KEY = "gen_ai.response.finish_reasons"
|
|
34
|
+
GEN_AI_RESPONSE_MODEL_KEY = "gen_ai.response.model"
|
|
35
|
+
GEN_AI_SYSTEM_KEY = "gen_ai.system"
|
|
36
|
+
GEN_AI_SYSTEM_VALUE = "az.ai.agent365"
|
|
37
|
+
GEN_AI_THOUGHT_PROCESS_KEY = "gen_ai.agent.thought.process"
|
|
38
|
+
|
|
39
|
+
GEN_AI_AGENT_ID_KEY = "gen_ai.agent.id"
|
|
40
|
+
GEN_AI_AGENT_NAME_KEY = "gen_ai.agent.name"
|
|
41
|
+
GEN_AI_AGENT_DESCRIPTION_KEY = "gen_ai.agent.description"
|
|
42
|
+
GEN_AI_CONVERSATION_ID_KEY = "gen_ai.conversation.id"
|
|
43
|
+
GEN_AI_CONVERSATION_ITEM_LINK_KEY = "gen_ai.conversation.item.link"
|
|
44
|
+
GEN_AI_TOKEN_TYPE_KEY = "gen_ai.token.type"
|
|
45
|
+
GEN_AI_USAGE_INPUT_TOKENS_KEY = "gen_ai.usage.input_tokens"
|
|
46
|
+
GEN_AI_USAGE_OUTPUT_TOKENS_KEY = "gen_ai.usage.output_tokens"
|
|
47
|
+
GEN_AI_CHOICE = "gen_ai.choice"
|
|
48
|
+
GEN_AI_PROVIDER_NAME_KEY = "gen_ai.provider.name"
|
|
49
|
+
GEN_AI_AGENT_TYPE_KEY = "gen_ai.agent.type"
|
|
50
|
+
|
|
51
|
+
GEN_AI_SYSTEM_INSTRUCTIONS_KEY = "gen_ai.system_instructions"
|
|
52
|
+
GEN_AI_INPUT_MESSAGES_KEY = "gen_ai.input.messages"
|
|
53
|
+
GEN_AI_OUTPUT_MESSAGES_KEY = "gen_ai.output.messages"
|
|
54
|
+
GEN_AI_EVENT_CONTENT = "gen_ai.event.content"
|
|
55
|
+
|
|
56
|
+
# Tool execution constants
|
|
57
|
+
GEN_AI_TOOL_CALL_ID_KEY = "gen_ai.tool.call.id"
|
|
58
|
+
GEN_AI_TOOL_NAME_KEY = "gen_ai.tool.name"
|
|
59
|
+
GEN_AI_TOOL_DESCRIPTION_KEY = "gen_ai.tool.description"
|
|
60
|
+
GEN_AI_TOOL_ARGS_KEY = "gen_ai.tool.arguments"
|
|
61
|
+
GEN_AI_TOOL_CALL_RESULT_KEY = GEN_AI_EVENT_CONTENT # GEN_AI_EVENT_CONTENT
|
|
62
|
+
GEN_AI_TOOL_TYPE_KEY = "gen_ai.tool.type"
|
|
63
|
+
|
|
64
|
+
# Agent user(user tied to agent instance during creation) or caller dimensions
|
|
65
|
+
GEN_AI_AGENT_USER_ID_KEY = "gen_ai.agent.userid"
|
|
66
|
+
GEN_AI_CALLER_USER_ID_KEY = "gen_ai.caller.userid"
|
|
67
|
+
GEN_AI_CALLER_TENANT_ID_KEY = "gen_ai.caller.tenantid"
|
|
68
|
+
GEN_AI_CALLER_ID_KEY = "gen_ai.caller.id"
|
|
69
|
+
GEN_AI_CALLER_NAME_KEY = "gen_ai.caller.name"
|
|
70
|
+
GEN_AI_CALLER_UPN_KEY = "gen_ai.caller.upn"
|
|
71
|
+
|
|
72
|
+
# Agent to Agent caller agent dimensions
|
|
73
|
+
GEN_AI_CALLER_AGENT_USER_ID_KEY = "gen_ai.caller.agent.userid"
|
|
74
|
+
GEN_AI_CALLER_AGENT_UPN_KEY = "gen_ai.caller.agent.upn"
|
|
75
|
+
GEN_AI_CALLER_AGENT_TENANT_ID_KEY = "gen_ai.caller.agent.tenantid"
|
|
76
|
+
GEN_AI_CALLER_AGENT_NAME_KEY = "gen_ai.caller.agent.name"
|
|
77
|
+
GEN_AI_CALLER_AGENT_ID_KEY = "gen_ai.caller.agent.id"
|
|
78
|
+
GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = "gen_ai.caller.agent.applicationid"
|
|
79
|
+
GEN_AI_CALLER_AGENT_TYPE_KEY = "gen_ai.caller.agent.type"
|
|
80
|
+
|
|
81
|
+
# Agent-specific dimensions
|
|
82
|
+
AGENT_ID_KEY = "gen_ai.agent.id"
|
|
83
|
+
GEN_AI_TASK_ID_KEY = "gen_ai.task.id"
|
|
84
|
+
SESSION_ID_KEY = "session.id"
|
|
85
|
+
GEN_AI_ICON_URI_KEY = "gen_ai.agent365.icon_uri"
|
|
86
|
+
TENANT_ID_KEY = "tenant.id"
|
|
87
|
+
|
|
88
|
+
# Baggage keys
|
|
89
|
+
OPERATION_SOURCE_KEY = "operation.source"
|
|
90
|
+
GEN_AI_AGENT_AUID_KEY = "gen_ai.agent.user.id"
|
|
91
|
+
GEN_AI_AGENT_UPN_KEY = "gen_ai.agent.upn"
|
|
92
|
+
GEN_AI_AGENT_BLUEPRINT_ID_KEY = "gen_ai.agent.applicationid"
|
|
93
|
+
CORRELATION_ID_KEY = "correlation.id"
|
|
94
|
+
HIRING_MANAGER_ID_KEY = "hiring.manager.id"
|
|
95
|
+
|
|
96
|
+
# Execution context dimensions
|
|
97
|
+
GEN_AI_EXECUTION_TYPE_KEY = "gen_ai.execution.type"
|
|
98
|
+
GEN_AI_EXECUTION_PAYLOAD_KEY = "gen_ai.execution.payload"
|
|
99
|
+
|
|
100
|
+
# Source metadata dimensions
|
|
101
|
+
GEN_AI_EXECUTION_SOURCE_ID_KEY = "gen_ai.execution.sourceMetadata.id"
|
|
102
|
+
GEN_AI_EXECUTION_SOURCE_NAME_KEY = "gen_ai.channel.name"
|
|
103
|
+
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY = "gen_ai.channel.link"
|
|
104
|
+
|
|
105
|
+
# custom parent id and parent name key
|
|
106
|
+
CUSTOM_PARENT_SPAN_ID_KEY = "custom.parent.span.id"
|
|
107
|
+
CUSTOM_SPAN_NAME_KEY = "custom.span.name"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from .agent_details import AgentDetails
|
|
5
|
+
from .constants import (
|
|
6
|
+
EXECUTE_TOOL_OPERATION_NAME,
|
|
7
|
+
GEN_AI_EVENT_CONTENT,
|
|
8
|
+
GEN_AI_TOOL_ARGS_KEY,
|
|
9
|
+
GEN_AI_TOOL_CALL_ID_KEY,
|
|
10
|
+
GEN_AI_TOOL_DESCRIPTION_KEY,
|
|
11
|
+
GEN_AI_TOOL_NAME_KEY,
|
|
12
|
+
GEN_AI_TOOL_TYPE_KEY,
|
|
13
|
+
SERVER_ADDRESS_KEY,
|
|
14
|
+
SERVER_PORT_KEY,
|
|
15
|
+
)
|
|
16
|
+
from .opentelemetry_scope import OpenTelemetryScope
|
|
17
|
+
from .tenant_details import TenantDetails
|
|
18
|
+
from .tool_call_details import ToolCallDetails
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ExecuteToolScope(OpenTelemetryScope):
|
|
22
|
+
"""Provides OpenTelemetry tracing scope for AI tool execution operations."""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def start(
|
|
26
|
+
details: ToolCallDetails,
|
|
27
|
+
agent_details: AgentDetails,
|
|
28
|
+
tenant_details: TenantDetails,
|
|
29
|
+
) -> "ExecuteToolScope":
|
|
30
|
+
"""Creates and starts a new scope for tool execution tracing.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
details: The details of the tool call
|
|
34
|
+
agent_details: The details of the agent making the call
|
|
35
|
+
tenant_details: The details of the tenant
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
A new ExecuteToolScope instance
|
|
39
|
+
"""
|
|
40
|
+
return ExecuteToolScope(details, agent_details, tenant_details)
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
details: ToolCallDetails,
|
|
45
|
+
agent_details: AgentDetails,
|
|
46
|
+
tenant_details: TenantDetails,
|
|
47
|
+
):
|
|
48
|
+
"""Initialize the tool execution scope.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
details: The details of the tool call
|
|
52
|
+
agent_details: The details of the agent making the call
|
|
53
|
+
tenant_details: The details of the tenant
|
|
54
|
+
"""
|
|
55
|
+
super().__init__(
|
|
56
|
+
kind="Internal",
|
|
57
|
+
operation_name=EXECUTE_TOOL_OPERATION_NAME,
|
|
58
|
+
activity_name=f"{EXECUTE_TOOL_OPERATION_NAME} {details.tool_name}",
|
|
59
|
+
agent_details=agent_details,
|
|
60
|
+
tenant_details=tenant_details,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Extract details using deconstruction-like approach
|
|
64
|
+
tool_name = details.tool_name
|
|
65
|
+
arguments = details.arguments
|
|
66
|
+
tool_call_id = details.tool_call_id
|
|
67
|
+
description = details.description
|
|
68
|
+
tool_type = details.tool_type
|
|
69
|
+
endpoint = details.endpoint
|
|
70
|
+
|
|
71
|
+
self.set_tag_maybe(GEN_AI_TOOL_NAME_KEY, tool_name)
|
|
72
|
+
self.set_tag_maybe(GEN_AI_TOOL_ARGS_KEY, arguments)
|
|
73
|
+
self.set_tag_maybe(GEN_AI_TOOL_TYPE_KEY, tool_type)
|
|
74
|
+
self.set_tag_maybe(GEN_AI_TOOL_CALL_ID_KEY, tool_call_id)
|
|
75
|
+
self.set_tag_maybe(GEN_AI_TOOL_DESCRIPTION_KEY, description)
|
|
76
|
+
|
|
77
|
+
if endpoint:
|
|
78
|
+
self.set_tag_maybe(SERVER_ADDRESS_KEY, endpoint.hostname)
|
|
79
|
+
if endpoint.port and endpoint.port != 443:
|
|
80
|
+
self.set_tag_maybe(SERVER_PORT_KEY, endpoint.port)
|
|
81
|
+
|
|
82
|
+
def record_response(self, response: str) -> None:
|
|
83
|
+
"""Records response information for telemetry tracking.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
response: The response to record
|
|
87
|
+
"""
|
|
88
|
+
self.set_tag_maybe(GEN_AI_EVENT_CONTENT, response)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
# Execution type enum.
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ExecutionType(Enum):
|
|
9
|
+
"""Enumeration for different types of agent execution contexts."""
|
|
10
|
+
|
|
11
|
+
AGENT_TO_AGENT = "Agent2Agent"
|
|
12
|
+
EVENT_TO_AGENT = "EventToAgent"
|
|
13
|
+
HUMAN_TO_AGENT = "HumanToAgent"
|