microsoft-agents-a365-observability-core 0.2.1.dev33__tar.gz → 0.2.1.dev36__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 (48) hide show
  1. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/PKG-INFO +1 -1
  2. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/__init__.py +7 -1
  3. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/agent_details.py +4 -6
  4. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/config.py +39 -12
  5. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/constants.py +39 -39
  6. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/execute_tool_scope.py +25 -10
  7. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/exporters/__init__.py +2 -1
  8. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +0 -20
  9. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/exporters/enriched_span.py +10 -1
  10. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/exporters/enriching_span_processor.py +30 -0
  11. microsoft_agents_a365_observability_core-0.2.1.dev36/microsoft_agents_a365/observability/core/exporters/spectra_exporter_options.py +56 -0
  12. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/inference_call_details.py +13 -1
  13. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/inference_scope.py +19 -14
  14. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/invoke_agent_scope.py +22 -22
  15. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/middleware/baggage_builder.py +11 -63
  16. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/models/caller_details.py +2 -5
  17. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/opentelemetry_scope.py +36 -21
  18. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/trace_processor/span_processor.py +1 -10
  19. microsoft_agents_a365_observability_core-0.2.1.dev36/microsoft_agents_a365/observability/core/trace_processor/util.py +43 -0
  20. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/utils.py +13 -0
  21. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365_observability_core.egg-info/PKG-INFO +1 -1
  22. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365_observability_core.egg-info/SOURCES.txt +1 -0
  23. microsoft_agents_a365_observability_core-0.2.1.dev33/microsoft_agents_a365/observability/core/trace_processor/util.py +0 -50
  24. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/README.md +0 -0
  25. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/execution_type.py +0 -0
  26. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/exporters/agent365_exporter_options.py +0 -0
  27. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/exporters/utils.py +0 -0
  28. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/inference_operation_type.py +0 -0
  29. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/invoke_agent_details.py +0 -0
  30. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/middleware/__init__.py +0 -0
  31. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/models/__init__.py +0 -0
  32. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/models/agent_type.py +0 -0
  33. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/models/operation_source.py +0 -0
  34. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/models/response.py +0 -0
  35. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/request.py +0 -0
  36. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/source_metadata.py +0 -0
  37. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/spans_scopes/__init__.py +0 -0
  38. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/spans_scopes/output_scope.py +0 -0
  39. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/tenant_details.py +0 -0
  40. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/tool_call_details.py +0 -0
  41. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/tool_type.py +0 -0
  42. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365/observability/core/trace_processor/__init__.py +0 -0
  43. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365_observability_core.egg-info/dependency_links.txt +0 -0
  44. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365_observability_core.egg-info/requires.txt +0 -0
  45. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/microsoft_agents_a365_observability_core.egg-info/top_level.txt +0 -0
  46. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/pyproject.toml +0 -0
  47. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/setup.cfg +0 -0
  48. {microsoft_agents_a365_observability_core-0.2.1.dev33 → microsoft_agents_a365_observability_core-0.2.1.dev36}/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.dev33
3
+ Version: 0.2.1.dev36
4
4
  Summary: Telemetry, tracing, and monitoring components for AI agents
5
5
  Author-email: Microsoft <support@microsoft.com>
6
6
  License: MIT
@@ -12,13 +12,15 @@ from .config import (
12
12
  )
13
13
  from .execute_tool_scope import ExecuteToolScope
14
14
  from .execution_type import ExecutionType
15
+ from .exporters.agent365_exporter_options import Agent365ExporterOptions
15
16
  from .exporters.enriched_span import EnrichedReadableSpan
16
17
  from .exporters.enriching_span_processor import (
17
18
  get_span_enricher,
18
19
  register_span_enricher,
19
20
  unregister_span_enricher,
20
21
  )
21
- from .inference_call_details import InferenceCallDetails
22
+ from .exporters.spectra_exporter_options import SpectraExporterOptions
23
+ from .inference_call_details import InferenceCallDetails, ServiceEndpoint
22
24
  from .inference_operation_type import InferenceOperationType
23
25
  from .inference_scope import InferenceScope
24
26
  from .invoke_agent_details import InvokeAgentDetails
@@ -38,6 +40,9 @@ __all__ = [
38
40
  "is_configured",
39
41
  "get_tracer",
40
42
  "get_tracer_provider",
43
+ # Exporter options
44
+ "Agent365ExporterOptions",
45
+ "SpectraExporterOptions",
41
46
  # Span enrichment
42
47
  "register_span_enricher",
43
48
  "unregister_span_enricher",
@@ -61,6 +66,7 @@ __all__ = [
61
66
  "SourceMetadata",
62
67
  "Request",
63
68
  "InferenceCallDetails",
69
+ "ServiceEndpoint",
64
70
  # Enums
65
71
  "ExecutionType",
66
72
  "InferenceOperationType",
@@ -4,8 +4,6 @@
4
4
  from dataclasses import dataclass
5
5
  from typing import Optional
6
6
 
7
- from .models.agent_type import AgentType
8
-
9
7
 
10
8
  @dataclass
11
9
  class AgentDetails:
@@ -29,8 +27,8 @@ class AgentDetails:
29
27
  agent_blueprint_id: Optional[str] = None
30
28
  """Blueprint/Application ID for the agent."""
31
29
 
32
- agent_type: Optional[AgentType] = None
33
- """The agent type."""
30
+ agent_platform_id: Optional[str] = None
31
+ """Platform ID for the agent."""
34
32
 
35
33
  tenant_id: Optional[str] = None
36
34
  """Tenant ID for the agent."""
@@ -41,5 +39,5 @@ class AgentDetails:
41
39
  icon_uri: Optional[str] = None
42
40
  """Optional icon URI for the agent."""
43
41
 
44
- agent_client_ip: Optional[str] = None
45
- """Client IP address of the agent user."""
42
+ provider_name: Optional[str] = None
43
+ """The provider name (e.g., openai, anthropic)."""
@@ -8,6 +8,9 @@ from collections.abc import Callable
8
8
  from typing import Any, Optional
9
9
 
10
10
  from opentelemetry import trace
11
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
12
+ OTLPSpanExporter as GrpcOTLPSpanExporter,
13
+ )
11
14
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
12
15
  from opentelemetry.sdk.resources import SERVICE_NAME, SERVICE_NAMESPACE, Resource
13
16
  from opentelemetry.sdk.trace import TracerProvider
@@ -18,6 +21,7 @@ from .exporters.agent365_exporter_options import Agent365ExporterOptions
18
21
  from .exporters.enriching_span_processor import (
19
22
  _EnrichingBatchSpanProcessor,
20
23
  )
24
+ from .exporters.spectra_exporter_options import SpectraExporterOptions
21
25
  from .exporters.utils import is_agent365_exporter_enabled
22
26
  from .trace_processor.span_processor import SpanProcessor
23
27
 
@@ -58,7 +62,7 @@ class TelemetryManager:
58
62
  logger_name: str = DEFAULT_LOGGER_NAME,
59
63
  token_resolver: Callable[[str, str], str | None] | None = None,
60
64
  cluster_category: str = "prod",
61
- exporter_options: Optional[Agent365ExporterOptions] = None,
65
+ exporter_options: Agent365ExporterOptions | SpectraExporterOptions | None = None,
62
66
  suppress_invoke_agent_input: bool = False,
63
67
  **kwargs: Any,
64
68
  ) -> bool:
@@ -72,9 +76,10 @@ class TelemetryManager:
72
76
  Use exporter_options instead.
73
77
  :param cluster_category: (Deprecated) Environment / cluster category (e.g. "prod").
74
78
  Use exporter_options instead.
75
- :param exporter_options: Agent365ExporterOptions instance for configuring the exporter.
76
- 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.
77
- :param suppress_invoke_agent_input: If True, suppress input messages for spans that are children of InvokeAgent spans.
79
+ :param exporter_options: Exporter configuration. Pass Agent365ExporterOptions for A365 API
80
+ export, SpectraExporterOptions for Spectra Collector sidecar export, or None (default)
81
+ to construct Agent365ExporterOptions from legacy parameters.
82
+ :param suppress_invoke_agent_input: If True, suppress input messages for InvokeAgent spans.
78
83
  :return: True if configuration succeeded, False otherwise.
79
84
  """
80
85
  try:
@@ -100,7 +105,7 @@ class TelemetryManager:
100
105
  logger_name: str,
101
106
  token_resolver: Callable[[str, str], str | None] | None = None,
102
107
  cluster_category: str = "prod",
103
- exporter_options: Optional[Agent365ExporterOptions] = None,
108
+ exporter_options: Agent365ExporterOptions | SpectraExporterOptions | None = None,
104
109
  suppress_invoke_agent_input: bool = False,
105
110
  **kwargs: Any,
106
111
  ) -> bool:
@@ -156,25 +161,43 @@ class TelemetryManager:
156
161
  "max_export_batch_size": exporter_options.max_export_batch_size,
157
162
  }
158
163
 
159
- if is_agent365_exporter_enabled() and exporter_options.token_resolver is not None:
164
+ # Type-based exporter dispatch
165
+ if isinstance(exporter_options, SpectraExporterOptions):
166
+ # Spectra path — OTLP exporter to sidecar
167
+ # ENABLE_A365_OBSERVABILITY_EXPORTER is intentionally ignored.
168
+ if exporter_options.protocol == "grpc":
169
+ exporter = GrpcOTLPSpanExporter(
170
+ endpoint=exporter_options.endpoint,
171
+ insecure=exporter_options.insecure,
172
+ )
173
+ else:
174
+ exporter = OTLPSpanExporter(
175
+ endpoint=exporter_options.endpoint,
176
+ )
177
+
178
+ elif is_agent365_exporter_enabled() and exporter_options.token_resolver is not None:
160
179
  exporter = _Agent365Exporter(
161
180
  token_resolver=exporter_options.token_resolver,
162
181
  cluster_category=exporter_options.cluster_category,
163
182
  use_s2s_endpoint=exporter_options.use_s2s_endpoint,
164
- suppress_invoke_agent_input=suppress_invoke_agent_input,
165
183
  )
166
184
 
167
185
  else:
168
186
  exporter = ConsoleSpanExporter()
169
187
  self._logger.warning(
170
- "is_agent365_exporter_enabled() not enabled or token_resolver not set. Falling back to console exporter."
188
+ "is_agent365_exporter_enabled() not enabled or token_resolver not set."
189
+ " Falling back to console exporter."
171
190
  )
172
191
 
173
192
  # Add span processors
174
193
 
175
194
  # Create _EnrichingBatchSpanProcessor with optimized settings
176
195
  # This allows extensions to enrich spans before export
177
- batch_processor = _EnrichingBatchSpanProcessor(exporter, **batch_processor_kwargs)
196
+ batch_processor = _EnrichingBatchSpanProcessor(
197
+ exporter,
198
+ suppress_invoke_agent_input=suppress_invoke_agent_input,
199
+ **batch_processor_kwargs,
200
+ )
178
201
  agent_processor = SpanProcessor()
179
202
 
180
203
  tracer_provider.add_span_processor(batch_processor)
@@ -248,7 +271,8 @@ def configure(
248
271
  logger_name: str = DEFAULT_LOGGER_NAME,
249
272
  token_resolver: Callable[[str, str], str | None] | None = None,
250
273
  cluster_category: str = "prod",
251
- exporter_options: Optional[Agent365ExporterOptions] = None,
274
+ exporter_options: Agent365ExporterOptions | SpectraExporterOptions | None = None,
275
+ suppress_invoke_agent_input: bool = False,
252
276
  **kwargs: Any,
253
277
  ) -> bool:
254
278
  """
@@ -261,8 +285,10 @@ def configure(
261
285
  Use exporter_options instead.
262
286
  :param cluster_category: (Deprecated) Environment / cluster category (e.g. "prod").
263
287
  Use exporter_options instead.
264
- :param exporter_options: Agent365ExporterOptions instance for configuring the exporter.
265
- 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.
288
+ :param exporter_options: Exporter configuration. Pass Agent365ExporterOptions for A365 API
289
+ export, SpectraExporterOptions for Spectra Collector sidecar export, or None (default)
290
+ to construct Agent365ExporterOptions from legacy parameters.
291
+ :param suppress_invoke_agent_input: If True, suppress input messages for InvokeAgent spans.
266
292
  :return: True if configuration succeeded, False otherwise.
267
293
  """
268
294
  return _telemetry_manager.configure(
@@ -272,6 +298,7 @@ def configure(
272
298
  token_resolver,
273
299
  cluster_category,
274
300
  exporter_options,
301
+ suppress_invoke_agent_input,
275
302
  **kwargs,
276
303
  )
277
304
 
@@ -6,6 +6,8 @@
6
6
  # Span operation names
7
7
  INVOKE_AGENT_OPERATION_NAME = "invoke_agent"
8
8
  EXECUTE_TOOL_OPERATION_NAME = "execute_tool"
9
+ OUTPUT_MESSAGES_OPERATION_NAME = "output_messages"
10
+ CHAT_OPERATION_NAME = "chat"
9
11
 
10
12
  # OpenTelemetry semantic conventions
11
13
  ERROR_TYPE_KEY = "error.type"
@@ -30,81 +32,79 @@ GEN_AI_REQUEST_MAX_TOKENS_KEY = "gen_ai.request.max_tokens"
30
32
  GEN_AI_REQUEST_MODEL_KEY = "gen_ai.request.model"
31
33
  GEN_AI_REQUEST_TEMPERATURE_KEY = "gen_ai.request.temperature"
32
34
  GEN_AI_REQUEST_TOP_P_KEY = "gen_ai.request.top_p"
33
- GEN_AI_RESPONSE_ID_KEY = "gen_ai.response.id"
34
35
  GEN_AI_RESPONSE_FINISH_REASONS_KEY = "gen_ai.response.finish_reasons"
35
36
  GEN_AI_RESPONSE_MODEL_KEY = "gen_ai.response.model"
36
- GEN_AI_SYSTEM_KEY = "gen_ai.system"
37
- GEN_AI_SYSTEM_VALUE = "az.ai.agent365"
38
- GEN_AI_THOUGHT_PROCESS_KEY = "gen_ai.agent.thought.process"
39
-
37
+ GEN_AI_RESPONSE_ID_KEY = "gen_ai.response.id"
40
38
  GEN_AI_AGENT_ID_KEY = "gen_ai.agent.id"
41
39
  GEN_AI_AGENT_NAME_KEY = "gen_ai.agent.name"
42
40
  GEN_AI_AGENT_DESCRIPTION_KEY = "gen_ai.agent.description"
41
+ GEN_AI_AGENT_PLATFORM_ID_KEY = "microsoft.a365.agent.platform.id"
42
+ GEN_AI_AGENT_THOUGHT_PROCESS_KEY = "microsoft.a365.agent.thought.process"
43
43
  GEN_AI_CONVERSATION_ID_KEY = "gen_ai.conversation.id"
44
- GEN_AI_CONVERSATION_ITEM_LINK_KEY = "gen_ai.conversation.item.link"
44
+ GEN_AI_CONVERSATION_ITEM_LINK_KEY = "microsoft.conversation.item.link"
45
45
  GEN_AI_TOKEN_TYPE_KEY = "gen_ai.token.type"
46
46
  GEN_AI_USAGE_INPUT_TOKENS_KEY = "gen_ai.usage.input_tokens"
47
47
  GEN_AI_USAGE_OUTPUT_TOKENS_KEY = "gen_ai.usage.output_tokens"
48
48
  GEN_AI_CHOICE = "gen_ai.choice"
49
49
  GEN_AI_PROVIDER_NAME_KEY = "gen_ai.provider.name"
50
- GEN_AI_AGENT_TYPE_KEY = "gen_ai.agent.type"
51
50
 
52
51
  GEN_AI_SYSTEM_INSTRUCTIONS_KEY = "gen_ai.system_instructions"
53
52
  GEN_AI_INPUT_MESSAGES_KEY = "gen_ai.input.messages"
54
53
  GEN_AI_OUTPUT_MESSAGES_KEY = "gen_ai.output.messages"
55
- GEN_AI_EVENT_CONTENT = "gen_ai.event.content"
56
54
 
57
55
  # Tool execution constants
58
56
  GEN_AI_TOOL_CALL_ID_KEY = "gen_ai.tool.call.id"
59
57
  GEN_AI_TOOL_NAME_KEY = "gen_ai.tool.name"
60
58
  GEN_AI_TOOL_DESCRIPTION_KEY = "gen_ai.tool.description"
61
- GEN_AI_TOOL_ARGS_KEY = "gen_ai.tool.arguments"
62
- GEN_AI_TOOL_CALL_RESULT_KEY = GEN_AI_EVENT_CONTENT # GEN_AI_EVENT_CONTENT
59
+ GEN_AI_TOOL_ARGS_KEY = "gen_ai.tool.call.arguments"
60
+ GEN_AI_TOOL_CALL_RESULT_KEY = "gen_ai.tool.call.result"
63
61
  GEN_AI_TOOL_TYPE_KEY = "gen_ai.tool.type"
64
62
 
65
- # Agent user(user tied to agent instance during creation) or caller dimensions
66
- GEN_AI_AGENT_USER_ID_KEY = "gen_ai.agent.userid"
67
- GEN_AI_CALLER_USER_ID_KEY = "gen_ai.caller.userid"
68
- GEN_AI_CALLER_TENANT_ID_KEY = "gen_ai.caller.tenantid"
69
- GEN_AI_CALLER_ID_KEY = "gen_ai.caller.id"
70
- GEN_AI_CALLER_NAME_KEY = "gen_ai.caller.name"
71
- GEN_AI_CALLER_UPN_KEY = "gen_ai.caller.upn"
72
- GEN_AI_CALLER_CLIENT_IP_KEY = "gen_ai.caller.client.ip"
63
+ # Agent user (user tied to agent instance during creation) or caller dimensions
64
+ GEN_AI_CALLER_ID_KEY = "microsoft.caller.id"
65
+ GEN_AI_CALLER_NAME_KEY = "microsoft.caller.name"
66
+ GEN_AI_CALLER_UPN_KEY = "microsoft.caller.upn"
67
+ GEN_AI_CALLER_CLIENT_IP_KEY = "client.address"
73
68
 
74
69
  # Agent to Agent caller agent dimensions
75
- GEN_AI_CALLER_AGENT_USER_ID_KEY = "gen_ai.caller.agent.userid"
76
- GEN_AI_CALLER_AGENT_UPN_KEY = "gen_ai.caller.agent.upn"
77
- GEN_AI_CALLER_AGENT_TENANT_ID_KEY = "gen_ai.caller.agent.tenantid"
78
- GEN_AI_CALLER_AGENT_NAME_KEY = "gen_ai.caller.agent.name"
79
- GEN_AI_CALLER_AGENT_ID_KEY = "gen_ai.caller.agent.id"
80
- GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = "gen_ai.caller.agent.applicationid"
81
- GEN_AI_CALLER_AGENT_TYPE_KEY = "gen_ai.caller.agent.type"
82
- GEN_AI_CALLER_AGENT_USER_CLIENT_IP = "gen_ai.caller.agent.user.client.ip"
70
+ GEN_AI_CALLER_AGENT_USER_ID_KEY = "microsoft.a365.caller.agent.user.id"
71
+ GEN_AI_CALLER_AGENT_UPN_KEY = "microsoft.a365.caller.agent.user.upn"
72
+ GEN_AI_CALLER_AGENT_NAME_KEY = "microsoft.a365.caller.agent.name"
73
+ GEN_AI_CALLER_AGENT_ID_KEY = "microsoft.a365.caller.agent.id"
74
+ GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = "microsoft.a365.caller.agent.blueprint.id"
75
+ GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY = "microsoft.a365.caller.agent.platform.id"
83
76
 
84
77
  # Agent-specific dimensions
85
78
  AGENT_ID_KEY = "gen_ai.agent.id"
86
79
  GEN_AI_TASK_ID_KEY = "gen_ai.task.id"
87
- SESSION_ID_KEY = "session.id"
80
+ SESSION_ID_KEY = "microsoft.session.id"
81
+ SESSION_DESCRIPTION_KEY = "microsoft.session.description"
88
82
  GEN_AI_ICON_URI_KEY = "gen_ai.agent365.icon_uri"
89
- TENANT_ID_KEY = "tenant.id"
83
+ TENANT_ID_KEY = "microsoft.tenant.id"
90
84
 
91
85
  # Baggage keys
92
- OPERATION_SOURCE_KEY = "operation.source"
93
- GEN_AI_AGENT_AUID_KEY = "gen_ai.agent.user.id"
94
- GEN_AI_AGENT_UPN_KEY = "gen_ai.agent.upn"
95
- GEN_AI_AGENT_BLUEPRINT_ID_KEY = "gen_ai.agent.applicationid"
96
- CORRELATION_ID_KEY = "correlation.id"
97
- HIRING_MANAGER_ID_KEY = "hiring.manager.id"
98
- SESSION_DESCRIPTION_KEY = "session.description"
86
+ GEN_AI_AGENT_AUID_KEY = "microsoft.agent.user.id"
87
+ GEN_AI_AGENT_UPN_KEY = "microsoft.agent.user.upn"
88
+ GEN_AI_AGENT_BLUEPRINT_ID_KEY = "microsoft.a365.agent.blueprint.id"
99
89
 
100
90
  # Execution context dimensions
101
91
  GEN_AI_EXECUTION_TYPE_KEY = "gen_ai.execution.type"
102
92
  GEN_AI_EXECUTION_PAYLOAD_KEY = "gen_ai.execution.payload"
103
93
 
104
- # Source metadata dimensions
105
- GEN_AI_EXECUTION_SOURCE_NAME_KEY = "gen_ai.channel.name"
106
- GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY = "gen_ai.channel.link"
94
+ # Channel dimensions
95
+ CHANNEL_NAME_KEY = "microsoft.channel.name"
96
+ CHANNEL_LINK_KEY = "microsoft.channel.link"
107
97
 
108
- # custom parent id and parent name key
98
+ # Custom parent id and parent name key
109
99
  CUSTOM_PARENT_SPAN_ID_KEY = "custom.parent.span.id"
110
100
  CUSTOM_SPAN_NAME_KEY = "custom.span.name"
101
+
102
+ # Service attributes
103
+ SERVICE_NAME_KEY = "service.name"
104
+
105
+ # Telemetry SDK attributes
106
+ TELEMETRY_SDK_NAME_KEY = "telemetry.sdk.name"
107
+ TELEMETRY_SDK_LANGUAGE_KEY = "telemetry.sdk.language"
108
+ TELEMETRY_SDK_VERSION_KEY = "telemetry.sdk.version"
109
+ TELEMETRY_SDK_NAME_VALUE = "A365ObservabilitySDK"
110
+ TELEMETRY_SDK_LANGUAGE_VALUE = "python"
@@ -3,12 +3,13 @@
3
3
 
4
4
  from datetime import datetime
5
5
 
6
+ from opentelemetry.trace import SpanKind
7
+
6
8
  from .agent_details import AgentDetails
7
9
  from .constants import (
10
+ CHANNEL_LINK_KEY,
11
+ CHANNEL_NAME_KEY,
8
12
  EXECUTE_TOOL_OPERATION_NAME,
9
- GEN_AI_EVENT_CONTENT,
10
- GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY,
11
- GEN_AI_EXECUTION_SOURCE_NAME_KEY,
12
13
  GEN_AI_TOOL_ARGS_KEY,
13
14
  GEN_AI_TOOL_CALL_ID_KEY,
14
15
  GEN_AI_TOOL_DESCRIPTION_KEY,
@@ -35,6 +36,7 @@ class ExecuteToolScope(OpenTelemetryScope):
35
36
  parent_id: str | None = None,
36
37
  start_time: datetime | None = None,
37
38
  end_time: datetime | None = None,
39
+ span_kind: SpanKind | None = None,
38
40
  ) -> "ExecuteToolScope":
39
41
  """Creates and starts a new scope for tool execution tracing.
40
42
 
@@ -50,12 +52,21 @@ class ExecuteToolScope(OpenTelemetryScope):
50
52
  end_time: Optional explicit end time as a datetime object. When provided,
51
53
  the span will use this timestamp when disposed instead of the
52
54
  current wall-clock time.
55
+ span_kind: Optional span kind override. Defaults to ``SpanKind.INTERNAL``.
56
+ Use ``SpanKind.CLIENT`` when the tool calls an external service.
53
57
 
54
58
  Returns:
55
59
  A new ExecuteToolScope instance
56
60
  """
57
61
  return ExecuteToolScope(
58
- details, agent_details, tenant_details, request, parent_id, start_time, end_time
62
+ details,
63
+ agent_details,
64
+ tenant_details,
65
+ request,
66
+ parent_id,
67
+ start_time,
68
+ end_time,
69
+ span_kind,
59
70
  )
60
71
 
61
72
  def __init__(
@@ -67,6 +78,7 @@ class ExecuteToolScope(OpenTelemetryScope):
67
78
  parent_id: str | None = None,
68
79
  start_time: datetime | None = None,
69
80
  end_time: datetime | None = None,
81
+ span_kind: SpanKind | None = None,
70
82
  ):
71
83
  """Initialize the tool execution scope.
72
84
 
@@ -82,9 +94,11 @@ class ExecuteToolScope(OpenTelemetryScope):
82
94
  end_time: Optional explicit end time as a datetime object. When provided,
83
95
  the span will use this timestamp when disposed instead of the
84
96
  current wall-clock time.
97
+ span_kind: Optional span kind override. Defaults to ``SpanKind.INTERNAL``.
98
+ Use ``SpanKind.CLIENT`` when the tool calls an external service.
85
99
  """
86
100
  super().__init__(
87
- kind="Internal",
101
+ kind=span_kind if span_kind is not None else SpanKind.INTERNAL,
88
102
  operation_name=EXECUTE_TOOL_OPERATION_NAME,
89
103
  activity_name=f"{EXECUTE_TOOL_OPERATION_NAME} {details.tool_name}",
90
104
  agent_details=agent_details,
@@ -115,15 +129,16 @@ class ExecuteToolScope(OpenTelemetryScope):
115
129
 
116
130
  # Set request metadata if provided
117
131
  if request and request.source_metadata:
118
- self.set_tag_maybe(GEN_AI_EXECUTION_SOURCE_NAME_KEY, request.source_metadata.name)
119
- self.set_tag_maybe(
120
- GEN_AI_EXECUTION_SOURCE_DESCRIPTION_KEY, request.source_metadata.description
121
- )
132
+ self.set_tag_maybe(CHANNEL_NAME_KEY, request.source_metadata.name)
133
+ self.set_tag_maybe(CHANNEL_LINK_KEY, request.source_metadata.description)
122
134
 
123
135
  def record_response(self, response: str) -> None:
124
136
  """Records response information for telemetry tracking.
125
137
 
138
+ Note: This method is intentionally a no-op as GEN_AI_EVENT_CONTENT was removed.
139
+ The method is kept for interface compatibility.
140
+
126
141
  Args:
127
142
  response: The response to record
128
143
  """
129
- self.set_tag_maybe(GEN_AI_EVENT_CONTENT, response)
144
+ pass
@@ -2,7 +2,8 @@
2
2
  # Licensed under the MIT License.
3
3
 
4
4
  from .agent365_exporter_options import Agent365ExporterOptions
5
+ from .spectra_exporter_options import SpectraExporterOptions
5
6
 
6
7
  # Agent365Exporter is not exported intentionally.
7
8
  # It should only be used internally by the observability core module.
8
- __all__ = ["Agent365ExporterOptions"]
9
+ __all__ = ["Agent365ExporterOptions", "SpectraExporterOptions"]
@@ -17,11 +17,6 @@ from opentelemetry.sdk.trace import ReadableSpan
17
17
  from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
18
18
  from opentelemetry.trace import StatusCode
19
19
 
20
- from ..constants import (
21
- GEN_AI_INPUT_MESSAGES_KEY,
22
- GEN_AI_OPERATION_NAME_KEY,
23
- INVOKE_AGENT_OPERATION_NAME,
24
- )
25
20
  from .utils import (
26
21
  build_export_url,
27
22
  get_validated_domain_override,
@@ -59,7 +54,6 @@ class _Agent365Exporter(SpanExporter):
59
54
  token_resolver: Callable[[str, str], str | None],
60
55
  cluster_category: str = "prod",
61
56
  use_s2s_endpoint: bool = False,
62
- suppress_invoke_agent_input: bool = False,
63
57
  ):
64
58
  if token_resolver is None:
65
59
  raise ValueError("token_resolver must be provided.")
@@ -69,7 +63,6 @@ class _Agent365Exporter(SpanExporter):
69
63
  self._token_resolver = token_resolver
70
64
  self._cluster_category = cluster_category
71
65
  self._use_s2s_endpoint = use_s2s_endpoint
72
- self._suppress_invoke_agent_input = suppress_invoke_agent_input
73
66
  # Read domain override once at initialization
74
67
  self._domain_override = get_validated_domain_override()
75
68
 
@@ -270,19 +263,6 @@ class _Agent365Exporter(SpanExporter):
270
263
  # attributes
271
264
  attrs = dict(sp.attributes or {})
272
265
 
273
- # Suppress input messages if configured and current span is an InvokeAgent span
274
- if self._suppress_invoke_agent_input:
275
- # Check if current span is an InvokeAgent span by:
276
- # 1. Span name starts with "invoke_agent"
277
- # 2. Has attribute gen_ai.operation.name set to INVOKE_AGENT_OPERATION_NAME
278
- operation_name = attrs.get(GEN_AI_OPERATION_NAME_KEY)
279
- if (
280
- sp.name.startswith(INVOKE_AGENT_OPERATION_NAME)
281
- and operation_name == INVOKE_AGENT_OPERATION_NAME
282
- ):
283
- # Remove input messages attribute
284
- attrs.pop(GEN_AI_INPUT_MESSAGES_KEY, None)
285
-
286
266
  # events
287
267
  events = []
288
268
  for ev in sp.events:
@@ -19,22 +19,31 @@ class EnrichedReadableSpan(ReadableSpan):
19
19
  the original span.
20
20
  """
21
21
 
22
- def __init__(self, span: ReadableSpan, extra_attributes: dict):
22
+ def __init__(
23
+ self,
24
+ span: ReadableSpan,
25
+ extra_attributes: dict,
26
+ excluded_attribute_keys: set[str] | None = None,
27
+ ):
23
28
  """
24
29
  Initialize the enriched span wrapper.
25
30
 
26
31
  Args:
27
32
  span: The original ReadableSpan to wrap.
28
33
  extra_attributes: Additional attributes to merge with the original.
34
+ excluded_attribute_keys: Attribute keys to remove after merging.
29
35
  """
30
36
  self._span = span
31
37
  self._extra_attributes = extra_attributes
38
+ self._excluded_attribute_keys = excluded_attribute_keys or set()
32
39
 
33
40
  @property
34
41
  def attributes(self) -> types.Attributes:
35
42
  """Return merged attributes from original span and extra attributes."""
36
43
  original = dict(self._span.attributes or {})
37
44
  original.update(self._extra_attributes)
45
+ for key in self._excluded_attribute_keys:
46
+ original.pop(key, None)
38
47
  return original
39
48
 
40
49
  @property
@@ -10,6 +10,13 @@ from collections.abc import Callable
10
10
  from opentelemetry.sdk.trace import ReadableSpan
11
11
  from opentelemetry.sdk.trace.export import BatchSpanProcessor
12
12
 
13
+ from ..constants import (
14
+ GEN_AI_INPUT_MESSAGES_KEY,
15
+ GEN_AI_OPERATION_NAME_KEY,
16
+ INVOKE_AGENT_OPERATION_NAME,
17
+ )
18
+ from .enriched_span import EnrichedReadableSpan
19
+
13
20
  logger = logging.getLogger(__name__)
14
21
 
15
22
  # Single span enricher - only one platform instrumentor should be active at a time
@@ -65,6 +72,15 @@ def get_span_enricher() -> Callable[[ReadableSpan], ReadableSpan] | None:
65
72
  class _EnrichingBatchSpanProcessor(BatchSpanProcessor):
66
73
  """BatchSpanProcessor that applies the registered enricher before batching."""
67
74
 
75
+ def __init__(
76
+ self,
77
+ *args: object,
78
+ suppress_invoke_agent_input: bool = False,
79
+ **kwargs: object,
80
+ ):
81
+ super().__init__(*args, **kwargs)
82
+ self._suppress_invoke_agent_input = suppress_invoke_agent_input
83
+
68
84
  def on_end(self, span: ReadableSpan) -> None:
69
85
  """Apply the span enricher and pass to parent for batching.
70
86
 
@@ -83,4 +99,18 @@ class _EnrichingBatchSpanProcessor(BatchSpanProcessor):
83
99
  enricher.__name__,
84
100
  )
85
101
 
102
+ # Apply input message suppression for InvokeAgent spans
103
+ if self._suppress_invoke_agent_input:
104
+ attrs = enriched_span.attributes or {}
105
+ operation_name = attrs.get(GEN_AI_OPERATION_NAME_KEY)
106
+ if (
107
+ enriched_span.name.startswith(INVOKE_AGENT_OPERATION_NAME)
108
+ and operation_name == INVOKE_AGENT_OPERATION_NAME
109
+ ):
110
+ enriched_span = EnrichedReadableSpan(
111
+ enriched_span,
112
+ extra_attributes={},
113
+ excluded_attribute_keys={GEN_AI_INPUT_MESSAGES_KEY},
114
+ )
115
+
86
116
  super().on_end(enriched_span)
@@ -0,0 +1,56 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ from typing import Literal
5
+
6
+
7
+ class SpectraExporterOptions:
8
+ """
9
+ Configuration for exporting traces to a Spectra Collector sidecar via OTLP.
10
+
11
+ Spectra Collector is deployed as a Kubernetes sidecar that accepts
12
+ standard OTLP telemetry on localhost. Defaults are tuned for this
13
+ deployment topology — most consumers should not need to override them.
14
+
15
+ Note: Batch processor fields (max_queue_size, scheduled_delay_ms, etc.)
16
+ are duplicated from Agent365ExporterOptions intentionally — these two
17
+ options classes have no shared base class per design decision C4.
18
+ """
19
+
20
+ _DEFAULT_GRPC_ENDPOINT = "http://localhost:4317"
21
+ _DEFAULT_HTTP_ENDPOINT = "http://localhost:4318"
22
+
23
+ def __init__(
24
+ self,
25
+ endpoint: str | None = None,
26
+ protocol: Literal["grpc", "http"] = "grpc",
27
+ insecure: bool = True,
28
+ max_queue_size: int = 2048,
29
+ scheduled_delay_ms: int = 5000,
30
+ exporter_timeout_ms: int = 30000,
31
+ max_export_batch_size: int = 512,
32
+ ):
33
+ """
34
+ Args:
35
+ endpoint: Spectra sidecar OTLP endpoint. Defaults to
36
+ http://localhost:4317 for gRPC or http://localhost:4318 for HTTP.
37
+ protocol: OTLP protocol — "grpc" or "http". Default: grpc.
38
+ insecure: Use insecure (no TLS) connection. Default: True (localhost sidecar).
39
+ max_queue_size: Batch processor queue size. Default: 2048.
40
+ scheduled_delay_ms: Export interval in milliseconds. Default: 5000.
41
+ exporter_timeout_ms: Export timeout in milliseconds. Default: 30000.
42
+ max_export_batch_size: Max spans per export batch. Default: 512.
43
+ """
44
+ if protocol not in ("grpc", "http"):
45
+ raise ValueError(f"protocol must be 'grpc' or 'http', got '{protocol}'")
46
+ if endpoint is None:
47
+ endpoint = (
48
+ self._DEFAULT_GRPC_ENDPOINT if protocol == "grpc" else self._DEFAULT_HTTP_ENDPOINT
49
+ )
50
+ self.endpoint = endpoint
51
+ self.protocol = protocol
52
+ self.insecure = insecure
53
+ self.max_queue_size = max_queue_size
54
+ self.scheduled_delay_ms = scheduled_delay_ms
55
+ self.exporter_timeout_ms = exporter_timeout_ms
56
+ self.max_export_batch_size = max_export_batch_size
@@ -6,6 +6,17 @@ from dataclasses import dataclass
6
6
  from .inference_operation_type import InferenceOperationType
7
7
 
8
8
 
9
+ @dataclass
10
+ class ServiceEndpoint:
11
+ """Represents a service endpoint with hostname and optional port."""
12
+
13
+ hostname: str
14
+ """The hostname of the service endpoint."""
15
+
16
+ port: int | None = None
17
+ """The port of the service endpoint."""
18
+
19
+
9
20
  @dataclass
10
21
  class InferenceCallDetails:
11
22
  """Details of an inference call for generative AI operations."""
@@ -16,4 +27,5 @@ class InferenceCallDetails:
16
27
  inputTokens: int | None = None
17
28
  outputTokens: int | None = None
18
29
  finishReasons: list[str] | None = None
19
- responseId: str | None = None
30
+ thoughtProcess: str | None = None
31
+ endpoint: ServiceEndpoint | None = None