microsoft-agents-a365-observability-core 0.1.0.dev30__py3-none-any.whl → 0.2.1.dev0__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 (34) hide show
  1. microsoft_agents_a365/observability/core/__init__.py +2 -1
  2. microsoft_agents_a365/observability/core/agent_details.py +7 -4
  3. microsoft_agents_a365/observability/core/config.py +71 -26
  4. microsoft_agents_a365/observability/core/constants.py +5 -2
  5. microsoft_agents_a365/observability/core/execute_tool_scope.py +15 -1
  6. microsoft_agents_a365/observability/core/execution_type.py +2 -1
  7. microsoft_agents_a365/observability/core/exporters/__init__.py +8 -0
  8. microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +55 -7
  9. microsoft_agents_a365/observability/core/exporters/agent365_exporter_options.py +39 -0
  10. microsoft_agents_a365/observability/core/exporters/utils.py +133 -1
  11. microsoft_agents_a365/observability/core/inference_call_details.py +2 -1
  12. microsoft_agents_a365/observability/core/inference_operation_type.py +2 -1
  13. microsoft_agents_a365/observability/core/inference_scope.py +9 -0
  14. microsoft_agents_a365/observability/core/invoke_agent_details.py +2 -1
  15. microsoft_agents_a365/observability/core/invoke_agent_scope.py +12 -3
  16. microsoft_agents_a365/observability/core/middleware/__init__.py +2 -1
  17. microsoft_agents_a365/observability/core/middleware/baggage_builder.py +52 -28
  18. microsoft_agents_a365/observability/core/models/operation_source.py +21 -0
  19. microsoft_agents_a365/observability/core/opentelemetry_scope.py +2 -1
  20. microsoft_agents_a365/observability/core/request.py +2 -2
  21. microsoft_agents_a365/observability/core/source_metadata.py +2 -1
  22. microsoft_agents_a365/observability/core/tenant_details.py +2 -1
  23. microsoft_agents_a365/observability/core/tool_call_details.py +2 -1
  24. microsoft_agents_a365/observability/core/tool_type.py +2 -1
  25. microsoft_agents_a365/observability/core/trace_processor/__init__.py +2 -1
  26. microsoft_agents_a365/observability/core/trace_processor/span_processor.py +13 -3
  27. microsoft_agents_a365/observability/core/trace_processor/util.py +10 -5
  28. microsoft_agents_a365/observability/core/utils.py +53 -4
  29. {microsoft_agents_a365_observability_core-0.1.0.dev30.dist-info → microsoft_agents_a365_observability_core-0.2.1.dev0.dist-info}/METADATA +3 -3
  30. microsoft_agents_a365_observability_core-0.2.1.dev0.dist-info/RECORD +35 -0
  31. {microsoft_agents_a365_observability_core-0.1.0.dev30.dist-info → microsoft_agents_a365_observability_core-0.2.1.dev0.dist-info}/WHEEL +1 -1
  32. {microsoft_agents_a365_observability_core-0.1.0.dev30.dist-info → microsoft_agents_a365_observability_core-0.2.1.dev0.dist-info}/top_level.txt +1 -0
  33. microsoft_agents_a365/observability/core/middleware/turn_context_baggage.py +0 -193
  34. microsoft_agents_a365_observability_core-0.1.0.dev30.dist-info/RECORD +0 -33
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  from dataclasses import dataclass
4
5
 
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  from enum import Enum
4
5
 
@@ -5,6 +5,8 @@ from typing import List
5
5
 
6
6
  from .agent_details import AgentDetails
7
7
  from .constants import (
8
+ GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY,
9
+ GEN_AI_EXECUTION_SOURCE_NAME_KEY,
8
10
  GEN_AI_INPUT_MESSAGES_KEY,
9
11
  GEN_AI_OPERATION_NAME_KEY,
10
12
  GEN_AI_OUTPUT_MESSAGES_KEY,
@@ -90,6 +92,13 @@ class InferenceScope(OpenTelemetryScope):
90
92
  )
91
93
  self.set_tag_maybe(GEN_AI_RESPONSE_ID_KEY, details.responseId)
92
94
 
95
+ # Set request metadata if provided
96
+ if request and request.source_metadata:
97
+ self.set_tag_maybe(GEN_AI_EXECUTION_SOURCE_NAME_KEY, request.source_metadata.name)
98
+ self.set_tag_maybe(
99
+ GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, request.source_metadata.description
100
+ )
101
+
93
102
  def record_input_messages(self, messages: List[str]) -> None:
94
103
  """Records the input messages for telemetry tracking.
95
104
 
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Data class for invoke agent details.
4
5
 
@@ -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.
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Middleware components for Microsoft Agent 365 SDK.
4
5
 
@@ -1,7 +1,9 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Per request baggage builder for OpenTelemetry context propagation.
4
5
 
6
+ import logging
5
7
  from typing import Any
6
8
 
7
9
  from opentelemetry import baggage, context
@@ -14,19 +16,24 @@ from ..constants import (
14
16
  GEN_AI_AGENT_ID_KEY,
15
17
  GEN_AI_AGENT_NAME_KEY,
16
18
  GEN_AI_AGENT_UPN_KEY,
19
+ GEN_AI_CALLER_CLIENT_IP_KEY,
17
20
  GEN_AI_CALLER_ID_KEY,
18
21
  GEN_AI_CALLER_NAME_KEY,
19
22
  GEN_AI_CALLER_UPN_KEY,
20
23
  GEN_AI_CONVERSATION_ID_KEY,
21
24
  GEN_AI_CONVERSATION_ITEM_LINK_KEY,
22
25
  GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY,
23
- GEN_AI_EXECUTION_SOURCE_ID_KEY,
24
26
  GEN_AI_EXECUTION_SOURCE_NAME_KEY,
25
27
  HIRING_MANAGER_ID_KEY,
26
28
  OPERATION_SOURCE_KEY,
29
+ SESSION_DESCRIPTION_KEY,
30
+ SESSION_ID_KEY,
27
31
  TENANT_ID_KEY,
28
32
  )
29
- from .turn_context_baggage import from_turn_context
33
+ from ..models.operation_source import OperationSource
34
+ from ..utils import deprecated, validate_and_normalize_ip
35
+
36
+ logger = logging.getLogger(__name__)
30
37
 
31
38
 
32
39
  class BaggageBuilder:
@@ -36,30 +43,35 @@ class BaggageBuilder:
36
43
  propagated in the OpenTelemetry context.
37
44
 
38
45
  Example:
39
- >>> with BaggageBuilder() \
40
- ... .tenant_id("tenant-123") \
41
- ... .agent_id("agent-456") \
42
- ... .correlation_id("corr-789") \
43
- ... .build():
44
- ... # Baggage is set in this context
45
- ... pass
46
- >>> # Baggage is restored after exiting the context
46
+ .. code-block:: python
47
+
48
+ builder = (BaggageBuilder()
49
+ .tenant_id("tenant-123")
50
+ .agent_id("agent-456")
51
+ .correlation_id("corr-789"))
52
+
53
+ with builder.build():
54
+ # Baggage is set in this context
55
+ pass
56
+ # Baggage is restored after exiting the context
47
57
  """
48
58
 
49
59
  def __init__(self):
50
60
  """Initialize the baggage builder."""
51
61
  self._pairs: dict[str, str] = {}
52
62
 
53
- def operation_source(self, value: str | None) -> "BaggageBuilder":
63
+ def operation_source(self, value: OperationSource | None) -> "BaggageBuilder":
54
64
  """Set the operation source baggage value.
55
65
 
56
66
  Args:
57
- value: The operation source value
67
+ value: The operation source enum value
58
68
 
59
69
  Returns:
60
70
  Self for method chaining
61
71
  """
62
- self._set(OPERATION_SOURCE_KEY, value)
72
+ # Convert enum to string value for baggage storage
73
+ str_value = value.value if value is not None else None
74
+ self._set(OPERATION_SOURCE_KEY, str_value)
63
75
  return self
64
76
 
65
77
  def tenant_id(self, value: str | None) -> "BaggageBuilder":
@@ -178,6 +190,11 @@ class BaggageBuilder:
178
190
  self._set(GEN_AI_CALLER_UPN_KEY, value)
179
191
  return self
180
192
 
193
+ def caller_client_ip(self, value: str | None) -> "BaggageBuilder":
194
+ """Set the caller client IP baggage value."""
195
+ self._set(GEN_AI_CALLER_CLIENT_IP_KEY, validate_and_normalize_ip(value))
196
+ return self
197
+
181
198
  def conversation_id(self, value: str | None) -> "BaggageBuilder":
182
199
  """Set the conversation ID baggage value."""
183
200
  self._set(GEN_AI_CONVERSATION_ID_KEY, value)
@@ -188,28 +205,35 @@ class BaggageBuilder:
188
205
  self._set(GEN_AI_CONVERSATION_ITEM_LINK_KEY, value)
189
206
  return self
190
207
 
191
- def source_metadata_id(self, value: str | None) -> "BaggageBuilder":
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
-
208
+ @deprecated("Use channel_name() instead")
196
209
  def source_metadata_name(self, value: str | None) -> "BaggageBuilder":
197
210
  """Set the execution source metadata name (e.g., channel name)."""
198
- self._set(GEN_AI_EXECUTION_SOURCE_NAME_KEY, value)
199
- return self
211
+ return self.channel_name(value)
200
212
 
213
+ @deprecated("Use channel_links() instead")
201
214
  def source_metadata_description(self, value: str | None) -> "BaggageBuilder":
202
215
  """Set the execution source metadata description (e.g., channel description)."""
203
- self._set(GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, value)
216
+ return self.channel_links(value)
217
+
218
+ def session_id(self, value: str | None) -> "BaggageBuilder":
219
+ """Set the session ID baggage value."""
220
+ self._set(SESSION_ID_KEY, value)
204
221
  return self
205
222
 
206
- def from_turn_context(self, turn_context: Any) -> "BaggageBuilder":
207
- """
208
- Populate baggage from a turn_context (duck-typed).
209
- Delegates to baggage_turn_context.from_turn_context.
210
- """
223
+ def session_description(self, value: str | None) -> "BaggageBuilder":
224
+ """Set the session description baggage value."""
225
+ self._set(SESSION_DESCRIPTION_KEY, value)
226
+ return self
211
227
 
212
- return self.set_pairs(from_turn_context(turn_context))
228
+ def channel_name(self, value: str | None) -> "BaggageBuilder":
229
+ """Sets the channel name baggage value (e.g., 'Teams', 'msteams')."""
230
+ self._set(GEN_AI_EXECUTION_SOURCE_NAME_KEY, value)
231
+ return self
232
+
233
+ def channel_links(self, value: str | None) -> "BaggageBuilder":
234
+ """Sets the channel link baggage value. (e.g., channel links or description)."""
235
+ self._set(GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, value)
236
+ return self
213
237
 
214
238
  def set_pairs(self, pairs: Any) -> "BaggageBuilder":
215
239
  """
@@ -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."""
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Base class for OpenTelemetry tracing scopes.
4
5
 
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Request class.
4
5
 
@@ -16,4 +17,3 @@ class Request:
16
17
  execution_type: ExecutionType
17
18
  session_id: str | None = None
18
19
  source_metadata: SourceMetadata | None = None
19
- payload: str | None = None
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Source metadata class.
4
5
 
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Tenant details class.
4
5
  from dataclasses import dataclass
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Data class for tool call details.
4
5
 
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  # Tool type enum.
4
5
 
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  """
4
5
  Trace Processors
@@ -1,6 +1,7 @@
1
- """Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
- Span processor for copying OpenTelemetry baggage entries onto spans.
4
+ """Span processor for copying OpenTelemetry baggage entries onto spans.
4
5
 
5
6
  This implementation assumes `opentelemetry.baggage.get_all` is available with the
6
7
  signature `get_all(context: Context | None) -> Mapping[str, object]`.
@@ -16,7 +17,8 @@ For every new span:
16
17
  from opentelemetry import baggage, context
17
18
  from opentelemetry.sdk.trace import SpanProcessor as BaseSpanProcessor
18
19
 
19
- from ..constants import GEN_AI_OPERATION_NAME_KEY, INVOKE_AGENT_OPERATION_NAME
20
+ from ..constants import GEN_AI_OPERATION_NAME_KEY, INVOKE_AGENT_OPERATION_NAME, OPERATION_SOURCE_KEY
21
+ from ..models.operation_source import OperationSource
20
22
  from .util import COMMON_ATTRIBUTES, INVOKE_AGENT_ATTRIBUTES
21
23
 
22
24
 
@@ -41,6 +43,14 @@ class SpanProcessor(BaseSpanProcessor):
41
43
  except Exception:
42
44
  baggage_map = {}
43
45
 
46
+ # Set operation source - coalesce baggage value with SDK default
47
+ if OPERATION_SOURCE_KEY not in existing:
48
+ operation_source = baggage_map.get(OPERATION_SOURCE_KEY) or OperationSource.SDK.value
49
+ try:
50
+ span.set_attribute(OPERATION_SOURCE_KEY, operation_source)
51
+ except Exception:
52
+ pass
53
+
44
54
  operation_name = existing.get(GEN_AI_OPERATION_NAME_KEY)
45
55
  is_invoke_agent = False
46
56
  if operation_name == INVOKE_AGENT_OPERATION_NAME:
@@ -1,4 +1,5 @@
1
- # Copyright (c) Microsoft. All rights reserved.
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
2
3
 
3
4
  from .. import constants as consts
4
5
 
@@ -19,6 +20,14 @@ COMMON_ATTRIBUTES = [
19
20
  consts.GEN_AI_AGENT_BLUEPRINT_ID_KEY, # gen_ai.agent.applicationid
20
21
  consts.GEN_AI_AGENT_AUID_KEY,
21
22
  consts.GEN_AI_AGENT_TYPE_KEY,
23
+ consts.OPERATION_SOURCE_KEY, # operation.source
24
+ consts.SESSION_ID_KEY,
25
+ consts.SESSION_DESCRIPTION_KEY,
26
+ consts.HIRING_MANAGER_ID_KEY,
27
+ consts.GEN_AI_CALLER_CLIENT_IP_KEY, # gen_ai.caller.client.ip
28
+ # Execution context
29
+ consts.GEN_AI_EXECUTION_SOURCE_NAME_KEY, # gen_ai.channel.name
30
+ consts.GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, # gen_ai.channel.link
22
31
  ]
23
32
 
24
33
  # Invoke Agent–specific attributes
@@ -36,9 +45,5 @@ INVOKE_AGENT_ATTRIBUTES = [
36
45
  consts.GEN_AI_CALLER_AGENT_UPN_KEY, # gen_ai.caller.agent.upn
37
46
  consts.GEN_AI_CALLER_AGENT_TENANT_ID_KEY, # gen_ai.caller.agent.tenantid
38
47
  consts.GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY, # gen_ai.caller.agent.applicationid
39
- # Execution context
40
48
  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
49
  ]
@@ -1,15 +1,22 @@
1
- # Copyright (c) Microsoft. All rights reserved.
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.trace import SpanAttributes as OTELSpanAttributes
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
- OTELSpanAttributes.EXCEPTION_MESSAGE: exception_message,
79
+ EXCEPTION_MESSAGE: exception_message,
73
80
  }
74
81
  try:
75
- attributes[OTELSpanAttributes.EXCEPTION_STACKTRACE] = traceback.format_exc()
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.1.0.dev30
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
@@ -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](../../../README.md) for more information
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](../../../LICENSE.md) file for details.
77
+ Licensed under the MIT License - see the [LICENSE](../../LICENSE.md) file for details.
78
78
 
@@ -0,0 +1,35 @@
1
+ microsoft_agents_a365/observability/core/__init__.py,sha256=r613vGLepo9ttTRmq3_EQz3hfDd4Ukp3v8AVS-6Uf_o,1762
2
+ microsoft_agents_a365/observability/core/agent_details.py,sha256=7x4tqcocig1hz4T6eCRZy7fSF2gOCpM0pvE6Q3Txww0,1210
3
+ microsoft_agents_a365/observability/core/config.py,sha256=YhnQaJr-SVNJLGM_HvYvaAfwXn-h9KinbNNZeXG9w90,11751
4
+ microsoft_agents_a365/observability/core/constants.py,sha256=suvJc4JgB9o3eeDVyiJDk0kjFVkjeDrl6u43XsvPWBc,4747
5
+ microsoft_agents_a365/observability/core/execute_tool_scope.py,sha256=XgramOBLhpilR5VjFsigdDCqYkDkG0D6CIrwN5xgeJw,3654
6
+ microsoft_agents_a365/observability/core/execution_type.py,sha256=qnNGmo9-Tlb3PBTK3pen2QIAm2hlQiKl8vRuMPWHtk8,328
7
+ microsoft_agents_a365/observability/core/inference_call_details.py,sha256=VZGB7ybr3BT7YZbjRU92YCpsDyvoicILoWPejKiI1rM,508
8
+ microsoft_agents_a365/observability/core/inference_operation_type.py,sha256=6FKl9lxSBgPptwD7TZ4znrd8t6R9nD1rq1QPACfVsqY,298
9
+ microsoft_agents_a365/observability/core/inference_scope.py,sha256=fca5AdPw5PvHSNkLgUpJGsZWxG5c-BDUFLnRKjpAEuo,5382
10
+ microsoft_agents_a365/observability/core/invoke_agent_details.py,sha256=ED0pjtlQAl-wCO9vJqXv-xghCwVFn_c8FCSz_vpEa_s,414
11
+ microsoft_agents_a365/observability/core/invoke_agent_scope.py,sha256=A2syvvT9lUE1x7M1Ns7DuXY0J4STMiOBe7kGmPpyLmQ,6917
12
+ microsoft_agents_a365/observability/core/opentelemetry_scope.py,sha256=J2vetCZnVRgVgc7wlKzmL5FbGf5LdbWaf1ryxt8rBC0,9759
13
+ microsoft_agents_a365/observability/core/request.py,sha256=FVO8cjNSAp_5NnOmz4XuXkKJNl5NOYBHZAIDRo6mjcc,424
14
+ microsoft_agents_a365/observability/core/source_metadata.py,sha256=BgAyyA17sZtHSDH1aPQAm1BEtQ3G5zpbKZinjew_leo,346
15
+ microsoft_agents_a365/observability/core/tenant_details.py,sha256=VoJIUeEIIHRvr2trzRz5BE8QWkLTt6Z4NVVI2MbyBeQ,243
16
+ microsoft_agents_a365/observability/core/tool_call_details.py,sha256=D7wrDYjr2-65Ddx2LwK2P5bSghqq7ehdhUUToUiW-SI,480
17
+ microsoft_agents_a365/observability/core/tool_type.py,sha256=Jg021SlX-Vwz2WdHffcgJFm9Ys3zr1LRJNf4KYaZ6S4,296
18
+ microsoft_agents_a365/observability/core/utils.py,sha256=fnZBCEsD-1FlxpJ5hcbwovrEK4baeSKOyKJADSD3n3Q,6063
19
+ microsoft_agents_a365/observability/core/exporters/__init__.py,sha256=UEaMJYkbTViVAUVQTFSf1BUvni1pNTcDwriKzOzvGFU,296
20
+ microsoft_agents_a365/observability/core/exporters/agent365_exporter.py,sha256=Q8ylOrgBF2gNB_KAn8QdtJu8E3aiC6kUUomCgNLqgb4,13490
21
+ microsoft_agents_a365/observability/core/exporters/agent365_exporter_options.py,sha256=f-p11ZQfxtDIiK-kVPDfebBgT83onxdE29IQryYoAPk,1648
22
+ microsoft_agents_a365/observability/core/exporters/utils.py,sha256=tICuOuWfgUKL1dHoGuyG5_xNOrREPTXFE-RJZehpVT4,6989
23
+ microsoft_agents_a365/observability/core/middleware/__init__.py,sha256=KNH2QjJBIGhn1xf_r3HdOVYUw9ngKEcy0HV9eHuTaqk,202
24
+ microsoft_agents_a365/observability/core/middleware/baggage_builder.py,sha256=KaTb8n0D8dbjS-qph3o160VUEn4kYy8YdaFvGMd5Vck,10505
25
+ microsoft_agents_a365/observability/core/models/__init__.py,sha256=aCOr6sEsQpv9z4cJgWFA4qOs4xJqclqYYnxOVcxiK2Q,75
26
+ microsoft_agents_a365/observability/core/models/agent_type.py,sha256=ZazwwMAQRrYzzN3Ytz69F6thV4R6nemA6JrK-70fyt0,560
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
29
+ microsoft_agents_a365/observability/core/trace_processor/__init__.py,sha256=0QDkZgvgGekJVcZXjubSqipYhCGMZaAAxLSanyR76pg,219
30
+ microsoft_agents_a365/observability/core/trace_processor/span_processor.py,sha256=O_DLuLR-6sV9VmkYfMzUF3s6n1OLrtK5qkD6csen73g,2980
31
+ microsoft_agents_a365/observability/core/trace_processor/util.py,sha256=h5oOFAGIY6x9WtgfErPetblSkWZsQZjOgbwoR_tL-7k,2364
32
+ microsoft_agents_a365_observability_core-0.2.1.dev0.dist-info/METADATA,sha256=mCXs-XGApLHUEkD7f17jKeJSHwXC4Um4U6eeV5Hnbyw,3871
33
+ microsoft_agents_a365_observability_core-0.2.1.dev0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
34
+ microsoft_agents_a365_observability_core-0.2.1.dev0.dist-info/top_level.txt,sha256=m90AvzRnjbL6fpi20mzOj6HUVkR2LWuf2JuXm4LL9LU,27
35
+ microsoft_agents_a365_observability_core-0.2.1.dev0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5