-
Notifications
You must be signed in to change notification settings - Fork 12
Add dependency diagram and script to generate dependency diagram. #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
798d2ba
Add dependency diagram and script to generate dependency diagram.
f7c94d7
Change layout of diagram to make it easier to read
709fb67
Update generate_dependency_diagram.py
pontemonti c8601ae
Optimize file I/O by caching pyproject data during first pass (#81)
Copilot 356697e
Use regex for dependency version specifier parsing (#82)
Copilot 0ba0fd3
Formatting fixes
c317994
Update name
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
pontemonti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) | ||
pontemonti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) | ||
pontemonti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # 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() | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.