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.
Files changed (33) hide show
  1. microsoft_agents_a365/observability/core/__init__.py +61 -0
  2. microsoft_agents_a365/observability/core/agent_details.py +42 -0
  3. microsoft_agents_a365/observability/core/config.py +246 -0
  4. microsoft_agents_a365/observability/core/constants.py +107 -0
  5. microsoft_agents_a365/observability/core/execute_tool_scope.py +88 -0
  6. microsoft_agents_a365/observability/core/execution_type.py +13 -0
  7. microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +310 -0
  8. microsoft_agents_a365/observability/core/exporters/utils.py +72 -0
  9. microsoft_agents_a365/observability/core/inference_call_details.py +18 -0
  10. microsoft_agents_a365/observability/core/inference_operation_type.py +11 -0
  11. microsoft_agents_a365/observability/core/inference_scope.py +140 -0
  12. microsoft_agents_a365/observability/core/invoke_agent_details.py +17 -0
  13. microsoft_agents_a365/observability/core/invoke_agent_scope.py +166 -0
  14. microsoft_agents_a365/observability/core/middleware/__init__.py +7 -0
  15. microsoft_agents_a365/observability/core/middleware/baggage_builder.py +319 -0
  16. microsoft_agents_a365/observability/core/middleware/turn_context_baggage.py +193 -0
  17. microsoft_agents_a365/observability/core/models/__init__.py +2 -0
  18. microsoft_agents_a365/observability/core/models/agent_type.py +25 -0
  19. microsoft_agents_a365/observability/core/models/caller_details.py +25 -0
  20. microsoft_agents_a365/observability/core/opentelemetry_scope.py +250 -0
  21. microsoft_agents_a365/observability/core/request.py +19 -0
  22. microsoft_agents_a365/observability/core/source_metadata.py +15 -0
  23. microsoft_agents_a365/observability/core/tenant_details.py +11 -0
  24. microsoft_agents_a365/observability/core/tool_call_details.py +18 -0
  25. microsoft_agents_a365/observability/core/tool_type.py +13 -0
  26. microsoft_agents_a365/observability/core/trace_processor/__init__.py +13 -0
  27. microsoft_agents_a365/observability/core/trace_processor/span_processor.py +75 -0
  28. microsoft_agents_a365/observability/core/trace_processor/util.py +44 -0
  29. microsoft_agents_a365/observability/core/utils.py +151 -0
  30. microsoft_agents_a365_observability_core-0.1.0.dist-info/METADATA +78 -0
  31. microsoft_agents_a365_observability_core-0.1.0.dist-info/RECORD +33 -0
  32. microsoft_agents_a365_observability_core-0.1.0.dist-info/WHEEL +5 -0
  33. 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"