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
15 changes: 13 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,20 @@ jobs:
run: |
A365_SDK_VERSION=${{ needs.version-number.outputs.PYTHON_PACKAGE_VERSION }} uv build --all-packages --wheel

- name: Run tests
- name: Run unit tests
run: |
uv run --frozen python -m pytest tests/ -v --tb=short
uv run --frozen python -m pytest tests/ -v --tb=short -m "not integration"

- name: Run integration tests
# Only run integration tests if secrets are available
if: ${{ vars.RUN_INTEGRATION_TESTS == 'true' }}
run: |
uv run --frozen python -m pytest tests/integration/ -v --tb=short -m integration
env:
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
AZURE_OPENAI_ENDPOINT: ${{ vars.AZURE_OPENAI_ENDPOINT }}
AZURE_OPENAI_DEPLOYMENT: ${{ vars.AZURE_OPENAI_DEPLOYMENT }}
AZURE_OPENAI_API_VERSION: ${{ vars.AZURE_OPENAI_API_VERSION }}

# Copy package and samples to drop folder
- name: Copy package and samples to drop folder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The Microsoft Agent 365 LangChain Observability Extensions automatically instrum

```python
from microsoft_agents_a365.observability.core.config import configure
from microsoft_agents_a365.observability.extensions.langchain import KairoInstrumentorLangChain
from microsoft_agents_a365.observability.extensions.langchain import CustomLangChainInstrumentor

# Configure observability
configure(
Expand All @@ -53,7 +53,7 @@ configure(
)

# Enable automatic instrumentation
instrumentor = KairoInstrumentorLangChain()
instrumentor = CustomLangChainInstrumentor()
instrumentor.instrument()

# Your LangChain code is now automatically traced
Expand Down Expand Up @@ -84,7 +84,7 @@ instrumentor.instrument()
```properties
# Core observability settings
ENABLE_OBSERVABILITY=true
ENABLE_KAIRO_EXPORTER=true
ENABLE_A365_OBSERVABILITY_EXPORTER=true
PYTHON_ENVIRONMENT=production

# LangChain-specific settings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
import langchain_core
import opentelemetry.trace as optel_trace
from langchain_core.callbacks import BaseCallbackManager
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.trace import Span
from wrapt import wrap_function_wrapper

from microsoft_agents_a365.observability.core.config import (
get_tracer,
get_tracer_provider,
is_configured,
)
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.trace import Span
from wrapt import wrap_function_wrapper

from microsoft_agents_a365.observability.extensions.langchain.tracer import CustomLangChainTracer

_INSTRUMENTS: str = "langchain_core >= 0.1.0"
Expand Down Expand Up @@ -48,7 +48,7 @@ def _instrument(self, **kwargs: Any) -> None:
tracer_name: str | None = kwargs.get("tracer_name")
tracer_version: str | None = kwargs.get("tracer_version")

# Prefer the Kairo tracer; fall back to OpenTelemetry’s default if needed.
# Prefer the Agent365 tracer; fall back to OpenTelemetry’s default if needed.
try:
tracer = get_tracer(tracer_name, tracer_version)
except Exception:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dependencies = [
Homepage = "https://github.com/microsoft/Agent365"
Repository = "https://github.com/microsoft/Agent365"
Issues = "https://github.com/microsoft/Agent365/issues"
Documentation = "https://github.com/microsoft/Agent365/tree/main/python/microsoft_kairo/observability/wrappers/langchain"
Documentation = "https://github.com/microsoft/Agent365-python/tree/main/libraries/microsoft-agents-a365-observability-extensions-langchain"

[project.optional-dependencies]
dev = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ The Microsoft Agent 365 OpenAI Agents Observability Extensions automatically ins

```python
from microsoft_agents_a365.observability.core.config import configure
from microsoft_agents_a365.observability.extensions.openai_agents import KairoInstrumentorOpenAIAgents
from microsoft_agents_a365.observability.extensions.openai_agents import OpenAIAgentsTraceInstrumentor

# Configure observability
configure(
Expand All @@ -54,7 +54,7 @@ configure(
)

# Enable automatic instrumentation
instrumentor = KairoInstrumentorOpenAIAgents()
instrumentor = OpenAIAgentsTraceInstrumentor()
instrumentor.instrument()

# Your OpenAI Agents code is now automatically traced
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Copyright (c) Microsoft. All rights reserved.

"""
Wraps the OpenAI Agents SDK tracer to integrate with our Kairo Telemetry Solution.
Wraps the OpenAI Agents SDK tracer to integrate with our Agent365 Telemetry Solution.
"""

from .trace_instrumentor import KairoInstrumentorOpenAIAgents
from .trace_instrumentor import OpenAIAgentsTraceInstrumentor

__all__ = ["KairoInstrumentorOpenAIAgents"]
__all__ = ["OpenAIAgentsTraceInstrumentor"]
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@

import opentelemetry.trace as optel_trace
from agents import set_trace_processors
from microsoft_agents_a365.observability.core import get_tracer, get_tracer_provider, is_configured
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.trace import Tracer

from microsoft_agents_a365.observability.core import get_tracer, get_tracer_provider, is_configured

from .trace_processor import OpenAIAgentsTraceProcessor

logging.basicConfig(level=logging.INFO)
Expand All @@ -21,51 +20,51 @@
_instruments = ("openai-agents >= 0.2.6",)


class KairoInstrumentorOpenAIAgents(BaseInstrumentor):
class OpenAIAgentsTraceInstrumentor(BaseInstrumentor):
"""
Custom Trace Processor for OpenAI Agents SDK using Kairo.
Forwards OpenAI Agents SDK traces and spans to Kairo's tracing scopes.
Custom Trace Processor for OpenAI Agents SDK using Agent365.
Forwards OpenAI Agents SDK traces and spans to Agent365's tracing scopes.

```
"""

def __init__(self):
"""Initialize the KairoInstrumentorOpenAIAgents.
Raises: RuntimeError: If Kairo is not configured.
"""Initialize the OpenAIAgentsTraceInstrumentor.
Raises: RuntimeError: If Agent365 is not configured.
"""
# Verify if Kairo is configured
kairo_status = is_configured()
if not kairo_status:
# Verify if Agent365 is configured
Agent365_status = is_configured()
if not Agent365_status:
raise RuntimeError(
"Kairo is not configured yet. Please configure Kairo before initializing this instrumentor."
"Agent365 is not configured yet. Please configure Agent365 before initializing this instrumentor."
)
super().__init__()

def instrumentation_dependencies(self) -> Collection[str]:
return _instruments

def _instrument(self, **kwargs: Any) -> None:
"""Instruments the OpenAI Agents SDK with Kairo tracing."""
"""Instruments the OpenAI Agents SDK with Agent365 tracing."""
tracer_name = kwargs["tracer_name"] if kwargs.get("tracer_name") else None
tracer_version = kwargs["tracer_version"] if kwargs.get("tracer_version") else None

# Get the configured Kairo Tracer
# Get the configured Agent365 Tracer
try:
tracer = get_tracer(tracer_name, tracer_version)
except Exception:
# fallback
tracer = optel_trace.get_tracer(tracer_name, tracer_version)

# Get the configured Kairo Tracer Provider instance
# Get the configured Agent365 Tracer Provider instance
try:
get_tracer_provider()
except Exception:
# fallback
optel_trace.get_tracer_provider()

kairo_tracer = cast(Tracer, tracer)
agent365_tracer = cast(Tracer, tracer)

set_trace_processors([OpenAIAgentsTraceProcessor(kairo_tracer)])
set_trace_processors([OpenAIAgentsTraceProcessor(agent365_tracer)])

def _uninstrument(self, **kwargs: Any) -> None:
pass
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dependencies = [
Homepage = "https://github.com/microsoft/Agent365"
Repository = "https://github.com/microsoft/Agent365"
Issues = "https://github.com/microsoft/Agent365/issues"
Documentation = "https://github.com/microsoft/Agent365/tree/main/python/microsoft_kairo/observability/wrappers/openai_agents"
Documentation = "https://github.com/microsoft/Agent365-python/tree/main/libraries/microsoft-agents-a365-observability-extensions-openai"

[project.optional-dependencies]
dev = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ The Microsoft Agent 365 Semantic Kernel Observability Extensions automatically i

```python
from microsoft_agents_a365.observability.core.config import configure
from microsoft_agents_a365.observability.extensions.semantic_kernel import KairoInstrumentorSemanticKernel
from microsoft_agents_a365.observability.extensions.semantic_kernel import SemanticKernelInstrumentor

# Configure observability
configure(
Expand All @@ -55,7 +55,7 @@ configure(
)

# Enable automatic instrumentation
instrumentor = KairoInstrumentorSemanticKernel()
instrumentor = SemanticKernelInstrumentor()
instrumentor.instrument()

# Your Semantic Kernel code is now automatically traced
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from collections.abc import Collection
from typing import Any

from microsoft_agents_a365.observability.core.config import get_tracer_provider, is_configured
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor

from microsoft_agents_a365.observability.core.config import get_tracer_provider, is_configured
from microsoft_agents_a365.observability.extensions.semantickernel.span_processor import (
SemanticKernelSpanProcessor,
)
Expand All @@ -28,7 +28,7 @@ class SemanticKernelInstrumentor(BaseInstrumentor):
def __init__(self):
if not is_configured():
raise RuntimeError(
"Kairo (or your telemetry config) is not initialized. Configure it before instrumenting."
"Agent365 (or your telemetry config) is not initialized. Configure it before instrumenting."
)
super().__init__()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dependencies = [
Homepage = "https://github.com/microsoft/Agent365"
Repository = "https://github.com/microsoft/Agent365"
Issues = "https://github.com/microsoft/Agent365/issues"
Documentation = "https://github.com/microsoft/Agent365/tree/main/python/microsoft_kairo/observability/wrappers/semantic_kernel"
Documentation = "https://github.com/microsoft/Agent365-python/tree/main/libraries/microsoft-agents-a365-observability-extensions-semantickernel"

[project.optional-dependencies]
dev = [
Expand Down
28 changes: 28 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,34 @@ microsoft-agents-a365-observability-core = { workspace = true }
microsoft-agents-a365-runtime = { workspace = true }
microsoft-agents-a365-tooling = { workspace = true }

# Development dependencies for the entire workspace
[tool.uv]
dev-dependencies = [
"pytest>=7.0.0",
"pytest-asyncio>=0.21.0",
"pytest-mock>=3.10.0",
"ruff>=0.1.0",
"python-dotenv>=1.0.0",
"openai>=1.0.0",
"azure-identity>=1.12.0",
"openai-agents >= 0.2.6",
]

# Test configuration
[tool.pytest.ini_options]
minversion = "7.0"
addopts = [
"-v",
"--tb=short",
"--strict-markers",
"--strict-config",
]
testpaths = ["tests"]
markers = [
"unit: Unit tests (fast, mocked)",
"integration: Integration tests (slow, real services)",
]

[tool.ruff]
# Set the maximum line length to 100.
line-length = 100
Expand Down
1 change: 1 addition & 0 deletions tests/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ OTEL_SDK_DISABLED=false
AZURE_OPENAI_API_VERSION={optional}
AZURE_OPENAI_ENDPOINT={optional}
AZURE_OPENAI_DEPLOYMENT={optional}
AZURE_OPENAI_API_KEY={required_for_integration_tests}
4 changes: 4 additions & 0 deletions tests/integration/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

"""Integration tests for Agent365 Python SDK."""
56 changes: 56 additions & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

import os
from pathlib import Path
from typing import Any

import pytest

# Load .env file if it exists (for local development)
try:
from dotenv import load_dotenv

# Look for .env file in tests directory
env_file = Path(__file__).parent.parent / ".env"
if env_file.exists():
load_dotenv(env_file)
except ImportError:
# python-dotenv not installed, skip loading .env file
pass


def pytest_configure(config):
"""Add integration marker."""
config.addinivalue_line("markers", "integration: marks tests as integration tests")


@pytest.fixture(scope="session")
def azure_openai_config() -> dict[str, Any]:
"""Azure OpenAI configuration for integration tests."""
api_key = os.getenv("AZURE_OPENAI_API_KEY")
endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
deployment = os.getenv("AZURE_OPENAI_DEPLOYMENT", "gpt-4")
api_version = os.getenv("AZURE_OPENAI_API_VERSION", "2024-08-01-preview")

if not api_key or not endpoint:
pytest.skip("Integration tests require AZURE_OPENAI_API_KEY and AZURE_OPENAI_ENDPOINT")

return {
"api_key": api_key,
"endpoint": endpoint,
"deployment": deployment,
"api_version": api_version,
}


@pytest.fixture(scope="session")
def agent365_config() -> dict[str, Any]:
"""Agent365 configuration for integration tests."""
tenant_id = os.getenv("AGENT365_TEST_TENANT_ID", "4d44f041-f91e-4d00-b107-61e47b26f5a8")
agent_id = os.getenv("AGENT365_TEST_AGENT_ID", "3bccd52b-daaa-4b11-af40-47443852137c")

if not tenant_id:
pytest.skip("Integration tests require AGENT365_TEST_TENANT_ID")

return {"tenant_id": tenant_id, "agent_id": agent_id}
Loading
Loading