Skip to content

Commit a33e81a

Browse files
pontemontiJohan BrobergCopilotCopilot
authored
Add dependency diagram and script to generate dependency diagram. (#80)
* Add dependency diagram and script to generate dependency diagram. * Change layout of diagram to make it easier to read * Update generate_dependency_diagram.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * 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> * 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> * Formatting fixes --------- Co-authored-by: Johan Broberg <johanb@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com>
1 parent 494a17a commit a33e81a

2 files changed

Lines changed: 292 additions & 0 deletions

File tree

DEPENDENCIES.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Microsoft Agent 365 SDK Python Package Dependencies
2+
3+
This diagram shows the internal dependencies between Microsoft Agent 365 SDK Python packages.
4+
5+
```mermaid
6+
graph LR
7+
%% Package Nodes
8+
microsoft_agents_a365_notifications["microsoft-agents-a365-notifications"]
9+
microsoft_agents_a365_observability_core["microsoft-agents-a365-observability-core"]
10+
microsoft_agents_a365_observability_extensions_langchain["microsoft-agents-a365-observability-extensions-langchain"]
11+
microsoft_agents_a365_observability_extensions_openai["microsoft-agents-a365-observability-extensions-openai"]
12+
microsoft_agents_a365_observability_extensions_semantic_kernel["microsoft-agents-a365-observability-extensions-semantic-kernel"]
13+
microsoft_agents_a365_observability_extensions_agent_framework["microsoft-agents-a365-observability-extensions-agent-framework"]
14+
microsoft_agents_a365_runtime["microsoft-agents-a365-runtime"]
15+
microsoft_agents_a365_tooling["microsoft-agents-a365-tooling"]
16+
microsoft_agents_a365_tooling_extensions_azureaifoundry["microsoft-agents-a365-tooling-extensions-azureaifoundry"]
17+
microsoft_agents_a365_tooling_extensions_openai["microsoft-agents-a365-tooling-extensions-openai"]
18+
microsoft_agents_a365_tooling_extensions_semantickernel["microsoft-agents-a365-tooling-extensions-semantickernel"]
19+
microsoft_agents_a365_tooling_extensions_agentframework["microsoft-agents-a365-tooling-extensions-agentframework"]
20+
21+
%% Dependencies
22+
microsoft_agents_a365_observability_core --> microsoft_agents_a365_runtime
23+
microsoft_agents_a365_observability_extensions_langchain --> microsoft_agents_a365_observability_core
24+
microsoft_agents_a365_observability_extensions_openai --> microsoft_agents_a365_observability_core
25+
microsoft_agents_a365_observability_extensions_semantic_kernel --> microsoft_agents_a365_observability_core
26+
microsoft_agents_a365_observability_extensions_agent_framework --> microsoft_agents_a365_observability_core
27+
microsoft_agents_a365_tooling_extensions_azureaifoundry --> microsoft_agents_a365_tooling
28+
microsoft_agents_a365_tooling_extensions_openai --> microsoft_agents_a365_tooling
29+
microsoft_agents_a365_tooling_extensions_semantickernel --> microsoft_agents_a365_tooling
30+
microsoft_agents_a365_tooling_extensions_agentframework --> microsoft_agents_a365_tooling
31+
32+
%% Styling
33+
classDef notifications fill:#ffcdd2,stroke:#c62828,color:#280505,stroke-width:2px
34+
class microsoft_agents_a365_notifications notifications
35+
classDef runtime fill:#bbdefb,stroke:#1565c0,color:#0d1a26,stroke-width:2px
36+
class microsoft_agents_a365_runtime runtime
37+
classDef observability fill:#c8e6c9,stroke:#2e7d32,color:#142a14,stroke-width:2px
38+
class microsoft_agents_a365_observability_core observability
39+
classDef observability_extensions fill:#e8f5e9,stroke:#66bb6a,color:#1f3d1f,stroke-width:2px
40+
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
41+
classDef tooling fill:#ffe0b2,stroke:#e65100,color:#331a00,stroke-width:2px
42+
class microsoft_agents_a365_tooling tooling
43+
classDef tooling_extensions fill:#fff3e0,stroke:#fb8c00,color:#4d2600,stroke-width:2px
44+
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
45+
```
46+
47+
## Package Types
48+
49+
- **Notifications** (Red): Notification and messaging extensions
50+
- **Runtime** (Blue): Core runtime components
51+
- **Observability** (Green): Telemetry and monitoring core
52+
- **Observability Extensions** (Light Green): Framework-specific observability integrations
53+
- **Tooling** (Orange): Agent tooling SDK core
54+
- **Tooling Extensions** (Light Orange): Framework-specific tooling integrations

generate_dependency_diagram.py

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
"""
5+
Script to generate a dependency diagram for Microsoft Agent 365 SDK packages.
6+
Reads pyproject.toml files and creates a Mermaid diagram showing internal package dependencies.
7+
"""
8+
9+
import re
10+
import tomllib
11+
from pathlib import Path
12+
from typing import Dict, List, Set
13+
14+
15+
class PackageInfo:
16+
"""Information about a package and its dependencies."""
17+
18+
def __init__(self, name: str, package_type: str, path: Path):
19+
self.name = name
20+
self.package_type = package_type
21+
self.path = path
22+
self.dependencies: Set[str] = set()
23+
24+
25+
def read_pyproject_toml(path: Path) -> Dict:
26+
"""Read and parse a pyproject.toml file."""
27+
with open(path, "rb") as f:
28+
return tomllib.load(f)
29+
30+
31+
def extract_dependencies(pyproject_data: Dict, package_names: Set[str]) -> Set[str]:
32+
"""Extract internal package dependencies from pyproject.toml data."""
33+
dependencies = set()
34+
35+
if "project" in pyproject_data and "dependencies" in pyproject_data["project"]:
36+
for dep in pyproject_data["project"]["dependencies"]:
37+
# Extract package name (before any version specifier)
38+
# Use regex to handle multiple version specifiers (e.g., "package>=1.0,<2.0")
39+
dep_name = re.split(r"[><=!~]", dep)[0].strip()
40+
41+
# Only include if it's one of our internal packages
42+
if dep_name in package_names:
43+
dependencies.add(dep_name)
44+
45+
return dependencies
46+
47+
48+
def generate_mermaid_diagram(packages: List[PackageInfo]) -> str:
49+
"""Generate a Mermaid diagram from package information."""
50+
51+
# Color scheme based on package types
52+
colors = {
53+
"Notifications": {"fill": "#ffcdd2", "stroke": "#c62828", "color": "#280505"}, # Red
54+
"Runtime": {"fill": "#bbdefb", "stroke": "#1565c0", "color": "#0d1a26"}, # Blue
55+
"Observability": {"fill": "#c8e6c9", "stroke": "#2e7d32", "color": "#142a14"}, # Green
56+
"Observability Extensions": {
57+
"fill": "#e8f5e9",
58+
"stroke": "#66bb6a",
59+
"color": "#1f3d1f",
60+
}, # Light Green
61+
"Tooling": {"fill": "#ffe0b2", "stroke": "#e65100", "color": "#331a00"}, # Orange
62+
"Tooling Extensions": {
63+
"fill": "#fff3e0",
64+
"stroke": "#fb8c00",
65+
"color": "#4d2600",
66+
}, # Light Orange
67+
}
68+
69+
lines = ["```mermaid", "graph LR"]
70+
lines.append(" %% Package Nodes")
71+
72+
# Create node definitions with shortened names for display
73+
node_map = {}
74+
for pkg in packages:
75+
# Create a short identifier for the node
76+
node_id = pkg.name.replace("-", "_")
77+
node_map[pkg.name] = node_id
78+
79+
# Create display name (remove microsoft-agents-a365- prefix for cleaner display)
80+
display_name = pkg.name
81+
lines.append(f' {node_id}["{display_name}"]')
82+
83+
lines.append("")
84+
lines.append(" %% Dependencies")
85+
86+
# Add dependency edges
87+
for pkg in packages:
88+
if pkg.dependencies:
89+
source_id = node_map[pkg.name]
90+
for dep_name in sorted(pkg.dependencies):
91+
if dep_name in node_map:
92+
target_id = node_map[dep_name]
93+
lines.append(f" {source_id} --> {target_id}")
94+
95+
lines.append("")
96+
lines.append(" %% Styling")
97+
98+
# Group packages by type for styling
99+
packages_by_type: Dict[str, List[str]] = {}
100+
for pkg in packages:
101+
if pkg.package_type not in packages_by_type:
102+
packages_by_type[pkg.package_type] = []
103+
packages_by_type[pkg.package_type].append(node_map[pkg.name])
104+
105+
# Apply styles
106+
for pkg_type, color_spec in colors.items():
107+
if pkg_type in packages_by_type:
108+
class_name = pkg_type.lower().replace(" ", "_")
109+
lines.append(
110+
f" classDef {class_name} fill:{color_spec['fill']},stroke:{color_spec['stroke']},color:{color_spec['color']},stroke-width:2px"
111+
)
112+
node_list = ",".join(packages_by_type[pkg_type])
113+
lines.append(f" class {node_list} {class_name}")
114+
115+
lines.append("```")
116+
117+
return "\n".join(lines)
118+
119+
120+
def determine_package_type(package_name: str) -> str:
121+
"""Determine the package type based on package name."""
122+
if "notifications" in package_name:
123+
return "Notifications"
124+
elif "runtime" in package_name:
125+
return "Runtime"
126+
elif "observability-extensions" in package_name:
127+
return "Observability Extensions"
128+
elif "observability" in package_name:
129+
return "Observability"
130+
elif "tooling-extensions" in package_name:
131+
return "Tooling Extensions"
132+
elif "tooling" in package_name:
133+
return "Tooling"
134+
else:
135+
return "Other"
136+
137+
138+
def main():
139+
"""Main function to generate the dependency diagram."""
140+
141+
# Get repository root
142+
repo_root = Path(__file__).parent
143+
144+
# Read workspace members from root pyproject.toml
145+
root_pyproject_path = repo_root / "pyproject.toml"
146+
if not root_pyproject_path.exists():
147+
print(f"Error: {root_pyproject_path} not found")
148+
return
149+
150+
root_pyproject = read_pyproject_toml(root_pyproject_path)
151+
152+
# Extract workspace members
153+
workspace_members = []
154+
if "tool" in root_pyproject and "uv" in root_pyproject["tool"]:
155+
if "workspace" in root_pyproject["tool"]["uv"]:
156+
workspace_members = root_pyproject["tool"]["uv"]["workspace"].get("members", [])
157+
158+
if not workspace_members:
159+
print("Error: No workspace members found in pyproject.toml")
160+
return
161+
162+
print(f"Found {len(workspace_members)} workspace members")
163+
164+
# Build package configs from workspace members
165+
package_configs = []
166+
for member_path in workspace_members:
167+
# Determine package type from path
168+
pkg_type = determine_package_type(member_path)
169+
package_configs.append((member_path, pkg_type))
170+
171+
# Collect all package names first and cache pyproject data
172+
all_package_names = set()
173+
packages: List[PackageInfo] = []
174+
pyproject_data_cache: Dict[str, Dict] = {}
175+
176+
for path_str, pkg_type in package_configs:
177+
pyproject_path = repo_root / path_str / "pyproject.toml"
178+
179+
if not pyproject_path.exists():
180+
print(f"Warning: {pyproject_path} not found")
181+
continue
182+
183+
pyproject_data = read_pyproject_toml(pyproject_path)
184+
if "project" not in pyproject_data or "name" not in pyproject_data["project"]:
185+
print(f"Warning: {pyproject_path} is missing project.name field")
186+
continue
187+
pkg_name = pyproject_data["project"]["name"]
188+
all_package_names.add(pkg_name)
189+
190+
# Cache the pyproject data to avoid reading the file again
191+
pyproject_data_cache[pkg_name] = pyproject_data
192+
193+
pkg_info = PackageInfo(pkg_name, pkg_type, pyproject_path)
194+
packages.append(pkg_info)
195+
196+
# Extract dependencies for each package using cached data
197+
for pkg in packages:
198+
pyproject_data = pyproject_data_cache[pkg.name]
199+
pkg.dependencies = extract_dependencies(pyproject_data, all_package_names)
200+
201+
# Generate Mermaid diagram
202+
diagram = generate_mermaid_diagram(packages)
203+
204+
# Write to markdown file
205+
output_path = repo_root / "DEPENDENCIES.md"
206+
with open(output_path, "w", encoding="utf-8") as f:
207+
f.write("# Microsoft Agent 365 SDK Python Package Dependencies\n\n")
208+
f.write(
209+
"This diagram shows the internal dependencies between Microsoft Agent 365 SDK Python packages.\n\n"
210+
)
211+
f.write(diagram)
212+
f.write("\n\n")
213+
f.write("## Package Types\n\n")
214+
f.write("- **Notifications** (Red): Notification and messaging extensions\n")
215+
f.write("- **Runtime** (Blue): Core runtime components\n")
216+
f.write("- **Observability** (Green): Telemetry and monitoring core\n")
217+
f.write(
218+
"- **Observability Extensions** (Light Green): Framework-specific observability integrations\n"
219+
)
220+
f.write("- **Tooling** (Orange): Agent tooling SDK core\n")
221+
f.write(
222+
"- **Tooling Extensions** (Light Orange): Framework-specific tooling integrations\n"
223+
)
224+
225+
print(f"Dependency diagram generated successfully: {output_path}")
226+
227+
# Print summary
228+
print(f"\nAnalyzed {len(packages)} packages:")
229+
for pkg in sorted(packages, key=lambda p: p.name):
230+
if pkg.dependencies:
231+
deps = ", ".join(sorted(dep for dep in pkg.dependencies))
232+
print(f" {pkg.name}{deps}")
233+
else:
234+
print(f" {pkg.name} (no internal dependencies)")
235+
236+
237+
if __name__ == "__main__":
238+
main()

0 commit comments

Comments
 (0)