Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright (c) Microsoft. All rights reserved.

import logging
import os

from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter, AzureMonitorTraceExporter
from dotenv import load_dotenv
from opentelemetry import trace
from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.semconv.resource import ResourceAttributes
from opentelemetry.trace import set_tracer_provider
from opentelemetry.trace.span import format_trace_id

load_dotenv()

APPINSIGHTS_CONNECTION_STRING = os.getenv("APPINSIGHTS_CONNECTION_STRING")

resource = Resource.create({ResourceAttributes.SERVICE_NAME: "multi_agent_orchestration_sample"})


def set_up_logging():
class KernelFilter(logging.Filter):
"""A filter to not process records from semantic_kernel."""

# These are the namespaces that we want to exclude from logging for the purposes of this demo.
namespaces_to_exclude: list[str] = [
"semantic_kernel.functions.kernel_plugin",
"semantic_kernel.prompt_template.kernel_prompt_template",
]

def filter(self, record):
return not any([record.name.startswith(namespace) for namespace in self.namespaces_to_exclude])

exporters = []
exporters.append(AzureMonitorLogExporter(connection_string=APPINSIGHTS_CONNECTION_STRING))

# Create and set a global logger provider for the application.
logger_provider = LoggerProvider(resource=resource)
# Log processors are initialized with an exporter which is responsible
# for sending the telemetry data to a particular backend.
for log_exporter in exporters:
logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter))
# Sets the global default logger provider
set_logger_provider(logger_provider)

# Create a logging handler to write logging records, in OTLP format, to the exporter.
handler = LoggingHandler()
# Add filters to the handler to only process records from semantic_kernel.
handler.addFilter(logging.Filter("semantic_kernel"))
handler.addFilter(KernelFilter())
# Attach the handler to the root logger. `getLogger()` with no arguments returns the root logger.
# Events from all child loggers will be processed by this handler.
logger = logging.getLogger()
logger.addHandler(handler)
# Set the logging level to NOTSET to allow all records to be processed by the handler.
logger.setLevel(logging.NOTSET)


def set_up_tracing():
exporters = []
exporters.append(AzureMonitorTraceExporter(connection_string=APPINSIGHTS_CONNECTION_STRING))

# Initialize a trace provider for the application. This is a factory for creating tracers.
tracer_provider = TracerProvider(resource=resource)
# Span processors are initialized with an exporter which is responsible
# for sending the telemetry data to a particular backend.
for exporter in exporters:
tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
# Sets the global default tracer provider
set_tracer_provider(tracer_provider)


def enable_observability(func):
"""A decorator to enable observability for the samples."""

async def wrapper(*args, **kwargs):
if not APPINSIGHTS_CONNECTION_STRING:
# If the connection string is not set, skip observability setup.
return await func(*args, **kwargs)

set_up_logging()
set_up_tracing()

tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("main") as current_span:
print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}")
return await func(*args, **kwargs)

return wrapper
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import DefaultAzureCredential

from samples.getting_started_with_agents.multi_agent_orchestration.observability import enable_observability
from semantic_kernel.agents import (
Agent,
AzureAIAgent,
Expand Down Expand Up @@ -180,6 +181,7 @@ def human_response_function() -> ChatMessageContent:
return ChatMessageContent(role=AuthorRole.USER, content=user_input)


@enable_observability
async def main():
"""Main function to run the agents."""
# 0. Initialize the Azure AI agent clients
Expand Down
3 changes: 2 additions & 1 deletion python/semantic_kernel/agents/azure_ai/azure_ai_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
from semantic_kernel.utils.telemetry.agent_diagnostics.decorators import (
trace_agent_get_response,
trace_agent_invocation,
trace_agent_streaming_invocation,
)
from semantic_kernel.utils.telemetry.user_agent import APP_INFO, SEMANTIC_KERNEL_USER_AGENT

Expand Down Expand Up @@ -832,7 +833,7 @@ async def invoke(
# Emit tool-related messages only via callback
await on_intermediate_message(message)

@trace_agent_invocation
@trace_agent_streaming_invocation
@override
async def invoke_stream(
self,
Expand Down
3 changes: 2 additions & 1 deletion python/semantic_kernel/agents/bedrock/bedrock_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from semantic_kernel.utils.telemetry.agent_diagnostics.decorators import (
trace_agent_get_response,
trace_agent_invocation,
trace_agent_streaming_invocation,
)

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -458,7 +459,7 @@ async def invoke(
"Failed to get a response from the agent. Please consider increasing the auto invoke attempts."
)

@trace_agent_invocation
@trace_agent_streaming_invocation
@override
async def invoke_stream(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from semantic_kernel.utils.telemetry.agent_diagnostics.decorators import (
trace_agent_get_response,
trace_agent_invocation,
trace_agent_streaming_invocation,
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -370,7 +371,7 @@ async def invoke(
):
yield AgentResponseItem(message=response, thread=thread)

@trace_agent_invocation
@trace_agent_streaming_invocation
@override
async def invoke_stream(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,10 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar, Literal

from microsoft.agents.copilotstudio.client import (
AgentType,
CopilotClient,
PowerPlatformCloud,
)
from microsoft.agents.copilotstudio.client import AgentType, CopilotClient, PowerPlatformCloud
from microsoft.agents.core.models import ActivityTypes
from msal import (
ConfidentialClientApplication,
PublicClientApplication,
)
from msal_extensions import (
FilePersistence,
PersistedTokenCache,
build_encrypted_persistence,
)
from msal import ConfidentialClientApplication, PublicClientApplication
from msal_extensions import FilePersistence, PersistedTokenCache, build_encrypted_persistence
from pydantic import ValidationError

from semantic_kernel.agents import Agent
Expand All @@ -47,6 +36,7 @@
from semantic_kernel.utils.telemetry.agent_diagnostics.decorators import (
trace_agent_get_response,
trace_agent_invocation,
trace_agent_streaming_invocation,
)

if sys.version_info >= (3, 12):
Expand Down Expand Up @@ -509,6 +499,7 @@ async def invoke(
):
yield AgentResponseItem(message=response, thread=thread)

@trace_agent_streaming_invocation
@override
async def invoke_stream(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
from semantic_kernel.utils.telemetry.agent_diagnostics.decorators import (
trace_agent_get_response,
trace_agent_invocation,
trace_agent_streaming_invocation,
)
from semantic_kernel.utils.telemetry.user_agent import APP_INFO, prepend_semantic_kernel_to_user_agent

Expand Down Expand Up @@ -947,7 +948,7 @@ async def invoke(
# Emit tool-related messages only via callback
await on_intermediate_message(message)

@trace_agent_invocation
@trace_agent_streaming_invocation
@override
async def invoke_stream(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
from semantic_kernel.utils.telemetry.agent_diagnostics.decorators import (
trace_agent_get_response,
trace_agent_invocation,
trace_agent_streaming_invocation,
)
from semantic_kernel.utils.telemetry.user_agent import APP_INFO, prepend_semantic_kernel_to_user_agent

Expand Down Expand Up @@ -1040,7 +1041,7 @@ async def invoke(
# Emit tool-related messages only via callback
await on_intermediate_message(message)

@trace_agent_invocation
@trace_agent_streaming_invocation
@override
async def invoke_stream(
self,
Expand Down
Loading
Loading