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
54 changes: 54 additions & 0 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Microsoft Agent 365 SDK Python Package Dependencies

This diagram shows the internal dependencies between Microsoft Agent 365 SDK Python packages.

```mermaid
graph LR
%% Package Nodes
microsoft_agents_a365_notifications["microsoft-agents-a365-notifications"]
microsoft_agents_a365_observability_core["microsoft-agents-a365-observability-core"]
microsoft_agents_a365_observability_extensions_langchain["microsoft-agents-a365-observability-extensions-langchain"]
microsoft_agents_a365_observability_extensions_openai["microsoft-agents-a365-observability-extensions-openai"]
microsoft_agents_a365_observability_extensions_semantic_kernel["microsoft-agents-a365-observability-extensions-semantic-kernel"]
microsoft_agents_a365_observability_extensions_agent_framework["microsoft-agents-a365-observability-extensions-agent-framework"]
microsoft_agents_a365_runtime["microsoft-agents-a365-runtime"]
microsoft_agents_a365_tooling["microsoft-agents-a365-tooling"]
microsoft_agents_a365_tooling_extensions_azureaifoundry["microsoft-agents-a365-tooling-extensions-azureaifoundry"]
microsoft_agents_a365_tooling_extensions_openai["microsoft-agents-a365-tooling-extensions-openai"]
microsoft_agents_a365_tooling_extensions_semantickernel["microsoft-agents-a365-tooling-extensions-semantickernel"]
microsoft_agents_a365_tooling_extensions_agentframework["microsoft-agents-a365-tooling-extensions-agentframework"]

%% Dependencies
microsoft_agents_a365_observability_core --> microsoft_agents_a365_runtime
microsoft_agents_a365_observability_extensions_langchain --> microsoft_agents_a365_observability_core
microsoft_agents_a365_observability_extensions_openai --> microsoft_agents_a365_observability_core
microsoft_agents_a365_observability_extensions_semantic_kernel --> microsoft_agents_a365_observability_core
microsoft_agents_a365_observability_extensions_agent_framework --> microsoft_agents_a365_observability_core
microsoft_agents_a365_tooling_extensions_azureaifoundry --> microsoft_agents_a365_tooling
microsoft_agents_a365_tooling_extensions_openai --> microsoft_agents_a365_tooling
microsoft_agents_a365_tooling_extensions_semantickernel --> microsoft_agents_a365_tooling
microsoft_agents_a365_tooling_extensions_agentframework --> microsoft_agents_a365_tooling

%% Styling
classDef notifications fill:#ffcdd2,stroke:#c62828,color:#280505,stroke-width:2px
class microsoft_agents_a365_notifications notifications
classDef runtime fill:#bbdefb,stroke:#1565c0,color:#0d1a26,stroke-width:2px
class microsoft_agents_a365_runtime runtime
classDef observability fill:#c8e6c9,stroke:#2e7d32,color:#142a14,stroke-width:2px
class microsoft_agents_a365_observability_core observability
classDef observability_extensions fill:#e8f5e9,stroke:#66bb6a,color:#1f3d1f,stroke-width:2px
class microsoft_agents_a365_observability_extensions_langchain,microsoft_agents_a365_observability_extensions_openai,microsoft_agents_a365_observability_extensions_semantic_kernel,microsoft_agents_a365_observability_extensions_agent_framework observability_extensions
classDef tooling fill:#ffe0b2,stroke:#e65100,color:#331a00,stroke-width:2px
class microsoft_agents_a365_tooling tooling
classDef tooling_extensions fill:#fff3e0,stroke:#fb8c00,color:#4d2600,stroke-width:2px
class microsoft_agents_a365_tooling_extensions_azureaifoundry,microsoft_agents_a365_tooling_extensions_openai,microsoft_agents_a365_tooling_extensions_semantickernel,microsoft_agents_a365_tooling_extensions_agentframework tooling_extensions
```

## Package Types

- **Notifications** (Red): Notification and messaging extensions
- **Runtime** (Blue): Core runtime components
- **Observability** (Green): Telemetry and monitoring core
- **Observability Extensions** (Light Green): Framework-specific observability integrations
- **Tooling** (Orange): Agent tooling SDK core
- **Tooling Extensions** (Light Orange): Framework-specific tooling integrations
238 changes: 238 additions & 0 deletions generate_dependency_diagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

"""
Script to generate a dependency diagram for Microsoft Agent 365 SDK packages.
Reads pyproject.toml files and creates a Mermaid diagram showing internal package dependencies.
"""

import re
import tomllib
from pathlib import Path
from typing import Dict, List, Set


class PackageInfo:
"""Information about a package and its dependencies."""

def __init__(self, name: str, package_type: str, path: Path):
self.name = name
self.package_type = package_type
self.path = path
self.dependencies: Set[str] = set()


def read_pyproject_toml(path: Path) -> Dict:
"""Read and parse a pyproject.toml file."""
with open(path, "rb") as f:
return tomllib.load(f)


def extract_dependencies(pyproject_data: Dict, package_names: Set[str]) -> Set[str]:
"""Extract internal package dependencies from pyproject.toml data."""
dependencies = set()

if "project" in pyproject_data and "dependencies" in pyproject_data["project"]:
for dep in pyproject_data["project"]["dependencies"]:
# Extract package name (before any version specifier)
# Use regex to handle multiple version specifiers (e.g., "package>=1.0,<2.0")
dep_name = re.split(r"[><=!~]", dep)[0].strip()

# Only include if it's one of our internal packages
if dep_name in package_names:
dependencies.add(dep_name)

return dependencies


def generate_mermaid_diagram(packages: List[PackageInfo]) -> str:
"""Generate a Mermaid diagram from package information."""

# Color scheme based on package types
colors = {
"Notifications": {"fill": "#ffcdd2", "stroke": "#c62828", "color": "#280505"}, # Red
"Runtime": {"fill": "#bbdefb", "stroke": "#1565c0", "color": "#0d1a26"}, # Blue
"Observability": {"fill": "#c8e6c9", "stroke": "#2e7d32", "color": "#142a14"}, # Green
"Observability Extensions": {
"fill": "#e8f5e9",
"stroke": "#66bb6a",
"color": "#1f3d1f",
}, # Light Green
"Tooling": {"fill": "#ffe0b2", "stroke": "#e65100", "color": "#331a00"}, # Orange
"Tooling Extensions": {
"fill": "#fff3e0",
"stroke": "#fb8c00",
"color": "#4d2600",
}, # Light Orange
}

lines = ["```mermaid", "graph LR"]
lines.append(" %% Package Nodes")

# Create node definitions with shortened names for display
node_map = {}
for pkg in packages:
# Create a short identifier for the node
node_id = pkg.name.replace("-", "_")
node_map[pkg.name] = node_id

# Create display name (remove microsoft-agents-a365- prefix for cleaner display)
display_name = pkg.name
lines.append(f' {node_id}["{display_name}"]')

lines.append("")
lines.append(" %% Dependencies")

# Add dependency edges
for pkg in packages:
if pkg.dependencies:
source_id = node_map[pkg.name]
for dep_name in sorted(pkg.dependencies):
if dep_name in node_map:
target_id = node_map[dep_name]
lines.append(f" {source_id} --> {target_id}")

lines.append("")
lines.append(" %% Styling")

# Group packages by type for styling
packages_by_type: Dict[str, List[str]] = {}
for pkg in packages:
if pkg.package_type not in packages_by_type:
packages_by_type[pkg.package_type] = []
packages_by_type[pkg.package_type].append(node_map[pkg.name])

# Apply styles
for pkg_type, color_spec in colors.items():
if pkg_type in packages_by_type:
class_name = pkg_type.lower().replace(" ", "_")
lines.append(
f" classDef {class_name} fill:{color_spec['fill']},stroke:{color_spec['stroke']},color:{color_spec['color']},stroke-width:2px"
)
node_list = ",".join(packages_by_type[pkg_type])
lines.append(f" class {node_list} {class_name}")

lines.append("```")

return "\n".join(lines)


def determine_package_type(package_name: str) -> str:
"""Determine the package type based on package name."""
if "notifications" in package_name:
return "Notifications"
elif "runtime" in package_name:
return "Runtime"
elif "observability-extensions" in package_name:
return "Observability Extensions"
elif "observability" in package_name:
return "Observability"
elif "tooling-extensions" in package_name:
return "Tooling Extensions"
elif "tooling" in package_name:
return "Tooling"
else:
return "Other"


def main():
"""Main function to generate the dependency diagram."""

# Get repository root
repo_root = Path(__file__).parent

# Read workspace members from root pyproject.toml
root_pyproject_path = repo_root / "pyproject.toml"
if not root_pyproject_path.exists():
print(f"Error: {root_pyproject_path} not found")
return

root_pyproject = read_pyproject_toml(root_pyproject_path)

# Extract workspace members
workspace_members = []
if "tool" in root_pyproject and "uv" in root_pyproject["tool"]:
if "workspace" in root_pyproject["tool"]["uv"]:
workspace_members = root_pyproject["tool"]["uv"]["workspace"].get("members", [])

if not workspace_members:
print("Error: No workspace members found in pyproject.toml")
return

print(f"Found {len(workspace_members)} workspace members")

# Build package configs from workspace members
package_configs = []
for member_path in workspace_members:
# Determine package type from path
pkg_type = determine_package_type(member_path)
package_configs.append((member_path, pkg_type))

# Collect all package names first and cache pyproject data
all_package_names = set()
packages: List[PackageInfo] = []
pyproject_data_cache: Dict[str, Dict] = {}

for path_str, pkg_type in package_configs:
pyproject_path = repo_root / path_str / "pyproject.toml"

if not pyproject_path.exists():
print(f"Warning: {pyproject_path} not found")
continue

pyproject_data = read_pyproject_toml(pyproject_path)
if "project" not in pyproject_data or "name" not in pyproject_data["project"]:
print(f"Warning: {pyproject_path} is missing project.name field")
continue
pkg_name = pyproject_data["project"]["name"]
all_package_names.add(pkg_name)

# Cache the pyproject data to avoid reading the file again
pyproject_data_cache[pkg_name] = pyproject_data

pkg_info = PackageInfo(pkg_name, pkg_type, pyproject_path)
packages.append(pkg_info)

# Extract dependencies for each package using cached data
for pkg in packages:
pyproject_data = pyproject_data_cache[pkg.name]
pkg.dependencies = extract_dependencies(pyproject_data, all_package_names)

# Generate Mermaid diagram
diagram = generate_mermaid_diagram(packages)

# Write to markdown file
output_path = repo_root / "DEPENDENCIES.md"
with open(output_path, "w", encoding="utf-8") as f:
f.write("# Microsoft Agent 365 SDK Python Package Dependencies\n\n")
f.write(
"This diagram shows the internal dependencies between Microsoft Agent 365 SDK Python packages.\n\n"
)
f.write(diagram)
f.write("\n\n")
f.write("## Package Types\n\n")
f.write("- **Notifications** (Red): Notification and messaging extensions\n")
f.write("- **Runtime** (Blue): Core runtime components\n")
f.write("- **Observability** (Green): Telemetry and monitoring core\n")
f.write(
"- **Observability Extensions** (Light Green): Framework-specific observability integrations\n"
)
f.write("- **Tooling** (Orange): Agent tooling SDK core\n")
f.write(
"- **Tooling Extensions** (Light Orange): Framework-specific tooling integrations\n"
)

print(f"Dependency diagram generated successfully: {output_path}")

# Print summary
print(f"\nAnalyzed {len(packages)} packages:")
for pkg in sorted(packages, key=lambda p: p.name):
if pkg.dependencies:
deps = ", ".join(sorted(dep for dep in pkg.dependencies))
print(f" {pkg.name} → {deps}")
else:
print(f" {pkg.name} (no internal dependencies)")


if __name__ == "__main__":
main()
Loading