From 798d2ba0698bc21954951cbbfb1b3ccb15c8bbe1 Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Wed, 3 Dec 2025 09:01:47 -0800 Subject: [PATCH 1/7] Add dependency diagram and script to generate dependency diagram. --- DEPENDENCIES.md | 54 +++++++++ generate_dependency_diagram.py | 214 +++++++++++++++++++++++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 DEPENDENCIES.md create mode 100644 generate_dependency_diagram.py diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md new file mode 100644 index 00000000..cc860ad3 --- /dev/null +++ b/DEPENDENCIES.md @@ -0,0 +1,54 @@ +# Agent 365 SDK Python Package Dependencies + +This diagram shows the internal dependencies between Agent 365 SDK Python packages. + +```mermaid +graph TD + %% Package Nodes + notifications["microsoft-agents-a365-notifications"] + observability_core["microsoft-agents-a365-observability-core"] + observability_extensions_langchain["microsoft-agents-a365-observability-extensions-langchain"] + observability_extensions_openai["microsoft-agents-a365-observability-extensions-openai"] + observability_extensions_semantic_kernel["microsoft-agents-a365-observability-extensions-semantic-kernel"] + observability_extensions_agent_framework["microsoft-agents-a365-observability-extensions-agent-framework"] + runtime["microsoft-agents-a365-runtime"] + tooling["microsoft-agents-a365-tooling"] + tooling_extensions_azureaifoundry["microsoft-agents-a365-tooling-extensions-azureaifoundry"] + tooling_extensions_openai["microsoft-agents-a365-tooling-extensions-openai"] + tooling_extensions_semantickernel["microsoft-agents-a365-tooling-extensions-semantickernel"] + tooling_extensions_agentframework["microsoft-agents-a365-tooling-extensions-agentframework"] + + %% Dependencies + observability_core --> runtime + observability_extensions_langchain --> observability_core + observability_extensions_openai --> observability_core + observability_extensions_semantic_kernel --> observability_core + observability_extensions_agent_framework --> observability_core + tooling_extensions_azureaifoundry --> tooling + tooling_extensions_openai --> tooling + tooling_extensions_semantickernel --> tooling + tooling_extensions_agentframework --> tooling + + %% Styling + classDef notifications fill:#ffcdd2,stroke:#c62828,color:#280505,stroke-width:2px + class notifications notifications + classDef runtime fill:#bbdefb,stroke:#1565c0,color:#0d1a26,stroke-width:2px + class runtime runtime + classDef observability fill:#c8e6c9,stroke:#2e7d32,color:#142a14,stroke-width:2px + class observability_core observability + classDef observability_extensions fill:#e8f5e9,stroke:#66bb6a,color:#1f3d1f,stroke-width:2px + class observability_extensions_langchain,observability_extensions_openai,observability_extensions_semantic_kernel,observability_extensions_agent_framework observability_extensions + classDef tooling fill:#ffe0b2,stroke:#e65100,color:#331a00,stroke-width:2px + class tooling tooling + classDef tooling_extensions fill:#fff3e0,stroke:#fb8c00,color:#4d2600,stroke-width:2px + class tooling_extensions_azureaifoundry,tooling_extensions_openai,tooling_extensions_semantickernel,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 diff --git a/generate_dependency_diagram.py b/generate_dependency_diagram.py new file mode 100644 index 00000000..8928b9b8 --- /dev/null +++ b/generate_dependency_diagram.py @@ -0,0 +1,214 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +""" +Script to generate a dependency diagram for Agent 365 SDK packages. +Reads pyproject.toml files and creates a Mermaid diagram showing internal package dependencies. +""" + +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) + dep_name = dep.split('>=')[0].split('==')[0].split('<')[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 TD'] + 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('microsoft-agents-a365-', '').replace('-', '_') + node_map[pkg.name] = node_id + + # Create display name (remove microsoft-agents-a365- prefix for cleaner display) + #display_name = pkg.name.replace('microsoft-agents-a365-', '') + 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 + all_package_names = set() + packages: List[PackageInfo] = [] + + 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) + pkg_name = pyproject_data['project']['name'] + all_package_names.add(pkg_name) + + pkg_info = PackageInfo(pkg_name, pkg_type, pyproject_path) + packages.append(pkg_info) + + # Extract dependencies for each package + for pkg in packages: + pyproject_data = read_pyproject_toml(pkg.path) + 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('# Agent 365 SDK Python Package Dependencies\n\n') + f.write('This diagram shows the internal dependencies between 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.replace('microsoft-agents-a365-', '') for dep in pkg.dependencies)) + print(f" {pkg.name.replace('microsoft-agents-a365-', '')} → {deps}") + else: + print(f" {pkg.name.replace('microsoft-agents-a365-', '')} (no internal dependencies)") + + +if __name__ == '__main__': + main() From f7c94d7198855a04f8ca3e1ffaa1ac695c884351 Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Wed, 3 Dec 2025 09:10:38 -0800 Subject: [PATCH 2/7] Change layout of diagram to make it easier to read --- DEPENDENCIES.md | 56 +++++++++++++++++----------------- generate_dependency_diagram.py | 11 +++---- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index cc860ad3..6b591b37 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -3,45 +3,45 @@ This diagram shows the internal dependencies between Agent 365 SDK Python packages. ```mermaid -graph TD +graph LR %% Package Nodes - notifications["microsoft-agents-a365-notifications"] - observability_core["microsoft-agents-a365-observability-core"] - observability_extensions_langchain["microsoft-agents-a365-observability-extensions-langchain"] - observability_extensions_openai["microsoft-agents-a365-observability-extensions-openai"] - observability_extensions_semantic_kernel["microsoft-agents-a365-observability-extensions-semantic-kernel"] - observability_extensions_agent_framework["microsoft-agents-a365-observability-extensions-agent-framework"] - runtime["microsoft-agents-a365-runtime"] - tooling["microsoft-agents-a365-tooling"] - tooling_extensions_azureaifoundry["microsoft-agents-a365-tooling-extensions-azureaifoundry"] - tooling_extensions_openai["microsoft-agents-a365-tooling-extensions-openai"] - tooling_extensions_semantickernel["microsoft-agents-a365-tooling-extensions-semantickernel"] - tooling_extensions_agentframework["microsoft-agents-a365-tooling-extensions-agentframework"] + 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 - observability_core --> runtime - observability_extensions_langchain --> observability_core - observability_extensions_openai --> observability_core - observability_extensions_semantic_kernel --> observability_core - observability_extensions_agent_framework --> observability_core - tooling_extensions_azureaifoundry --> tooling - tooling_extensions_openai --> tooling - tooling_extensions_semantickernel --> tooling - tooling_extensions_agentframework --> tooling + 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 notifications notifications + class microsoft_agents_a365_notifications notifications classDef runtime fill:#bbdefb,stroke:#1565c0,color:#0d1a26,stroke-width:2px - class runtime runtime + class microsoft_agents_a365_runtime runtime classDef observability fill:#c8e6c9,stroke:#2e7d32,color:#142a14,stroke-width:2px - class observability_core observability + class microsoft_agents_a365_observability_core observability classDef observability_extensions fill:#e8f5e9,stroke:#66bb6a,color:#1f3d1f,stroke-width:2px - class observability_extensions_langchain,observability_extensions_openai,observability_extensions_semantic_kernel,observability_extensions_agent_framework observability_extensions + 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 tooling tooling + class microsoft_agents_a365_tooling tooling classDef tooling_extensions fill:#fff3e0,stroke:#fb8c00,color:#4d2600,stroke-width:2px - class tooling_extensions_azureaifoundry,tooling_extensions_openai,tooling_extensions_semantickernel,tooling_extensions_agentframework tooling_extensions + 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 diff --git a/generate_dependency_diagram.py b/generate_dependency_diagram.py index 8928b9b8..a5e7e350 100644 --- a/generate_dependency_diagram.py +++ b/generate_dependency_diagram.py @@ -56,18 +56,17 @@ def generate_mermaid_diagram(packages: List[PackageInfo]) -> str: 'Tooling Extensions': {'fill': '#fff3e0', 'stroke': '#fb8c00', 'color': "#4d2600"}, # Light Orange } - lines = ['```mermaid', 'graph TD'] + 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('microsoft-agents-a365-', '').replace('-', '_') + 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.replace('microsoft-agents-a365-', '') display_name = pkg.name lines.append(f' {node_id}["{display_name}"]') @@ -204,10 +203,10 @@ def main(): print(f"\nAnalyzed {len(packages)} packages:") for pkg in sorted(packages, key=lambda p: p.name): if pkg.dependencies: - deps = ', '.join(sorted(dep.replace('microsoft-agents-a365-', '') for dep in pkg.dependencies)) - print(f" {pkg.name.replace('microsoft-agents-a365-', '')} → {deps}") + deps = ', '.join(sorted(dep for dep in pkg.dependencies)) + print(f" {pkg.name} → {deps}") else: - print(f" {pkg.name.replace('microsoft-agents-a365-', '')} (no internal dependencies)") + print(f" {pkg.name} (no internal dependencies)") if __name__ == '__main__': From 709fb6778c303f226aec90cd1d4d2418d86cae86 Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Wed, 3 Dec 2025 09:12:33 -0800 Subject: [PATCH 3/7] Update generate_dependency_diagram.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- generate_dependency_diagram.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/generate_dependency_diagram.py b/generate_dependency_diagram.py index a5e7e350..6bc84e8c 100644 --- a/generate_dependency_diagram.py +++ b/generate_dependency_diagram.py @@ -168,6 +168,9 @@ def main(): 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) From c8601ae8162da92f173f2e13dcda75e5cbaa97e2 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:19:13 -0800 Subject: [PATCH 4/7] Optimize file I/O by caching pyproject data during first pass (#81) * Initial plan * Optimize file I/O by caching pyproject data during first pass Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- generate_dependency_diagram.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/generate_dependency_diagram.py b/generate_dependency_diagram.py index 6bc84e8c..bc102049 100644 --- a/generate_dependency_diagram.py +++ b/generate_dependency_diagram.py @@ -156,9 +156,10 @@ def main(): pkg_type = determine_package_type(member_path) package_configs.append((member_path, pkg_type)) - # Collect all package names first + # 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' @@ -174,12 +175,15 @@ def main(): 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 + # Extract dependencies for each package using cached data for pkg in packages: - pyproject_data = read_pyproject_toml(pkg.path) + pyproject_data = pyproject_data_cache[pkg.name] pkg.dependencies = extract_dependencies(pyproject_data, all_package_names) # Generate Mermaid diagram From 356697e3fa281f6d0e96e74a8333cbe91e2851e1 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:28:46 -0800 Subject: [PATCH 5/7] Use regex for dependency version specifier parsing (#82) * Initial plan * Use regex for parsing dependency version specifiers Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --- generate_dependency_diagram.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/generate_dependency_diagram.py b/generate_dependency_diagram.py index bc102049..ff8f1310 100644 --- a/generate_dependency_diagram.py +++ b/generate_dependency_diagram.py @@ -6,6 +6,7 @@ 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 @@ -34,7 +35,8 @@ def extract_dependencies(pyproject_data: Dict, package_names: Set[str]) -> Set[s 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) - dep_name = dep.split('>=')[0].split('==')[0].split('<')[0].strip() + # 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: From 0ba0fd32c7d1d85ef1abdfecc7cc1f28a964e50f Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Wed, 3 Dec 2025 09:31:09 -0800 Subject: [PATCH 6/7] Formatting fixes --- generate_dependency_diagram.py | 198 ++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 91 deletions(-) diff --git a/generate_dependency_diagram.py b/generate_dependency_diagram.py index ff8f1310..a6f0b9bc 100644 --- a/generate_dependency_diagram.py +++ b/generate_dependency_diagram.py @@ -14,7 +14,7 @@ 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 @@ -24,57 +24,65 @@ def __init__(self, name: str, package_type: str, path: Path): def read_pyproject_toml(path: Path) -> Dict: """Read and parse a pyproject.toml file.""" - with open(path, 'rb') as f: + 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']: + + 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() - + 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 + "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') - + + 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_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') - + + lines.append("") + lines.append(" %% Dependencies") + # Add dependency edges for pkg in packages: if pkg.dependencies: @@ -82,141 +90,149 @@ def generate_mermaid_diagram(packages: List[PackageInfo]) -> str: 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') - + 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) + 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' + 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' + 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' + 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 "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' - + 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']: + 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'] + 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('# Agent 365 SDK Python Package Dependencies\n\n') - f.write('This diagram shows the internal dependencies between Agent 365 SDK Python packages.\n\n') + output_path = repo_root / "DEPENDENCIES.md" + with open(output_path, "w", encoding="utf-8") as f: + f.write("# Agent 365 SDK Python Package Dependencies\n\n") + f.write( + "This diagram shows the internal dependencies between 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') - + 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)) + 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__': +if __name__ == "__main__": main() From c31799499f3e21f10b57abaab45dc61d71806382 Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Wed, 3 Dec 2025 09:44:24 -0800 Subject: [PATCH 7/7] Update name --- DEPENDENCIES.md | 4 ++-- generate_dependency_diagram.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index 6b591b37..ee572d02 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -1,6 +1,6 @@ -# Agent 365 SDK Python Package Dependencies +# Microsoft Agent 365 SDK Python Package Dependencies -This diagram shows the internal dependencies between Agent 365 SDK Python packages. +This diagram shows the internal dependencies between Microsoft Agent 365 SDK Python packages. ```mermaid graph LR diff --git a/generate_dependency_diagram.py b/generate_dependency_diagram.py index a6f0b9bc..c766de62 100644 --- a/generate_dependency_diagram.py +++ b/generate_dependency_diagram.py @@ -2,7 +2,7 @@ # Licensed under the MIT License. """ -Script to generate a dependency diagram for Agent 365 SDK packages. +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. """ @@ -204,9 +204,9 @@ def main(): # Write to markdown file output_path = repo_root / "DEPENDENCIES.md" with open(output_path, "w", encoding="utf-8") as f: - f.write("# Agent 365 SDK Python Package Dependencies\n\n") + f.write("# Microsoft Agent 365 SDK Python Package Dependencies\n\n") f.write( - "This diagram shows the internal dependencies between Agent 365 SDK Python packages.\n\n" + "This diagram shows the internal dependencies between Microsoft Agent 365 SDK Python packages.\n\n" ) f.write(diagram) f.write("\n\n")