microsoft-agents-a365-observability-core 0.1.0__py3-none-any.whl → 0.2.0.dev5__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/agent_details.py +7 -4
- microsoft_agents_a365/observability/core/config.py +38 -7
- microsoft_agents_a365/observability/core/constants.py +3 -1
- microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +5 -1
- microsoft_agents_a365/observability/core/exporters/agent365_exporter_options.py +39 -0
- microsoft_agents_a365/observability/core/exporters/utils.py +77 -0
- microsoft_agents_a365/observability/core/invoke_agent_scope.py +12 -3
- microsoft_agents_a365/observability/core/middleware/baggage_builder.py +40 -11
- microsoft_agents_a365/observability/core/middleware/turn_context_baggage.py +31 -32
- microsoft_agents_a365/observability/core/models/operation_source.py +21 -0
- microsoft_agents_a365/observability/core/request.py +0 -1
- microsoft_agents_a365/observability/core/trace_processor/span_processor.py +10 -1
- microsoft_agents_a365/observability/core/trace_processor/util.py +7 -4
- microsoft_agents_a365/observability/core/utils.py +53 -4
- {microsoft_agents_a365_observability_core-0.1.0.dist-info → microsoft_agents_a365_observability_core-0.2.0.dev5.dist-info}/METADATA +3 -3
- {microsoft_agents_a365_observability_core-0.1.0.dist-info → microsoft_agents_a365_observability_core-0.2.0.dev5.dist-info}/RECORD +18 -16
- {microsoft_agents_a365_observability_core-0.1.0.dist-info → microsoft_agents_a365_observability_core-0.2.0.dev5.dist-info}/WHEEL +0 -0
- {microsoft_agents_a365_observability_core-0.1.0.dist-info → microsoft_agents_a365_observability_core-0.2.0.dev5.dist-info}/top_level.txt +0 -0
|
@@ -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."""
|
|
@@ -11,6 +11,7 @@ from opentelemetry.sdk.trace import TracerProvider
|
|
|
11
11
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
|
|
12
12
|
|
|
13
13
|
from .exporters.agent365_exporter import Agent365Exporter
|
|
14
|
+
from .exporters.agent365_exporter_options import Agent365ExporterOptions
|
|
14
15
|
from .exporters.utils import is_agent365_exporter_enabled
|
|
15
16
|
from .trace_processor.span_processor import SpanProcessor
|
|
16
17
|
|
|
@@ -51,6 +52,7 @@ class TelemetryManager:
|
|
|
51
52
|
logger_name: str = DEFAULT_LOGGER_NAME,
|
|
52
53
|
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
53
54
|
cluster_category: str = "prod",
|
|
55
|
+
exporter_options: Optional[Agent365ExporterOptions] = None,
|
|
54
56
|
**kwargs: Any,
|
|
55
57
|
) -> bool:
|
|
56
58
|
"""
|
|
@@ -59,8 +61,12 @@ class TelemetryManager:
|
|
|
59
61
|
:param service_name: The name of the service.
|
|
60
62
|
:param service_namespace: The namespace of the service.
|
|
61
63
|
: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
|
-
|
|
64
|
+
:param token_resolver: (Deprecated) Callable that returns an auth token for a given agent + tenant.
|
|
65
|
+
Use exporter_options instead.
|
|
66
|
+
:param cluster_category: (Deprecated) Environment / cluster category (e.g. "prod").
|
|
67
|
+
Use exporter_options instead.
|
|
68
|
+
:param exporter_options: Agent365ExporterOptions instance for configuring the exporter.
|
|
69
|
+
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.
|
|
64
70
|
:return: True if configuration succeeded, False otherwise.
|
|
65
71
|
"""
|
|
66
72
|
try:
|
|
@@ -71,6 +77,7 @@ class TelemetryManager:
|
|
|
71
77
|
logger_name,
|
|
72
78
|
token_resolver,
|
|
73
79
|
cluster_category,
|
|
80
|
+
exporter_options,
|
|
74
81
|
**kwargs,
|
|
75
82
|
)
|
|
76
83
|
except Exception as e:
|
|
@@ -84,6 +91,7 @@ class TelemetryManager:
|
|
|
84
91
|
logger_name: str,
|
|
85
92
|
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
86
93
|
cluster_category: str = "prod",
|
|
94
|
+
exporter_options: Optional[Agent365ExporterOptions] = None,
|
|
87
95
|
**kwargs: Any,
|
|
88
96
|
) -> bool:
|
|
89
97
|
"""Internal configuration method - not thread-safe, must be called with lock."""
|
|
@@ -115,11 +123,26 @@ class TelemetryManager:
|
|
|
115
123
|
trace.set_tracer_provider(tracer_provider)
|
|
116
124
|
self._tracer_provider = tracer_provider
|
|
117
125
|
|
|
118
|
-
if
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
# Use exporter_options if provided, otherwise create default options with legacy parameters
|
|
127
|
+
if exporter_options is None:
|
|
128
|
+
exporter_options = Agent365ExporterOptions(
|
|
121
129
|
cluster_category=cluster_category,
|
|
122
|
-
|
|
130
|
+
token_resolver=token_resolver,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Extract configuration for BatchSpanProcessor
|
|
134
|
+
batch_processor_kwargs = {
|
|
135
|
+
"max_queue_size": exporter_options.max_queue_size,
|
|
136
|
+
"schedule_delay_millis": exporter_options.scheduled_delay_ms,
|
|
137
|
+
"export_timeout_millis": exporter_options.exporter_timeout_ms,
|
|
138
|
+
"max_export_batch_size": exporter_options.max_export_batch_size,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if is_agent365_exporter_enabled() and exporter_options.token_resolver is not None:
|
|
142
|
+
exporter = Agent365Exporter(
|
|
143
|
+
token_resolver=exporter_options.token_resolver,
|
|
144
|
+
cluster_category=exporter_options.cluster_category,
|
|
145
|
+
use_s2s_endpoint=exporter_options.use_s2s_endpoint,
|
|
123
146
|
)
|
|
124
147
|
else:
|
|
125
148
|
exporter = ConsoleSpanExporter()
|
|
@@ -130,7 +153,7 @@ class TelemetryManager:
|
|
|
130
153
|
# Add span processors
|
|
131
154
|
|
|
132
155
|
# Create BatchSpanProcessor with optimized settings
|
|
133
|
-
batch_processor = BatchSpanProcessor(exporter)
|
|
156
|
+
batch_processor = BatchSpanProcessor(exporter, **batch_processor_kwargs)
|
|
134
157
|
agent_processor = SpanProcessor()
|
|
135
158
|
|
|
136
159
|
tracer_provider.add_span_processor(batch_processor)
|
|
@@ -197,6 +220,7 @@ def configure(
|
|
|
197
220
|
logger_name: str = DEFAULT_LOGGER_NAME,
|
|
198
221
|
token_resolver: Callable[[str, str], str | None] | None = None,
|
|
199
222
|
cluster_category: str = "prod",
|
|
223
|
+
exporter_options: Optional[Agent365ExporterOptions] = None,
|
|
200
224
|
**kwargs: Any,
|
|
201
225
|
) -> bool:
|
|
202
226
|
"""
|
|
@@ -205,6 +229,12 @@ def configure(
|
|
|
205
229
|
:param service_name: The name of the service.
|
|
206
230
|
:param service_namespace: The namespace of the service.
|
|
207
231
|
:param logger_name: The name of the logger to collect telemetry from.
|
|
232
|
+
:param token_resolver: (Deprecated) Callable that returns an auth token for a given agent + tenant.
|
|
233
|
+
Use exporter_options instead.
|
|
234
|
+
:param cluster_category: (Deprecated) Environment / cluster category (e.g. "prod").
|
|
235
|
+
Use exporter_options instead.
|
|
236
|
+
:param exporter_options: Agent365ExporterOptions instance for configuring the exporter.
|
|
237
|
+
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
238
|
:return: True if configuration succeeded, False otherwise.
|
|
209
239
|
"""
|
|
210
240
|
return _telemetry_manager.configure(
|
|
@@ -213,6 +243,7 @@ def configure(
|
|
|
213
243
|
logger_name,
|
|
214
244
|
token_resolver,
|
|
215
245
|
cluster_category,
|
|
246
|
+
exporter_options,
|
|
216
247
|
**kwargs,
|
|
217
248
|
)
|
|
218
249
|
|
|
@@ -68,6 +68,7 @@ GEN_AI_CALLER_TENANT_ID_KEY = "gen_ai.caller.tenantid"
|
|
|
68
68
|
GEN_AI_CALLER_ID_KEY = "gen_ai.caller.id"
|
|
69
69
|
GEN_AI_CALLER_NAME_KEY = "gen_ai.caller.name"
|
|
70
70
|
GEN_AI_CALLER_UPN_KEY = "gen_ai.caller.upn"
|
|
71
|
+
GEN_AI_CALLER_CLIENT_IP_KEY = "gen_ai.caller.client.ip"
|
|
71
72
|
|
|
72
73
|
# Agent to Agent caller agent dimensions
|
|
73
74
|
GEN_AI_CALLER_AGENT_USER_ID_KEY = "gen_ai.caller.agent.userid"
|
|
@@ -77,6 +78,7 @@ GEN_AI_CALLER_AGENT_NAME_KEY = "gen_ai.caller.agent.name"
|
|
|
77
78
|
GEN_AI_CALLER_AGENT_ID_KEY = "gen_ai.caller.agent.id"
|
|
78
79
|
GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = "gen_ai.caller.agent.applicationid"
|
|
79
80
|
GEN_AI_CALLER_AGENT_TYPE_KEY = "gen_ai.caller.agent.type"
|
|
81
|
+
GEN_AI_CALLER_AGENT_USER_CLIENT_IP = "gen_ai.caller.agent.user.client.ip"
|
|
80
82
|
|
|
81
83
|
# Agent-specific dimensions
|
|
82
84
|
AGENT_ID_KEY = "gen_ai.agent.id"
|
|
@@ -92,13 +94,13 @@ GEN_AI_AGENT_UPN_KEY = "gen_ai.agent.upn"
|
|
|
92
94
|
GEN_AI_AGENT_BLUEPRINT_ID_KEY = "gen_ai.agent.applicationid"
|
|
93
95
|
CORRELATION_ID_KEY = "correlation.id"
|
|
94
96
|
HIRING_MANAGER_ID_KEY = "hiring.manager.id"
|
|
97
|
+
SESSION_DESCRIPTION_KEY = "session.description"
|
|
95
98
|
|
|
96
99
|
# Execution context dimensions
|
|
97
100
|
GEN_AI_EXECUTION_TYPE_KEY = "gen_ai.execution.type"
|
|
98
101
|
GEN_AI_EXECUTION_PAYLOAD_KEY = "gen_ai.execution.payload"
|
|
99
102
|
|
|
100
103
|
# Source metadata dimensions
|
|
101
|
-
GEN_AI_EXECUTION_SOURCE_ID_KEY = "gen_ai.execution.sourceMetadata.id"
|
|
102
104
|
GEN_AI_EXECUTION_SOURCE_NAME_KEY = "gen_ai.channel.name"
|
|
103
105
|
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY = "gen_ai.channel.link"
|
|
104
106
|
|
|
@@ -23,6 +23,7 @@ from .utils import (
|
|
|
23
23
|
kind_name,
|
|
24
24
|
partition_by_identity,
|
|
25
25
|
status_name,
|
|
26
|
+
truncate_span,
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
# ---- Exporter ---------------------------------------------------------------
|
|
@@ -295,7 +296,7 @@ class Agent365Exporter(SpanExporter):
|
|
|
295
296
|
start_ns = sp.start_time
|
|
296
297
|
end_ns = sp.end_time
|
|
297
298
|
|
|
298
|
-
|
|
299
|
+
span_dict = {
|
|
299
300
|
"traceId": hex_trace_id(ctx.trace_id),
|
|
300
301
|
"spanId": hex_span_id(ctx.span_id),
|
|
301
302
|
"parentSpanId": parent_span_id,
|
|
@@ -308,3 +309,6 @@ class Agent365Exporter(SpanExporter):
|
|
|
308
309
|
"links": links,
|
|
309
310
|
"status": status,
|
|
310
311
|
}
|
|
312
|
+
|
|
313
|
+
# Apply truncation if needed
|
|
314
|
+
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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Copyright (c) Microsoft. All rights reserved.
|
|
2
2
|
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
3
5
|
import os
|
|
4
6
|
from collections.abc import Sequence
|
|
5
7
|
from typing import Any
|
|
@@ -13,6 +15,11 @@ from ..constants import (
|
|
|
13
15
|
TENANT_ID_KEY,
|
|
14
16
|
)
|
|
15
17
|
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# Maximum allowed span size in bytes (250KB)
|
|
21
|
+
MAX_SPAN_SIZE_BYTES = 250 * 1024
|
|
22
|
+
|
|
16
23
|
|
|
17
24
|
def hex_trace_id(value: int) -> str:
|
|
18
25
|
# 128-bit -> 32 hex chars
|
|
@@ -46,6 +53,76 @@ def status_name(code: StatusCode) -> str:
|
|
|
46
53
|
return str(code)
|
|
47
54
|
|
|
48
55
|
|
|
56
|
+
def truncate_span(span_dict: dict[str, Any]) -> dict[str, Any]:
|
|
57
|
+
"""
|
|
58
|
+
Truncate span attributes if the serialized span exceeds MAX_SPAN_SIZE_BYTES.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
span_dict: The span dictionary to potentially truncate
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The potentially truncated span dictionary
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
# Serialize the span to check its size
|
|
68
|
+
serialized = json.dumps(span_dict, separators=(",", ":"))
|
|
69
|
+
current_size = len(serialized.encode("utf-8"))
|
|
70
|
+
|
|
71
|
+
if current_size <= MAX_SPAN_SIZE_BYTES:
|
|
72
|
+
return span_dict
|
|
73
|
+
|
|
74
|
+
logger.warning(
|
|
75
|
+
f"Span size ({current_size} bytes) exceeds limit ({MAX_SPAN_SIZE_BYTES} bytes). "
|
|
76
|
+
"Truncating large payload attributes."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Create a deep copy to modify (shallow copy would still reference original attributes)
|
|
80
|
+
truncated_span = span_dict.copy()
|
|
81
|
+
if "attributes" in truncated_span:
|
|
82
|
+
truncated_span["attributes"] = truncated_span["attributes"].copy()
|
|
83
|
+
attributes = truncated_span.get("attributes", {})
|
|
84
|
+
|
|
85
|
+
# Track what was truncated for logging
|
|
86
|
+
truncated_keys = []
|
|
87
|
+
|
|
88
|
+
# Sort attributes by size (largest first) and truncate until size is acceptable
|
|
89
|
+
if attributes:
|
|
90
|
+
# Calculate size of each attribute value when serialized
|
|
91
|
+
attr_sizes = []
|
|
92
|
+
for key, value in attributes.items():
|
|
93
|
+
try:
|
|
94
|
+
value_size = len(json.dumps(value, separators=(",", ":")).encode("utf-8"))
|
|
95
|
+
attr_sizes.append((key, value_size))
|
|
96
|
+
except Exception:
|
|
97
|
+
# If we can't serialize the value, assume it's small
|
|
98
|
+
attr_sizes.append((key, 0))
|
|
99
|
+
|
|
100
|
+
# Sort by size (descending - largest first)
|
|
101
|
+
attr_sizes.sort(key=lambda x: x[1], reverse=True)
|
|
102
|
+
|
|
103
|
+
# Truncate largest attributes first until size is acceptable
|
|
104
|
+
for key, _ in attr_sizes:
|
|
105
|
+
if key in attributes:
|
|
106
|
+
attributes[key] = "TRUNCATED"
|
|
107
|
+
truncated_keys.append(key)
|
|
108
|
+
|
|
109
|
+
# Check size after truncation
|
|
110
|
+
serialized = json.dumps(truncated_span, separators=(",", ":"))
|
|
111
|
+
current_size = len(serialized.encode("utf-8"))
|
|
112
|
+
|
|
113
|
+
if current_size <= MAX_SPAN_SIZE_BYTES:
|
|
114
|
+
break
|
|
115
|
+
|
|
116
|
+
if truncated_keys:
|
|
117
|
+
logger.info(f"Truncated attributes: {', '.join(truncated_keys)}")
|
|
118
|
+
|
|
119
|
+
return truncated_span
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.error(f"Error during span truncation: {e}")
|
|
123
|
+
return span_dict
|
|
124
|
+
|
|
125
|
+
|
|
49
126
|
def partition_by_identity(
|
|
50
127
|
spans: Sequence[ReadableSpan],
|
|
51
128
|
) -> dict[tuple[str, str], list[ReadableSpan]]:
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
# Invoke agent scope for tracing agent invocation.
|
|
5
5
|
|
|
6
|
+
import logging
|
|
7
|
+
|
|
6
8
|
from .agent_details import AgentDetails
|
|
7
9
|
from .constants import (
|
|
8
10
|
GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY,
|
|
@@ -10,6 +12,7 @@ from .constants import (
|
|
|
10
12
|
GEN_AI_CALLER_AGENT_NAME_KEY,
|
|
11
13
|
GEN_AI_CALLER_AGENT_TENANT_ID_KEY,
|
|
12
14
|
GEN_AI_CALLER_AGENT_UPN_KEY,
|
|
15
|
+
GEN_AI_CALLER_AGENT_USER_CLIENT_IP,
|
|
13
16
|
GEN_AI_CALLER_AGENT_USER_ID_KEY,
|
|
14
17
|
GEN_AI_CALLER_ID_KEY,
|
|
15
18
|
GEN_AI_CALLER_NAME_KEY,
|
|
@@ -17,7 +20,6 @@ from .constants import (
|
|
|
17
20
|
GEN_AI_CALLER_UPN_KEY,
|
|
18
21
|
GEN_AI_CALLER_USER_ID_KEY,
|
|
19
22
|
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY,
|
|
20
|
-
GEN_AI_EXECUTION_SOURCE_ID_KEY,
|
|
21
23
|
GEN_AI_EXECUTION_SOURCE_NAME_KEY,
|
|
22
24
|
GEN_AI_EXECUTION_TYPE_KEY,
|
|
23
25
|
GEN_AI_INPUT_MESSAGES_KEY,
|
|
@@ -32,7 +34,9 @@ from .models.caller_details import CallerDetails
|
|
|
32
34
|
from .opentelemetry_scope import OpenTelemetryScope
|
|
33
35
|
from .request import Request
|
|
34
36
|
from .tenant_details import TenantDetails
|
|
35
|
-
from .utils import safe_json_dumps
|
|
37
|
+
from .utils import safe_json_dumps, validate_and_normalize_ip
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
36
40
|
|
|
37
41
|
|
|
38
42
|
class InvokeAgentScope(OpenTelemetryScope):
|
|
@@ -111,7 +115,6 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
111
115
|
# Set request metadata if provided
|
|
112
116
|
if request:
|
|
113
117
|
if request.source_metadata:
|
|
114
|
-
self.set_tag_maybe(GEN_AI_EXECUTION_SOURCE_ID_KEY, request.source_metadata.id)
|
|
115
118
|
self.set_tag_maybe(GEN_AI_EXECUTION_SOURCE_NAME_KEY, request.source_metadata.name)
|
|
116
119
|
self.set_tag_maybe(
|
|
117
120
|
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, request.source_metadata.description
|
|
@@ -121,6 +124,7 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
121
124
|
GEN_AI_EXECUTION_TYPE_KEY,
|
|
122
125
|
request.execution_type.value if request.execution_type else None,
|
|
123
126
|
)
|
|
127
|
+
self.set_tag_maybe(GEN_AI_INPUT_MESSAGES_KEY, safe_json_dumps([request.content]))
|
|
124
128
|
|
|
125
129
|
# Set caller details tags
|
|
126
130
|
if caller_details:
|
|
@@ -140,6 +144,11 @@ class InvokeAgentScope(OpenTelemetryScope):
|
|
|
140
144
|
self.set_tag_maybe(GEN_AI_CALLER_AGENT_USER_ID_KEY, caller_agent_details.agent_auid)
|
|
141
145
|
self.set_tag_maybe(GEN_AI_CALLER_AGENT_UPN_KEY, caller_agent_details.agent_upn)
|
|
142
146
|
self.set_tag_maybe(GEN_AI_CALLER_AGENT_TENANT_ID_KEY, caller_agent_details.tenant_id)
|
|
147
|
+
# Validate and set caller agent client IP
|
|
148
|
+
self.set_tag_maybe(
|
|
149
|
+
GEN_AI_CALLER_AGENT_USER_CLIENT_IP,
|
|
150
|
+
validate_and_normalize_ip(caller_agent_details.agent_client_ip),
|
|
151
|
+
)
|
|
143
152
|
|
|
144
153
|
def record_response(self, response: str) -> None:
|
|
145
154
|
"""Record response information for telemetry tracking.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Per request baggage builder for OpenTelemetry context propagation.
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
7
8
|
from opentelemetry import baggage, context
|
|
@@ -14,20 +15,26 @@ from ..constants import (
|
|
|
14
15
|
GEN_AI_AGENT_ID_KEY,
|
|
15
16
|
GEN_AI_AGENT_NAME_KEY,
|
|
16
17
|
GEN_AI_AGENT_UPN_KEY,
|
|
18
|
+
GEN_AI_CALLER_CLIENT_IP_KEY,
|
|
17
19
|
GEN_AI_CALLER_ID_KEY,
|
|
18
20
|
GEN_AI_CALLER_NAME_KEY,
|
|
19
21
|
GEN_AI_CALLER_UPN_KEY,
|
|
20
22
|
GEN_AI_CONVERSATION_ID_KEY,
|
|
21
23
|
GEN_AI_CONVERSATION_ITEM_LINK_KEY,
|
|
22
24
|
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY,
|
|
23
|
-
GEN_AI_EXECUTION_SOURCE_ID_KEY,
|
|
24
25
|
GEN_AI_EXECUTION_SOURCE_NAME_KEY,
|
|
25
26
|
HIRING_MANAGER_ID_KEY,
|
|
26
27
|
OPERATION_SOURCE_KEY,
|
|
28
|
+
SESSION_DESCRIPTION_KEY,
|
|
29
|
+
SESSION_ID_KEY,
|
|
27
30
|
TENANT_ID_KEY,
|
|
28
31
|
)
|
|
32
|
+
from ..models.operation_source import OperationSource
|
|
33
|
+
from ..utils import deprecated, validate_and_normalize_ip
|
|
29
34
|
from .turn_context_baggage import from_turn_context
|
|
30
35
|
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
31
38
|
|
|
32
39
|
class BaggageBuilder:
|
|
33
40
|
"""Per request baggage builder.
|
|
@@ -50,16 +57,18 @@ class BaggageBuilder:
|
|
|
50
57
|
"""Initialize the baggage builder."""
|
|
51
58
|
self._pairs: dict[str, str] = {}
|
|
52
59
|
|
|
53
|
-
def operation_source(self, value:
|
|
60
|
+
def operation_source(self, value: OperationSource | None) -> "BaggageBuilder":
|
|
54
61
|
"""Set the operation source baggage value.
|
|
55
62
|
|
|
56
63
|
Args:
|
|
57
|
-
value: The operation source value
|
|
64
|
+
value: The operation source enum value
|
|
58
65
|
|
|
59
66
|
Returns:
|
|
60
67
|
Self for method chaining
|
|
61
68
|
"""
|
|
62
|
-
|
|
69
|
+
# Convert enum to string value for baggage storage
|
|
70
|
+
str_value = value.value if value is not None else None
|
|
71
|
+
self._set(OPERATION_SOURCE_KEY, str_value)
|
|
63
72
|
return self
|
|
64
73
|
|
|
65
74
|
def tenant_id(self, value: str | None) -> "BaggageBuilder":
|
|
@@ -178,6 +187,11 @@ class BaggageBuilder:
|
|
|
178
187
|
self._set(GEN_AI_CALLER_UPN_KEY, value)
|
|
179
188
|
return self
|
|
180
189
|
|
|
190
|
+
def caller_client_ip(self, value: str | None) -> "BaggageBuilder":
|
|
191
|
+
"""Set the caller client IP baggage value."""
|
|
192
|
+
self._set(GEN_AI_CALLER_CLIENT_IP_KEY, validate_and_normalize_ip(value))
|
|
193
|
+
return self
|
|
194
|
+
|
|
181
195
|
def conversation_id(self, value: str | None) -> "BaggageBuilder":
|
|
182
196
|
"""Set the conversation ID baggage value."""
|
|
183
197
|
self._set(GEN_AI_CONVERSATION_ID_KEY, value)
|
|
@@ -188,18 +202,33 @@ class BaggageBuilder:
|
|
|
188
202
|
self._set(GEN_AI_CONVERSATION_ITEM_LINK_KEY, value)
|
|
189
203
|
return self
|
|
190
204
|
|
|
191
|
-
|
|
192
|
-
"""Set the execution source metadata ID (e.g., channel ID)."""
|
|
193
|
-
self._set(GEN_AI_EXECUTION_SOURCE_ID_KEY, value)
|
|
194
|
-
return self
|
|
195
|
-
|
|
205
|
+
@deprecated("Use channel_name() instead")
|
|
196
206
|
def source_metadata_name(self, value: str | None) -> "BaggageBuilder":
|
|
197
207
|
"""Set the execution source metadata name (e.g., channel name)."""
|
|
198
|
-
self.
|
|
199
|
-
return self
|
|
208
|
+
return self.channel_name(value)
|
|
200
209
|
|
|
210
|
+
@deprecated("Use channel_links() instead")
|
|
201
211
|
def source_metadata_description(self, value: str | None) -> "BaggageBuilder":
|
|
202
212
|
"""Set the execution source metadata description (e.g., channel description)."""
|
|
213
|
+
return self.channel_links(value)
|
|
214
|
+
|
|
215
|
+
def session_id(self, value: str | None) -> "BaggageBuilder":
|
|
216
|
+
"""Set the session ID baggage value."""
|
|
217
|
+
self._set(SESSION_ID_KEY, value)
|
|
218
|
+
return self
|
|
219
|
+
|
|
220
|
+
def session_description(self, value: str | None) -> "BaggageBuilder":
|
|
221
|
+
"""Set the session description baggage value."""
|
|
222
|
+
self._set(SESSION_DESCRIPTION_KEY, value)
|
|
223
|
+
return self
|
|
224
|
+
|
|
225
|
+
def channel_name(self, value: str | None) -> "BaggageBuilder":
|
|
226
|
+
"""Sets the channel name baggage value (e.g., 'Teams', 'msteams')."""
|
|
227
|
+
self._set(GEN_AI_EXECUTION_SOURCE_NAME_KEY, value)
|
|
228
|
+
return self
|
|
229
|
+
|
|
230
|
+
def channel_links(self, value: str | None) -> "BaggageBuilder":
|
|
231
|
+
"""Sets the channel link baggage value. (e.g., channel links or description)."""
|
|
203
232
|
self._set(GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, value)
|
|
204
233
|
return self
|
|
205
234
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, Iterator, Mapping
|
|
5
5
|
|
|
6
6
|
from ..constants import (
|
|
7
7
|
GEN_AI_AGENT_AUID_KEY,
|
|
@@ -17,7 +17,6 @@ from ..constants import (
|
|
|
17
17
|
GEN_AI_CONVERSATION_ID_KEY,
|
|
18
18
|
GEN_AI_CONVERSATION_ITEM_LINK_KEY,
|
|
19
19
|
GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY,
|
|
20
|
-
GEN_AI_EXECUTION_SOURCE_ID_KEY,
|
|
21
20
|
GEN_AI_EXECUTION_SOURCE_NAME_KEY,
|
|
22
21
|
GEN_AI_EXECUTION_TYPE_KEY,
|
|
23
22
|
TENANT_ID_KEY,
|
|
@@ -26,9 +25,6 @@ from ..execution_type import ExecutionType
|
|
|
26
25
|
|
|
27
26
|
AGENT_ROLE = "agenticUser"
|
|
28
27
|
CHANNEL_ID_AGENTS = "agents"
|
|
29
|
-
ENTITY_TYPE_WPX_COMMENT = "wpxcomment"
|
|
30
|
-
ENTITY_TYPE_EMAIL_NOTIFICATION = "emailNotification"
|
|
31
|
-
WPX_CONVERSATION_ID_FORMAT = "{document_id}_{parent_comment_id}"
|
|
32
28
|
|
|
33
29
|
|
|
34
30
|
def _safe_get(obj: Any, *names: str) -> Any:
|
|
@@ -135,37 +131,40 @@ def _iter_tenant_id_pair(activity: Any) -> Iterator[tuple[str, Any]]:
|
|
|
135
131
|
|
|
136
132
|
|
|
137
133
|
def _iter_source_metadata_pairs(activity: Any) -> Iterator[tuple[str, Any]]:
|
|
134
|
+
"""
|
|
135
|
+
Generate source metadata pairs from activity, handling both string and ChannelId object cases.
|
|
136
|
+
|
|
137
|
+
:param activity: The activity object (Activity instance or dict)
|
|
138
|
+
:return: Iterator of (key, value) tuples for source metadata
|
|
139
|
+
"""
|
|
140
|
+
# Handle channel_id (can be string or ChannelId object)
|
|
138
141
|
channel_id = _safe_get(activity, "channel_id")
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
|
|
143
|
+
# Extract channel name from either string or ChannelId object
|
|
144
|
+
channel_name = None
|
|
145
|
+
sub_channel = None
|
|
146
|
+
|
|
147
|
+
if channel_id is not None:
|
|
148
|
+
if isinstance(channel_id, str):
|
|
149
|
+
# Direct string value
|
|
150
|
+
channel_name = channel_id
|
|
151
|
+
elif hasattr(channel_id, "channel"):
|
|
152
|
+
# ChannelId object
|
|
153
|
+
channel_name = channel_id.channel
|
|
154
|
+
sub_channel = getattr(channel_id, "sub_channel", None)
|
|
155
|
+
elif isinstance(channel_id, dict):
|
|
156
|
+
# Serialized ChannelId as dict
|
|
157
|
+
channel_name = channel_id.get("channel")
|
|
158
|
+
sub_channel = channel_id.get("sub_channel")
|
|
159
|
+
|
|
160
|
+
# Yield channel name as source name
|
|
161
|
+
yield GEN_AI_EXECUTION_SOURCE_NAME_KEY, channel_name
|
|
162
|
+
yield GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, sub_channel
|
|
142
163
|
|
|
143
164
|
|
|
144
165
|
def _iter_conversation_pairs(activity: Any) -> Iterator[tuple[str, Any]]:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
conversation_id = None
|
|
148
|
-
|
|
149
|
-
if channel_id == CHANNEL_ID_AGENTS and isinstance(entities, Iterable):
|
|
150
|
-
# search entities for wpxcomment or emailNotification
|
|
151
|
-
for e in entities:
|
|
152
|
-
etype = _safe_get(e, "type", "Type")
|
|
153
|
-
if etype == ENTITY_TYPE_WPX_COMMENT:
|
|
154
|
-
document_id = _safe_get(e, "documentId", "document_id")
|
|
155
|
-
parent_comment_id = _safe_get(e, "parentCommentId", "parent_comment_id")
|
|
156
|
-
if document_id and parent_comment_id:
|
|
157
|
-
conversation_id = WPX_CONVERSATION_ID_FORMAT.format(
|
|
158
|
-
document_id=document_id,
|
|
159
|
-
parent_comment_id=parent_comment_id,
|
|
160
|
-
)
|
|
161
|
-
break
|
|
162
|
-
elif etype == ENTITY_TYPE_EMAIL_NOTIFICATION:
|
|
163
|
-
conversation_id = _safe_get(e, "conversationId", "conversation_id")
|
|
164
|
-
if conversation_id:
|
|
165
|
-
break
|
|
166
|
-
if not conversation_id:
|
|
167
|
-
conv = _safe_get(activity, "conversation")
|
|
168
|
-
conversation_id = _safe_get(conv, "id", "Id")
|
|
166
|
+
conv = _safe_get(activity, "conversation")
|
|
167
|
+
conversation_id = _safe_get(conv, "id")
|
|
169
168
|
|
|
170
169
|
item_link = _safe_get(activity, "service_url")
|
|
171
170
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""Operation source enumeration for Agent365 SDK."""
|
|
5
|
+
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OperationSource(Enum):
|
|
10
|
+
"""
|
|
11
|
+
Enumeration representing the source of an operation.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
SDK = "SDK"
|
|
15
|
+
"""Operation executed by SDK."""
|
|
16
|
+
|
|
17
|
+
GATEWAY = "Gateway"
|
|
18
|
+
"""Operation executed by Gateway."""
|
|
19
|
+
|
|
20
|
+
MCP_SERVER = "MCPServer"
|
|
21
|
+
"""Operation executed by MCP Server."""
|
|
@@ -16,7 +16,8 @@ For every new span:
|
|
|
16
16
|
from opentelemetry import baggage, context
|
|
17
17
|
from opentelemetry.sdk.trace import SpanProcessor as BaseSpanProcessor
|
|
18
18
|
|
|
19
|
-
from ..constants import GEN_AI_OPERATION_NAME_KEY, INVOKE_AGENT_OPERATION_NAME
|
|
19
|
+
from ..constants import GEN_AI_OPERATION_NAME_KEY, INVOKE_AGENT_OPERATION_NAME, OPERATION_SOURCE_KEY
|
|
20
|
+
from ..models.operation_source import OperationSource
|
|
20
21
|
from .util import COMMON_ATTRIBUTES, INVOKE_AGENT_ATTRIBUTES
|
|
21
22
|
|
|
22
23
|
|
|
@@ -41,6 +42,14 @@ class SpanProcessor(BaseSpanProcessor):
|
|
|
41
42
|
except Exception:
|
|
42
43
|
baggage_map = {}
|
|
43
44
|
|
|
45
|
+
# Set operation source - coalesce baggage value with SDK default
|
|
46
|
+
if OPERATION_SOURCE_KEY not in existing:
|
|
47
|
+
operation_source = baggage_map.get(OPERATION_SOURCE_KEY) or OperationSource.SDK.value
|
|
48
|
+
try:
|
|
49
|
+
span.set_attribute(OPERATION_SOURCE_KEY, operation_source)
|
|
50
|
+
except Exception:
|
|
51
|
+
pass
|
|
52
|
+
|
|
44
53
|
operation_name = existing.get(GEN_AI_OPERATION_NAME_KEY)
|
|
45
54
|
is_invoke_agent = False
|
|
46
55
|
if operation_name == INVOKE_AGENT_OPERATION_NAME:
|
|
@@ -19,6 +19,13 @@ COMMON_ATTRIBUTES = [
|
|
|
19
19
|
consts.GEN_AI_AGENT_BLUEPRINT_ID_KEY, # gen_ai.agent.applicationid
|
|
20
20
|
consts.GEN_AI_AGENT_AUID_KEY,
|
|
21
21
|
consts.GEN_AI_AGENT_TYPE_KEY,
|
|
22
|
+
consts.OPERATION_SOURCE_KEY, # operation.source
|
|
23
|
+
consts.SESSION_ID_KEY,
|
|
24
|
+
consts.SESSION_DESCRIPTION_KEY,
|
|
25
|
+
consts.HIRING_MANAGER_ID_KEY,
|
|
26
|
+
# Execution context
|
|
27
|
+
consts.GEN_AI_EXECUTION_SOURCE_NAME_KEY, # gen_ai.channel.name
|
|
28
|
+
consts.GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, # gen_ai.channel.link
|
|
22
29
|
]
|
|
23
30
|
|
|
24
31
|
# Invoke Agent–specific attributes
|
|
@@ -36,9 +43,5 @@ INVOKE_AGENT_ATTRIBUTES = [
|
|
|
36
43
|
consts.GEN_AI_CALLER_AGENT_UPN_KEY, # gen_ai.caller.agent.upn
|
|
37
44
|
consts.GEN_AI_CALLER_AGENT_TENANT_ID_KEY, # gen_ai.caller.agent.tenantid
|
|
38
45
|
consts.GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY, # gen_ai.caller.agent.applicationid
|
|
39
|
-
# Execution context
|
|
40
46
|
consts.GEN_AI_EXECUTION_TYPE_KEY, # gen_ai.execution.type
|
|
41
|
-
consts.GEN_AI_EXECUTION_SOURCE_ID_KEY, # gen_ai.execution.sourceMetadata.id
|
|
42
|
-
consts.GEN_AI_EXECUTION_SOURCE_NAME_KEY, # gen_ai.execution.sourceMetadata.name
|
|
43
|
-
consts.GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, # gen_ai.execution.sourceMetadata.description
|
|
44
47
|
]
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
# Copyright (c) Microsoft
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
2
3
|
|
|
3
4
|
import datetime
|
|
5
|
+
import functools
|
|
4
6
|
import json
|
|
5
7
|
import logging
|
|
6
8
|
import traceback
|
|
9
|
+
import warnings
|
|
7
10
|
from collections.abc import Callable, Hashable, Iterable, Iterator, Mapping
|
|
8
11
|
from enum import Enum
|
|
12
|
+
from ipaddress import AddressValueError, ip_address
|
|
9
13
|
from threading import RLock
|
|
10
14
|
from typing import Any, Generic, TypeVar, cast
|
|
11
15
|
|
|
12
|
-
from opentelemetry.semconv.
|
|
16
|
+
from opentelemetry.semconv.attributes.exception_attributes import (
|
|
17
|
+
EXCEPTION_MESSAGE,
|
|
18
|
+
EXCEPTION_STACKTRACE,
|
|
19
|
+
)
|
|
13
20
|
from opentelemetry.trace import Span
|
|
14
21
|
from opentelemetry.util.types import AttributeValue
|
|
15
22
|
from wrapt import ObjectProxy
|
|
@@ -69,10 +76,10 @@ def record_exception(span: Span, error: BaseException) -> None:
|
|
|
69
76
|
exception_message = repr(error)
|
|
70
77
|
attributes: dict[str, AttributeValue] = {
|
|
71
78
|
ERROR_TYPE_KEY: exception_type,
|
|
72
|
-
|
|
79
|
+
EXCEPTION_MESSAGE: exception_message,
|
|
73
80
|
}
|
|
74
81
|
try:
|
|
75
|
-
attributes[
|
|
82
|
+
attributes[EXCEPTION_STACKTRACE] = traceback.format_exc()
|
|
76
83
|
except Exception:
|
|
77
84
|
logger.exception("Failed to record exception stacktrace.")
|
|
78
85
|
span.add_event(name="exception", attributes=attributes)
|
|
@@ -149,3 +156,45 @@ def extract_model_name(span_name: str) -> str | None:
|
|
|
149
156
|
return model_name.strip()
|
|
150
157
|
|
|
151
158
|
return None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def deprecated(reason: str):
|
|
162
|
+
"""Decorator to mark functions as deprecated."""
|
|
163
|
+
|
|
164
|
+
def decorator(func):
|
|
165
|
+
@functools.wraps(func)
|
|
166
|
+
def wrapper(*args, **kwargs):
|
|
167
|
+
warnings.warn(
|
|
168
|
+
f"{func.__name__}() is deprecated. {reason}",
|
|
169
|
+
category=DeprecationWarning,
|
|
170
|
+
stacklevel=2,
|
|
171
|
+
)
|
|
172
|
+
return func(*args, **kwargs)
|
|
173
|
+
|
|
174
|
+
return wrapper
|
|
175
|
+
|
|
176
|
+
return decorator
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def validate_and_normalize_ip(ip_string: str | None) -> str | None:
|
|
180
|
+
"""Validate and normalize an IP address string.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
ip_string: The IP address string to validate (IPv4 or IPv6)
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
The normalized IP address string if valid, None if invalid or None input
|
|
187
|
+
|
|
188
|
+
Logs:
|
|
189
|
+
Error message if the IP address is invalid
|
|
190
|
+
"""
|
|
191
|
+
if ip_string is None:
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
# Validate and normalize IP address
|
|
196
|
+
ip_obj = ip_address(ip_string)
|
|
197
|
+
return str(ip_obj)
|
|
198
|
+
except (ValueError, AddressValueError):
|
|
199
|
+
logger.error(f"Invalid IP address: '{ip_string}'")
|
|
200
|
+
return None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-agents-a365-observability-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0.dev5
|
|
4
4
|
Summary: Telemetry, tracing, and monitoring components for AI agents
|
|
5
5
|
Author-email: Microsoft <support@microsoft.com>
|
|
6
6
|
License: MIT
|
|
@@ -64,7 +64,7 @@ For usage examples and detailed documentation, see the [Observability documentat
|
|
|
64
64
|
For issues, questions, or feedback:
|
|
65
65
|
|
|
66
66
|
- File issues in the [GitHub Issues](https://github.com/microsoft/Agent365-python/issues) section
|
|
67
|
-
- See the [main documentation](
|
|
67
|
+
- See the [main documentation](../../README.md) for more information
|
|
68
68
|
|
|
69
69
|
## Trademarks
|
|
70
70
|
|
|
@@ -74,5 +74,5 @@ For issues, questions, or feedback:
|
|
|
74
74
|
|
|
75
75
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
76
76
|
|
|
77
|
-
Licensed under the MIT License - see the [LICENSE](
|
|
77
|
+
Licensed under the MIT License - see the [LICENSE](../../LICENSE.md) file for details.
|
|
78
78
|
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
microsoft_agents_a365/observability/core/__init__.py,sha256=1-Jmokc2cQv7p_RB7W22ZEdqQ0Doq8yDENx_Yews2p8,1737
|
|
2
|
-
microsoft_agents_a365/observability/core/agent_details.py,sha256=
|
|
3
|
-
microsoft_agents_a365/observability/core/config.py,sha256
|
|
4
|
-
microsoft_agents_a365/observability/core/constants.py,sha256=
|
|
2
|
+
microsoft_agents_a365/observability/core/agent_details.py,sha256=7x4tqcocig1hz4T6eCRZy7fSF2gOCpM0pvE6Q3Txww0,1210
|
|
3
|
+
microsoft_agents_a365/observability/core/config.py,sha256=BK1Yg2ykt5yYUBmA-z_wKy2H4k8UB6InF4C5Xh8ZM-g,10996
|
|
4
|
+
microsoft_agents_a365/observability/core/constants.py,sha256=sypi1CXrTX5lX-19qQTq6VL6CZ691w5-gFVh5W6OpbY,4722
|
|
5
5
|
microsoft_agents_a365/observability/core/execute_tool_scope.py,sha256=RcibM_Q-lEhSOpAoRioDao-HiC72W741-SCbcPDWqyI,2989
|
|
6
6
|
microsoft_agents_a365/observability/core/execution_type.py,sha256=Qsf-7_7DzZnRQZ5NHYU31ggfxAjPwneNnda1hOodmG0,303
|
|
7
7
|
microsoft_agents_a365/observability/core/inference_call_details.py,sha256=V1-KQPNq0V36J91ptX96UNRcOW6cy-AV5eIlwbzsrSg,483
|
|
8
8
|
microsoft_agents_a365/observability/core/inference_operation_type.py,sha256=ZLdDdYC0V_0Sff_aTSaiIRVB4a9wEduS7EXpYlme6_M,273
|
|
9
9
|
microsoft_agents_a365/observability/core/inference_scope.py,sha256=87r367uQ4qVp8NpR0na-uQScRR5ID1FlP6c3cmIxvMc,4973
|
|
10
10
|
microsoft_agents_a365/observability/core/invoke_agent_details.py,sha256=PEwkJq-nUz8sxLsbz44hIydGHiO2tHj-sVBkMpZXC1A,389
|
|
11
|
-
microsoft_agents_a365/observability/core/invoke_agent_scope.py,sha256=
|
|
11
|
+
microsoft_agents_a365/observability/core/invoke_agent_scope.py,sha256=A2syvvT9lUE1x7M1Ns7DuXY0J4STMiOBe7kGmPpyLmQ,6917
|
|
12
12
|
microsoft_agents_a365/observability/core/opentelemetry_scope.py,sha256=I8WQoQkCygC7jZRrP2aHsJScGqQKDVktLlRGxApfiZo,9734
|
|
13
|
-
microsoft_agents_a365/observability/core/request.py,sha256=
|
|
13
|
+
microsoft_agents_a365/observability/core/request.py,sha256=x9kRlUbbaK0btsJ8Ym-Ctp5jgsiy54b3eMPst7Olirk,399
|
|
14
14
|
microsoft_agents_a365/observability/core/source_metadata.py,sha256=ijMIiJGvfepDnBqSuZbGsjvrTivz3f_Ub1azyZsCeMA,321
|
|
15
15
|
microsoft_agents_a365/observability/core/tenant_details.py,sha256=eC5S9Zq5sJrpGFWJLKBXVlj-OipysPSoLKSl-8I5YY0,218
|
|
16
16
|
microsoft_agents_a365/observability/core/tool_call_details.py,sha256=sPDW5yvey9_itpKxoo76pwoncOZyhONt9fy3cDdSYyY,455
|
|
17
17
|
microsoft_agents_a365/observability/core/tool_type.py,sha256=O9snmopwdhBjK5eqwyQU3aSdLZzX88c1UMi7oW7lFHg,271
|
|
18
|
-
microsoft_agents_a365/observability/core/utils.py,sha256=
|
|
19
|
-
microsoft_agents_a365/observability/core/exporters/agent365_exporter.py,sha256=
|
|
20
|
-
microsoft_agents_a365/observability/core/exporters/
|
|
18
|
+
microsoft_agents_a365/observability/core/utils.py,sha256=fnZBCEsD-1FlxpJ5hcbwovrEK4baeSKOyKJADSD3n3Q,6063
|
|
19
|
+
microsoft_agents_a365/observability/core/exporters/agent365_exporter.py,sha256=lhicYBnuoDL9RrvGOADEefKV8JCoJd9ChcFn5XS8YjM,11488
|
|
20
|
+
microsoft_agents_a365/observability/core/exporters/agent365_exporter_options.py,sha256=f-p11ZQfxtDIiK-kVPDfebBgT83onxdE29IQryYoAPk,1648
|
|
21
|
+
microsoft_agents_a365/observability/core/exporters/utils.py,sha256=VHhZOELtZtsNEFYcCA-rb7JNX9_aDcJ9XC6OhVIBjl8,4646
|
|
21
22
|
microsoft_agents_a365/observability/core/middleware/__init__.py,sha256=v_vLyv0BQOW9pAvuUeL6Q0ZQ3Ji4MdYYcFDfP7CqvMk,177
|
|
22
|
-
microsoft_agents_a365/observability/core/middleware/baggage_builder.py,sha256=
|
|
23
|
-
microsoft_agents_a365/observability/core/middleware/turn_context_baggage.py,sha256=
|
|
23
|
+
microsoft_agents_a365/observability/core/middleware/baggage_builder.py,sha256=F1HcLKEA4-h1tjfp0AgXMqIsEn58waP6nvyChvo-cAI,10752
|
|
24
|
+
microsoft_agents_a365/observability/core/middleware/turn_context_baggage.py,sha256=teDtlB38zKKdTynYaV9egI3G8wShgCw4KSPxzukSrmQ,6326
|
|
24
25
|
microsoft_agents_a365/observability/core/models/__init__.py,sha256=aCOr6sEsQpv9z4cJgWFA4qOs4xJqclqYYnxOVcxiK2Q,75
|
|
25
26
|
microsoft_agents_a365/observability/core/models/agent_type.py,sha256=ZazwwMAQRrYzzN3Ytz69F6thV4R6nemA6JrK-70fyt0,560
|
|
26
27
|
microsoft_agents_a365/observability/core/models/caller_details.py,sha256=8oaRKeGNteZq_RAQShhfUBs0iO-Sr9yjfk13Mv-xSjA,674
|
|
28
|
+
microsoft_agents_a365/observability/core/models/operation_source.py,sha256=HJp-SNR-lNSPIgoLlF6DMMlijtu4ZGhnqSyu58lIgnk,448
|
|
27
29
|
microsoft_agents_a365/observability/core/trace_processor/__init__.py,sha256=GML9CA7ssJPngxpGFPdVlp-0NmhgS4Ke9IGW5vm5Gys,194
|
|
28
|
-
microsoft_agents_a365/observability/core/trace_processor/span_processor.py,sha256=
|
|
29
|
-
microsoft_agents_a365/observability/core/trace_processor/util.py,sha256
|
|
30
|
-
microsoft_agents_a365_observability_core-0.
|
|
31
|
-
microsoft_agents_a365_observability_core-0.
|
|
32
|
-
microsoft_agents_a365_observability_core-0.
|
|
33
|
-
microsoft_agents_a365_observability_core-0.
|
|
30
|
+
microsoft_agents_a365/observability/core/trace_processor/span_processor.py,sha256=4gEvm8gcf7_A-Fty-InBc2wJG80WCvn8Sh7GKBuFYzU,2953
|
|
31
|
+
microsoft_agents_a365/observability/core/trace_processor/util.py,sha256=xoBZ7qbucZGYJCKgKylaUrl0tP34bcd34fgmh8sXZ_E,2270
|
|
32
|
+
microsoft_agents_a365_observability_core-0.2.0.dev5.dist-info/METADATA,sha256=OGU05eHdSN_0kiUd9CM2xJqE6iI9XOZqBe5mMcNln3k,3871
|
|
33
|
+
microsoft_agents_a365_observability_core-0.2.0.dev5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
34
|
+
microsoft_agents_a365_observability_core-0.2.0.dev5.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
|
|
35
|
+
microsoft_agents_a365_observability_core-0.2.0.dev5.dist-info/RECORD,,
|
|
File without changes
|