microsoft-agents-a365-observability-core 0.2.1.dev19__tar.gz → 0.2.1.dev22__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/PKG-INFO +1 -1
  2. microsoft_agents_a365_observability_core-0.2.1.dev22/microsoft_agents_a365/observability/core/models/response.py +12 -0
  3. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/opentelemetry_scope.py +18 -6
  4. microsoft_agents_a365_observability_core-0.2.1.dev22/microsoft_agents_a365/observability/core/spans_scopes/__init__.py +2 -0
  5. microsoft_agents_a365_observability_core-0.2.1.dev22/microsoft_agents_a365/observability/core/spans_scopes/output_scope.py +78 -0
  6. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/utils.py +124 -1
  7. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365_observability_core.egg-info/PKG-INFO +1 -1
  8. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365_observability_core.egg-info/SOURCES.txt +3 -0
  9. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/README.md +0 -0
  10. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/__init__.py +0 -0
  11. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/agent_details.py +0 -0
  12. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/config.py +0 -0
  13. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/constants.py +0 -0
  14. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/execute_tool_scope.py +0 -0
  15. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/execution_type.py +0 -0
  16. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/exporters/__init__.py +0 -0
  17. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +0 -0
  18. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/exporters/agent365_exporter_options.py +0 -0
  19. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/exporters/enriched_span.py +0 -0
  20. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/exporters/enriching_span_processor.py +0 -0
  21. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/exporters/utils.py +0 -0
  22. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/inference_call_details.py +0 -0
  23. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/inference_operation_type.py +0 -0
  24. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/inference_scope.py +0 -0
  25. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/invoke_agent_details.py +0 -0
  26. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/invoke_agent_scope.py +0 -0
  27. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/middleware/__init__.py +0 -0
  28. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/middleware/baggage_builder.py +0 -0
  29. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/models/__init__.py +0 -0
  30. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/models/agent_type.py +0 -0
  31. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/models/caller_details.py +0 -0
  32. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/models/operation_source.py +0 -0
  33. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/request.py +0 -0
  34. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/source_metadata.py +0 -0
  35. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/tenant_details.py +0 -0
  36. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/tool_call_details.py +0 -0
  37. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/tool_type.py +0 -0
  38. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/trace_processor/__init__.py +0 -0
  39. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/trace_processor/span_processor.py +0 -0
  40. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365/observability/core/trace_processor/util.py +0 -0
  41. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365_observability_core.egg-info/dependency_links.txt +0 -0
  42. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365_observability_core.egg-info/requires.txt +0 -0
  43. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/microsoft_agents_a365_observability_core.egg-info/top_level.txt +0 -0
  44. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/pyproject.toml +0 -0
  45. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/setup.cfg +0 -0
  46. {microsoft_agents_a365_observability_core-0.2.1.dev19 → microsoft_agents_a365_observability_core-0.2.1.dev22}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microsoft-agents-a365-observability-core
3
- Version: 0.2.1.dev19
3
+ Version: 0.2.1.dev22
4
4
  Summary: Telemetry, tracing, and monitoring components for AI agents
5
5
  Author-email: Microsoft <support@microsoft.com>
6
6
  License: MIT
@@ -0,0 +1,12 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from dataclasses import dataclass
5
+
6
+
7
+ @dataclass
8
+ class Response:
9
+ """Response details from agent execution."""
10
+
11
+ """The list of response messages from the agent."""
12
+ messages: list[str]
@@ -10,7 +10,14 @@ from threading import Lock
10
10
  from typing import TYPE_CHECKING, Any
11
11
 
12
12
  from opentelemetry import baggage, context, trace
13
- from opentelemetry.trace import Span, SpanKind, Status, StatusCode, Tracer, set_span_in_context
13
+ from opentelemetry.trace import (
14
+ Span,
15
+ SpanKind,
16
+ Status,
17
+ StatusCode,
18
+ Tracer,
19
+ set_span_in_context,
20
+ )
14
21
 
15
22
  from .constants import (
16
23
  ENABLE_A365_OBSERVABILITY,
@@ -32,6 +39,7 @@ from .constants import (
32
39
  SOURCE_NAME,
33
40
  TENANT_ID_KEY,
34
41
  )
42
+ from .utils import parse_parent_id_to_context
35
43
 
36
44
  if TYPE_CHECKING:
37
45
  from .agent_details import AgentDetails
@@ -71,6 +79,7 @@ class OpenTelemetryScope:
71
79
  activity_name: str,
72
80
  agent_details: "AgentDetails | None" = None,
73
81
  tenant_details: "TenantDetails | None" = None,
82
+ parent_id: str | None = None,
74
83
  ):
75
84
  """Initialize the OpenTelemetry scope.
76
85
 
@@ -80,6 +89,8 @@ class OpenTelemetryScope:
80
89
  activity_name: The name of the activity for display purposes
81
90
  agent_details: Optional agent details
82
91
  tenant_details: Optional tenant details
92
+ parent_id: Optional parent Activity ID used to link this span to an upstream
93
+ operation
83
94
  """
84
95
  self._span: Span | None = None
85
96
  self._start_time = time.time()
@@ -102,12 +113,13 @@ class OpenTelemetryScope:
102
113
  elif kind.lower() == "consumer":
103
114
  activity_kind = SpanKind.CONSUMER
104
115
 
105
- # Get current context for parent relationship
106
- current_context = context.get_current()
116
+ # Get context for parent relationship
117
+ # If parent_id is provided, parse it and use it as the parent context
118
+ # Otherwise, use the current context
119
+ parent_context = parse_parent_id_to_context(parent_id)
120
+ span_context = parent_context if parent_context else context.get_current()
107
121
 
108
- self._span = tracer.start_span(
109
- activity_name, kind=activity_kind, context=current_context
110
- )
122
+ self._span = tracer.start_span(activity_name, kind=activity_kind, context=span_context)
111
123
 
112
124
  # Log span creation
113
125
  if self._span:
@@ -0,0 +1,2 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
@@ -0,0 +1,78 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from ..agent_details import AgentDetails
5
+ from ..constants import GEN_AI_OUTPUT_MESSAGES_KEY
6
+ from ..models.response import Response
7
+ from ..opentelemetry_scope import OpenTelemetryScope
8
+ from ..tenant_details import TenantDetails
9
+ from ..utils import safe_json_dumps
10
+
11
+ OUTPUT_OPERATION_NAME = "output_messages"
12
+
13
+
14
+ class OutputScope(OpenTelemetryScope):
15
+ """Provides OpenTelemetry tracing scope for output messages."""
16
+
17
+ @staticmethod
18
+ def start(
19
+ agent_details: AgentDetails,
20
+ tenant_details: TenantDetails,
21
+ response: Response,
22
+ parent_id: str | None = None,
23
+ ) -> "OutputScope":
24
+ """Creates and starts a new scope for output tracing.
25
+
26
+ Args:
27
+ agent_details: The details of the agent
28
+ tenant_details: The details of the tenant
29
+ response: The response details from the agent
30
+ parent_id: Optional parent Activity ID used to link this span to an upstream
31
+ operation
32
+
33
+ Returns:
34
+ A new OutputScope instance
35
+ """
36
+ return OutputScope(agent_details, tenant_details, response, parent_id)
37
+
38
+ def __init__(
39
+ self,
40
+ agent_details: AgentDetails,
41
+ tenant_details: TenantDetails,
42
+ response: Response,
43
+ parent_id: str | None = None,
44
+ ):
45
+ """Initialize the output scope.
46
+
47
+ Args:
48
+ agent_details: The details of the agent
49
+ tenant_details: The details of the tenant
50
+ response: The response details from the agent
51
+ parent_id: Optional parent Activity ID used to link this span to an upstream
52
+ operation
53
+ """
54
+ super().__init__(
55
+ kind="Client",
56
+ operation_name=OUTPUT_OPERATION_NAME,
57
+ activity_name=(f"{OUTPUT_OPERATION_NAME} {agent_details.agent_id}"),
58
+ agent_details=agent_details,
59
+ tenant_details=tenant_details,
60
+ parent_id=parent_id,
61
+ )
62
+
63
+ # Initialize accumulated messages list
64
+ self._output_messages: list[str] = list(response.messages)
65
+
66
+ # Set response messages
67
+ self.set_tag_maybe(GEN_AI_OUTPUT_MESSAGES_KEY, safe_json_dumps(self._output_messages))
68
+
69
+ def record_output_messages(self, messages: list[str]) -> None:
70
+ """Records the output messages for telemetry tracking.
71
+
72
+ Appends the provided messages to the accumulated output messages list.
73
+
74
+ Args:
75
+ messages: List of output messages to append
76
+ """
77
+ self._output_messages.extend(messages)
78
+ self.set_tag_maybe(GEN_AI_OUTPUT_MESSAGES_KEY, safe_json_dumps(self._output_messages))
@@ -13,11 +13,12 @@ from ipaddress import AddressValueError, ip_address
13
13
  from threading import RLock
14
14
  from typing import Any, Generic, TypeVar, cast
15
15
 
16
+ from opentelemetry import context
16
17
  from opentelemetry.semconv.attributes.exception_attributes import (
17
18
  EXCEPTION_MESSAGE,
18
19
  EXCEPTION_STACKTRACE,
19
20
  )
20
- from opentelemetry.trace import Span
21
+ from opentelemetry.trace import NonRecordingSpan, Span, SpanContext, TraceFlags, set_span_in_context
21
22
  from opentelemetry.util.types import AttributeValue
22
23
  from wrapt import ObjectProxy
23
24
 
@@ -27,6 +28,128 @@ logger = logging.getLogger(__name__)
27
28
  logger.addHandler(logging.NullHandler())
28
29
 
29
30
 
31
+ # W3C Trace Context constants
32
+ W3C_TRACE_CONTEXT_VERSION = "00"
33
+ W3C_TRACE_ID_LENGTH = 32 # 32 hex chars = 128 bits
34
+ W3C_SPAN_ID_LENGTH = 16 # 16 hex chars = 64 bits
35
+
36
+
37
+ def validate_w3c_trace_context_version(version: str) -> bool:
38
+ """Validate W3C Trace Context version.
39
+
40
+ Args:
41
+ version: The version string to validate
42
+
43
+ Returns:
44
+ True if valid, False otherwise
45
+ """
46
+ return version == W3C_TRACE_CONTEXT_VERSION
47
+
48
+
49
+ def _is_valid_hex(hex_string: str) -> bool:
50
+ """Check if a string contains only valid hexadecimal characters.
51
+
52
+ Args:
53
+ hex_string: The string to validate
54
+
55
+ Returns:
56
+ True if all characters are valid hexadecimal (0-9, a-f, A-F), False otherwise
57
+ """
58
+ return all(c in "0123456789abcdefABCDEF" for c in hex_string)
59
+
60
+
61
+ def validate_trace_id(trace_id_hex: str) -> bool:
62
+ """Validate W3C Trace Context trace_id format.
63
+
64
+ Args:
65
+ trace_id_hex: The trace_id hex string to validate (should be 32 hex chars)
66
+
67
+ Returns:
68
+ True if valid (32 hex chars), False otherwise
69
+ """
70
+ return len(trace_id_hex) == W3C_TRACE_ID_LENGTH and _is_valid_hex(trace_id_hex)
71
+
72
+
73
+ def validate_span_id(span_id_hex: str) -> bool:
74
+ """Validate W3C Trace Context span_id format.
75
+
76
+ Args:
77
+ span_id_hex: The span_id hex string to validate (should be 16 hex chars)
78
+
79
+ Returns:
80
+ True if valid (16 hex chars), False otherwise
81
+ """
82
+ return len(span_id_hex) == W3C_SPAN_ID_LENGTH and _is_valid_hex(span_id_hex)
83
+
84
+
85
+ def parse_parent_id_to_context(parent_id: str | None) -> context.Context | None:
86
+ """Parse a W3C trace context parent ID and return a context with the parent span.
87
+
88
+ The parent_id format is expected to be W3C Trace Context format:
89
+ "00-{trace_id}-{span_id}-{trace_flags}"
90
+ Example: "00-1234567890abcdef1234567890abcdef-abcdefabcdef1234-01"
91
+
92
+ Args:
93
+ parent_id: The W3C Trace Context format parent ID string
94
+
95
+ Returns:
96
+ A context containing the parent span, or None if parent_id is invalid
97
+ """
98
+ if not parent_id:
99
+ return None
100
+
101
+ try:
102
+ # W3C Trace Context format: "00-{trace_id}-{span_id}-{trace_flags}"
103
+ parts = parent_id.split("-")
104
+ if len(parts) != 4:
105
+ logger.warning(f"Invalid parent_id format (expected 4 parts): {parent_id}")
106
+ return None
107
+
108
+ version, trace_id_hex, span_id_hex, trace_flags_hex = parts
109
+
110
+ # Validate W3C Trace Context version
111
+ if not validate_w3c_trace_context_version(version):
112
+ logger.warning(f"Unsupported W3C Trace Context version: {version}")
113
+ return None
114
+
115
+ # Validate trace_id (must be 32 hex chars)
116
+ if not validate_trace_id(trace_id_hex):
117
+ logger.warning(
118
+ f"Invalid trace_id (expected {W3C_TRACE_ID_LENGTH} hex chars): '{trace_id_hex}'"
119
+ )
120
+ return None
121
+
122
+ # Validate span_id (must be 16 hex chars)
123
+ if not validate_span_id(span_id_hex):
124
+ logger.warning(
125
+ f"Invalid span_id (expected {W3C_SPAN_ID_LENGTH} hex chars): '{span_id_hex}'"
126
+ )
127
+ return None
128
+
129
+ # Parse the hex values
130
+ trace_id = int(trace_id_hex, 16)
131
+ span_id = int(span_id_hex, 16)
132
+ trace_flags = TraceFlags(int(trace_flags_hex, 16))
133
+
134
+ # Create a SpanContext from the parsed values
135
+ parent_span_context = SpanContext(
136
+ trace_id=trace_id,
137
+ span_id=span_id,
138
+ is_remote=True,
139
+ trace_flags=trace_flags,
140
+ )
141
+
142
+ # Create a NonRecordingSpan with the parent context
143
+ parent_span = NonRecordingSpan(parent_span_context)
144
+
145
+ # Create a context with the parent span
146
+ return set_span_in_context(parent_span)
147
+
148
+ except (ValueError, IndexError) as e:
149
+ logger.warning(f"Failed to parse parent_id '{parent_id}': {e}")
150
+ return None
151
+
152
+
30
153
  def safe_json_dumps(obj: Any, **kwargs: Any) -> str:
31
154
  return json.dumps(obj, default=str, ensure_ascii=False, **kwargs)
32
155
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microsoft-agents-a365-observability-core
3
- Version: 0.2.1.dev19
3
+ Version: 0.2.1.dev22
4
4
  Summary: Telemetry, tracing, and monitoring components for AI agents
5
5
  Author-email: Microsoft <support@microsoft.com>
6
6
  License: MIT
@@ -31,6 +31,9 @@ microsoft_agents_a365/observability/core/models/__init__.py
31
31
  microsoft_agents_a365/observability/core/models/agent_type.py
32
32
  microsoft_agents_a365/observability/core/models/caller_details.py
33
33
  microsoft_agents_a365/observability/core/models/operation_source.py
34
+ microsoft_agents_a365/observability/core/models/response.py
35
+ microsoft_agents_a365/observability/core/spans_scopes/__init__.py
36
+ microsoft_agents_a365/observability/core/spans_scopes/output_scope.py
34
37
  microsoft_agents_a365/observability/core/trace_processor/__init__.py
35
38
  microsoft_agents_a365/observability/core/trace_processor/span_processor.py
36
39
  microsoft_agents_a365/observability/core/trace_processor/util.py