microsoft-agents-a365-observability-extensions-semantic-kernel 0.2.1.dev5__py3-none-any.whl → 0.2.1.dev9__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.
@@ -0,0 +1,75 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """Span enricher for Semantic Kernel."""
5
+
6
+ from microsoft_agents_a365.observability.core.constants import (
7
+ EXECUTE_TOOL_OPERATION_NAME,
8
+ GEN_AI_INPUT_MESSAGES_KEY,
9
+ GEN_AI_OUTPUT_MESSAGES_KEY,
10
+ GEN_AI_TOOL_ARGS_KEY,
11
+ GEN_AI_TOOL_CALL_RESULT_KEY,
12
+ INVOKE_AGENT_OPERATION_NAME,
13
+ )
14
+ from microsoft_agents_a365.observability.core.exporters.enriched_span import EnrichedReadableSpan
15
+ from opentelemetry.sdk.trace import ReadableSpan
16
+
17
+ from .utils import extract_content_as_string_list
18
+
19
+ # Semantic Kernel specific attribute keys
20
+ SK_TOOL_CALL_ARGUMENTS_KEY = "gen_ai.tool.call.arguments"
21
+ SK_TOOL_CALL_RESULT_KEY = "gen_ai.tool.call.result"
22
+
23
+
24
+ def enrich_semantic_kernel_span(span: ReadableSpan) -> ReadableSpan:
25
+ """
26
+ Enricher function for Semantic Kernel spans.
27
+
28
+ Transforms SK-specific attributes to standard gen_ai attributes
29
+ before the span is exported. Enrichment is applied based on span type:
30
+ - invoke_agent spans: Extract only content from input/output messages
31
+ - execute_tool spans: Map tool arguments and results to standard keys
32
+
33
+ Args:
34
+ span: The ReadableSpan to enrich.
35
+
36
+ Returns:
37
+ The enriched span (wrapped if attributes were added), or the
38
+ original span if no enrichment was needed.
39
+ """
40
+ extra_attributes = {}
41
+ attributes = span.attributes or {}
42
+
43
+ # Only extract content for invoke_agent spans
44
+ if span.name.startswith(INVOKE_AGENT_OPERATION_NAME):
45
+ # Transform SK-specific agent invocation attributes to standard gen_ai attributes
46
+ # Extract only the content from the full message objects
47
+ # Support both gen_ai.agent.invocation_input and gen_ai.input_messages as sources
48
+ input_messages = attributes.get("gen_ai.agent.invocation_input") or attributes.get(
49
+ GEN_AI_INPUT_MESSAGES_KEY
50
+ )
51
+ if input_messages:
52
+ extra_attributes[GEN_AI_INPUT_MESSAGES_KEY] = extract_content_as_string_list(
53
+ input_messages
54
+ )
55
+
56
+ output_messages = attributes.get("gen_ai.agent.invocation_output") or attributes.get(
57
+ GEN_AI_OUTPUT_MESSAGES_KEY
58
+ )
59
+ if output_messages:
60
+ extra_attributes[GEN_AI_OUTPUT_MESSAGES_KEY] = extract_content_as_string_list(
61
+ output_messages
62
+ )
63
+
64
+ # Map tool attributes for execute_tool spans
65
+ elif span.name.startswith(EXECUTE_TOOL_OPERATION_NAME):
66
+ if SK_TOOL_CALL_ARGUMENTS_KEY in attributes:
67
+ extra_attributes[GEN_AI_TOOL_ARGS_KEY] = attributes[SK_TOOL_CALL_ARGUMENTS_KEY]
68
+
69
+ if SK_TOOL_CALL_RESULT_KEY in attributes:
70
+ extra_attributes[GEN_AI_TOOL_CALL_RESULT_KEY] = attributes[SK_TOOL_CALL_RESULT_KEY]
71
+
72
+ if extra_attributes:
73
+ return EnrichedReadableSpan(span, extra_attributes)
74
+
75
+ return span
@@ -1,11 +1,16 @@
1
1
  # Copyright (c) Microsoft Corporation.
2
2
  # Licensed under the MIT License.
3
3
 
4
- # Custom Span Processor
5
-
6
- from microsoft_agents_a365.observability.core.constants import GEN_AI_OPERATION_NAME_KEY
4
+ from microsoft_agents_a365.observability.core.constants import (
5
+ GEN_AI_EXECUTION_TYPE_KEY,
6
+ GEN_AI_OPERATION_NAME_KEY,
7
+ INVOKE_AGENT_OPERATION_NAME,
8
+ )
9
+ from microsoft_agents_a365.observability.core.execution_type import ExecutionType
7
10
  from microsoft_agents_a365.observability.core.inference_operation_type import InferenceOperationType
8
11
  from microsoft_agents_a365.observability.core.utils import extract_model_name
12
+ from opentelemetry import context as context_api
13
+ from opentelemetry.sdk.trace import ReadableSpan, Span
9
14
  from opentelemetry.sdk.trace.export import SpanProcessor
10
15
 
11
16
 
@@ -15,13 +20,42 @@ class SemanticKernelSpanProcessor(SpanProcessor):
15
20
  """
16
21
 
17
22
  def __init__(self, service_name: str | None = None):
23
+ """
24
+ Initialize the Semantic Kernel span processor.
25
+
26
+ Args:
27
+ service_name: Optional service name for span enrichment.
28
+ """
18
29
  self.service_name = service_name
19
30
 
20
- def on_start(self, span, parent_context):
31
+ def on_start(self, span: Span, parent_context: context_api.Context | None) -> None:
32
+ """
33
+ Modify span while it's still writable.
34
+
35
+ Args:
36
+ span: The span that is starting (writable).
37
+ parent_context: The parent context of the span.
38
+ """
21
39
  if span.name.startswith("chat."):
22
40
  span.set_attribute(GEN_AI_OPERATION_NAME_KEY, InferenceOperationType.CHAT.value.lower())
23
41
  model_name = extract_model_name(span.name)
24
42
  span.update_name(f"{InferenceOperationType.CHAT.value.lower()} {model_name}")
25
43
 
26
- def on_end(self, span):
44
+ if span.name.startswith(INVOKE_AGENT_OPERATION_NAME):
45
+ span.set_attribute(
46
+ GEN_AI_EXECUTION_TYPE_KEY, ExecutionType.HUMAN_TO_AGENT.value.lower()
47
+ )
48
+
49
+ def on_end(self, span: ReadableSpan) -> None:
50
+ """
51
+ Called when a span ends.
52
+ """
27
53
  pass
54
+
55
+ def shutdown(self) -> None:
56
+ """Shutdown the processor."""
57
+ pass
58
+
59
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
60
+ """Force flush any pending spans."""
61
+ return True
@@ -6,30 +6,35 @@ from __future__ import annotations
6
6
  from collections.abc import Collection
7
7
  from typing import Any
8
8
 
9
- from microsoft_agents_a365.observability.core.config import get_tracer_provider, is_configured
9
+ from microsoft_agents_a365.observability.core.config import (
10
+ get_tracer_provider,
11
+ is_configured,
12
+ )
13
+ from microsoft_agents_a365.observability.core.exporters.enriching_span_processor import (
14
+ register_span_enricher,
15
+ unregister_span_enricher,
16
+ )
10
17
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
11
18
 
19
+ from microsoft_agents_a365.observability.extensions.semantickernel.span_enricher import (
20
+ enrich_semantic_kernel_span,
21
+ )
12
22
  from microsoft_agents_a365.observability.extensions.semantickernel.span_processor import (
13
23
  SemanticKernelSpanProcessor,
14
24
  )
15
25
 
16
- # -----------------------------
17
- # 3) The Instrumentor class
18
- # -----------------------------
19
26
  _instruments = ("semantic-kernel >= 1.0.0",)
20
27
 
21
28
 
22
29
  class SemanticKernelInstrumentor(BaseInstrumentor):
23
30
  """
24
- Instruments Semantic Kernel:
25
- • Installs your custom OTel SpanProcessor
26
- • (Optionally) attaches an SK function-invocation filter to enrich spans
31
+ Instruments Semantic Kernel with Agent365 observability.
27
32
  """
28
33
 
29
34
  def __init__(self):
30
35
  if not is_configured():
31
36
  raise RuntimeError(
32
- "Microsoft Agent 365 (or your telemetry config) is not initialized. Configure it before instrumenting."
37
+ "Microsoft Agent 365 is not initialized. Call configure() before instrumenting."
33
38
  )
34
39
  super().__init__()
35
40
 
@@ -38,13 +43,32 @@ class SemanticKernelInstrumentor(BaseInstrumentor):
38
43
 
39
44
  def _instrument(self, **kwargs: Any) -> None:
40
45
  """
41
- kwargs (all optional):
42
- """
46
+ Instrument Semantic Kernel.
43
47
 
44
- # Ensure we have an SDK TracerProvider
48
+ Args:
49
+ **kwargs: Optional configuration parameters.
50
+ """
45
51
  provider = get_tracer_provider()
52
+
53
+ # Add processor for on_start modifications (rename spans, add attributes)
46
54
  self._processor = SemanticKernelSpanProcessor()
47
55
  provider.add_span_processor(self._processor)
48
56
 
57
+ # Register enricher for on_end modifications
58
+ # This enricher runs before the span is exported, allowing us to
59
+ # transform SK-specific attributes to standard gen_ai attributes
60
+ register_span_enricher(enrich_semantic_kernel_span)
61
+
49
62
  def _uninstrument(self, **kwargs: Any) -> None:
50
- pass
63
+ """
64
+ Remove Semantic Kernel instrumentation.
65
+
66
+ Args:
67
+ **kwargs: Optional configuration parameters.
68
+ """
69
+ # Unregister the enricher
70
+ unregister_span_enricher()
71
+
72
+ # Shutdown the processor
73
+ if hasattr(self, "_processor"):
74
+ self._processor.shutdown()
@@ -0,0 +1,37 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """Utility functions for Semantic Kernel observability extensions."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import json
9
+
10
+
11
+ def extract_content_as_string_list(messages_json: str) -> str:
12
+ """Extract content values from messages JSON and return as JSON string list.
13
+
14
+ Transforms from: [{"role": "user", "content": "Hello"}]
15
+ To: ["Hello"]
16
+
17
+ Args:
18
+ messages_json: JSON string like '[{"role": "user", "content": "Hello"}]'
19
+
20
+ Returns:
21
+ JSON string containing only the content values as an array,
22
+ or the original string if parsing fails.
23
+ """
24
+ try:
25
+ messages = json.loads(messages_json)
26
+ if isinstance(messages, list):
27
+ contents = []
28
+ for msg in messages:
29
+ if isinstance(msg, dict) and "content" in msg:
30
+ contents.append(msg["content"])
31
+ elif isinstance(msg, str):
32
+ contents.append(msg)
33
+ return json.dumps(contents)
34
+ return messages_json
35
+ except (json.JSONDecodeError, TypeError):
36
+ # If parsing fails, return as-is
37
+ return messages_json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microsoft-agents-a365-observability-extensions-semantic-kernel
3
- Version: 0.2.1.dev5
3
+ Version: 0.2.1.dev9
4
4
  Summary: Semantic Kernel observability and tracing extensions for Microsoft Agent 365
5
5
  Author-email: Microsoft <support@microsoft.com>
6
6
  License: MIT
@@ -0,0 +1,9 @@
1
+ microsoft_agents_a365/observability/extensions/semantickernel/__init__.py,sha256=ftH5IKh1CBMvS1AYGEh6md7b7NX_80ZoIORUElbKBg4,74
2
+ microsoft_agents_a365/observability/extensions/semantickernel/span_enricher.py,sha256=OTeTffta2QcI57nNwLUhtftucZ6Ctoe4AG7ETvy6w9c,2872
3
+ microsoft_agents_a365/observability/extensions/semantickernel/span_processor.py,sha256=_pbNx_qc_PhQq79A37JozVr-FqwrvRJL_wV1a2OFHmw,2122
4
+ microsoft_agents_a365/observability/extensions/semantickernel/trace_instrumentor.py,sha256=KaGekkBMYY2jZcbuB46tSWGbCiu0a394eZKnQDlEXto,2306
5
+ microsoft_agents_a365/observability/extensions/semantickernel/utils.py,sha256=RdV0hlCnPD_i0g-CXKmN0qD5bybDhPHIiGPHjrMDNgY,1185
6
+ microsoft_agents_a365_observability_extensions_semantic_kernel-0.2.1.dev9.dist-info/METADATA,sha256=kxbSpbqbQ8RUTha_eTvVtT9qXMp4NomEWjkyHlfIpyQ,3713
7
+ microsoft_agents_a365_observability_extensions_semantic_kernel-0.2.1.dev9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
+ microsoft_agents_a365_observability_extensions_semantic_kernel-0.2.1.dev9.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
9
+ microsoft_agents_a365_observability_extensions_semantic_kernel-0.2.1.dev9.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- microsoft_agents_a365/observability/extensions/semantickernel/__init__.py,sha256=ftH5IKh1CBMvS1AYGEh6md7b7NX_80ZoIORUElbKBg4,74
2
- microsoft_agents_a365/observability/extensions/semantickernel/span_processor.py,sha256=OFlXRbHY4YjM3FnvIaIzjMFCMf-xRnxuni6eIGNbdpg,993
3
- microsoft_agents_a365/observability/extensions/semantickernel/trace_instrumentor.py,sha256=avsopF76V_n5L6aAcbILlQTJ8af-PmKBYxGu3DCxDko,1551
4
- microsoft_agents_a365_observability_extensions_semantic_kernel-0.2.1.dev5.dist-info/METADATA,sha256=fyKBYAEivxrEhMhc8bIoJRwkeF9QnZSFPbapiti6Wug,3713
5
- microsoft_agents_a365_observability_extensions_semantic_kernel-0.2.1.dev5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
6
- microsoft_agents_a365_observability_extensions_semantic_kernel-0.2.1.dev5.dist-info/top_level.txt,sha256=G3c2_4sy5_EM_BWO67SbK2tKj4G8XFn-QXRbh8g9Lgk,22
7
- microsoft_agents_a365_observability_extensions_semantic_kernel-0.2.1.dev5.dist-info/RECORD,,