Skip to content

Commit 798d2ba

Browse files
author
Johan Broberg
committed
Add dependency diagram and script to generate dependency diagram.
1 parent 494a17a commit 798d2ba

File tree

2 files changed

+268
-0
lines changed

2 files changed

+268
-0
lines changed

DEPENDENCIES.md

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

0 commit comments

Comments
 (0)