1414
1515class PackageInfo :
1616 """Information about a package and its dependencies."""
17-
17+
1818 def __init__ (self , name : str , package_type : str , path : Path ):
1919 self .name = name
2020 self .package_type = package_type
@@ -24,199 +24,215 @@ def __init__(self, name: str, package_type: str, path: Path):
2424
2525def read_pyproject_toml (path : Path ) -> Dict :
2626 """Read and parse a pyproject.toml file."""
27- with open (path , 'rb' ) as f :
27+ with open (path , "rb" ) as f :
2828 return tomllib .load (f )
2929
3030
3131def extract_dependencies (pyproject_data : Dict , package_names : Set [str ]) -> Set [str ]:
3232 """Extract internal package dependencies from pyproject.toml data."""
3333 dependencies = set ()
34-
35- if ' project' in pyproject_data and ' dependencies' in pyproject_data [' project' ]:
36- for dep in pyproject_data [' project' ][ ' dependencies' ]:
34+
35+ if " project" in pyproject_data and " dependencies" in pyproject_data [" project" ]:
36+ for dep in pyproject_data [" project" ][ " dependencies" ]:
3737 # Extract package name (before any version specifier)
3838 # Use regex to handle multiple version specifiers (e.g., "package>=1.0,<2.0")
39- dep_name = re .split (r' [><=!~]' , dep )[0 ].strip ()
40-
39+ dep_name = re .split (r" [><=!~]" , dep )[0 ].strip ()
40+
4141 # Only include if it's one of our internal packages
4242 if dep_name in package_names :
4343 dependencies .add (dep_name )
44-
44+
4545 return dependencies
4646
4747
4848def generate_mermaid_diagram (packages : List [PackageInfo ]) -> str :
4949 """Generate a Mermaid diagram from package information."""
50-
50+
5151 # Color scheme based on package types
5252 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' : {'fill' : '#e8f5e9' , 'stroke' : '#66bb6a' , 'color' : "#1f3d1f" }, # Light Green
57- 'Tooling' : {'fill' : '#ffe0b2' , 'stroke' : '#e65100' , 'color' : "#331a00" }, # Orange
58- 'Tooling Extensions' : {'fill' : '#fff3e0' , 'stroke' : '#fb8c00' , 'color' : "#4d2600" }, # Light Orange
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
5967 }
60-
61- lines = [' ```mermaid' , ' graph LR' ]
62- lines .append (' %% Package Nodes' )
63-
68+
69+ lines = [" ```mermaid" , " graph LR" ]
70+ lines .append (" %% Package Nodes" )
71+
6472 # Create node definitions with shortened names for display
6573 node_map = {}
6674 for pkg in packages :
6775 # Create a short identifier for the node
68- node_id = pkg .name .replace ('-' , '_' )
76+ node_id = pkg .name .replace ("-" , "_" )
6977 node_map [pkg .name ] = node_id
70-
78+
7179 # Create display name (remove microsoft-agents-a365- prefix for cleaner display)
7280 display_name = pkg .name
7381 lines .append (f' { node_id } ["{ display_name } "]' )
74-
75- lines .append ('' )
76- lines .append (' %% Dependencies' )
77-
82+
83+ lines .append ("" )
84+ lines .append (" %% Dependencies" )
85+
7886 # Add dependency edges
7987 for pkg in packages :
8088 if pkg .dependencies :
8189 source_id = node_map [pkg .name ]
8290 for dep_name in sorted (pkg .dependencies ):
8391 if dep_name in node_map :
8492 target_id = node_map [dep_name ]
85- lines .append (f' { source_id } --> { target_id } ' )
86-
87- lines .append ('' )
88- lines .append (' %% Styling' )
89-
93+ lines .append (f" { source_id } --> { target_id } " )
94+
95+ lines .append ("" )
96+ lines .append (" %% Styling" )
97+
9098 # Group packages by type for styling
9199 packages_by_type : Dict [str , List [str ]] = {}
92100 for pkg in packages :
93101 if pkg .package_type not in packages_by_type :
94102 packages_by_type [pkg .package_type ] = []
95103 packages_by_type [pkg .package_type ].append (node_map [pkg .name ])
96-
104+
97105 # Apply styles
98106 for pkg_type , color_spec in colors .items ():
99107 if pkg_type in packages_by_type :
100- class_name = pkg_type .lower ().replace (' ' , '_' )
101- lines .append (f' classDef { class_name } fill:{ color_spec ["fill" ]} ,stroke:{ color_spec ["stroke" ]} ,color:{ color_spec ["color" ]} ,stroke-width:2px' )
102- node_list = ',' .join (packages_by_type [pkg_type ])
103- lines .append (f' class { node_list } { class_name } ' )
104-
105- lines .append ('```' )
106-
107- return '\n ' .join (lines )
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 )
108118
109119
110120def determine_package_type (package_name : str ) -> str :
111121 """Determine the package type based on package name."""
112- if ' notifications' in package_name :
113- return ' Notifications'
114- elif ' runtime' in package_name :
115- return ' Runtime'
116- elif ' observability-extensions' in package_name :
117- return ' Observability Extensions'
118- elif ' observability' in package_name :
119- return ' Observability'
120- elif ' tooling-extensions' in package_name :
121- return ' Tooling Extensions'
122- elif ' tooling' in package_name :
123- return ' Tooling'
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"
124134 else :
125- return ' Other'
135+ return " Other"
126136
127137
128138def main ():
129139 """Main function to generate the dependency diagram."""
130-
140+
131141 # Get repository root
132142 repo_root = Path (__file__ ).parent
133-
143+
134144 # Read workspace members from root pyproject.toml
135- root_pyproject_path = repo_root / ' pyproject.toml'
145+ root_pyproject_path = repo_root / " pyproject.toml"
136146 if not root_pyproject_path .exists ():
137147 print (f"Error: { root_pyproject_path } not found" )
138148 return
139-
149+
140150 root_pyproject = read_pyproject_toml (root_pyproject_path )
141-
151+
142152 # Extract workspace members
143153 workspace_members = []
144- if ' tool' in root_pyproject and 'uv' in root_pyproject [' tool' ]:
145- if ' workspace' in root_pyproject [' tool' ][ 'uv' ]:
146- workspace_members = root_pyproject [' tool' ][ 'uv' ][ ' workspace' ].get (' members' , [])
147-
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+
148158 if not workspace_members :
149159 print ("Error: No workspace members found in pyproject.toml" )
150160 return
151-
161+
152162 print (f"Found { len (workspace_members )} workspace members" )
153-
163+
154164 # Build package configs from workspace members
155165 package_configs = []
156166 for member_path in workspace_members :
157167 # Determine package type from path
158168 pkg_type = determine_package_type (member_path )
159169 package_configs .append ((member_path , pkg_type ))
160-
170+
161171 # Collect all package names first and cache pyproject data
162172 all_package_names = set ()
163173 packages : List [PackageInfo ] = []
164174 pyproject_data_cache : Dict [str , Dict ] = {}
165-
175+
166176 for path_str , pkg_type in package_configs :
167- pyproject_path = repo_root / path_str / ' pyproject.toml'
168-
177+ pyproject_path = repo_root / path_str / " pyproject.toml"
178+
169179 if not pyproject_path .exists ():
170180 print (f"Warning: { pyproject_path } not found" )
171181 continue
172-
182+
173183 pyproject_data = read_pyproject_toml (pyproject_path )
174- if ' project' not in pyproject_data or ' name' not in pyproject_data [' project' ]:
184+ if " project" not in pyproject_data or " name" not in pyproject_data [" project" ]:
175185 print (f"Warning: { pyproject_path } is missing project.name field" )
176186 continue
177- pkg_name = pyproject_data [' project' ][ ' name' ]
187+ pkg_name = pyproject_data [" project" ][ " name" ]
178188 all_package_names .add (pkg_name )
179-
189+
180190 # Cache the pyproject data to avoid reading the file again
181191 pyproject_data_cache [pkg_name ] = pyproject_data
182-
192+
183193 pkg_info = PackageInfo (pkg_name , pkg_type , pyproject_path )
184194 packages .append (pkg_info )
185-
195+
186196 # Extract dependencies for each package using cached data
187197 for pkg in packages :
188198 pyproject_data = pyproject_data_cache [pkg .name ]
189199 pkg .dependencies = extract_dependencies (pyproject_data , all_package_names )
190-
200+
191201 # Generate Mermaid diagram
192202 diagram = generate_mermaid_diagram (packages )
193-
203+
194204 # Write to markdown file
195- output_path = repo_root / 'DEPENDENCIES.md'
196- with open (output_path , 'w' , encoding = 'utf-8' ) as f :
197- f .write ('# Agent 365 SDK Python Package Dependencies\n \n ' )
198- f .write ('This diagram shows the internal dependencies between Agent 365 SDK Python packages.\n \n ' )
205+ output_path = repo_root / "DEPENDENCIES.md"
206+ with open (output_path , "w" , encoding = "utf-8" ) as f :
207+ f .write ("# Agent 365 SDK Python Package Dependencies\n \n " )
208+ f .write (
209+ "This diagram shows the internal dependencies between Agent 365 SDK Python packages.\n \n "
210+ )
199211 f .write (diagram )
200- f .write ('\n \n ' )
201- f .write ('## Package Types\n \n ' )
202- f .write ('- **Notifications** (Red): Notification and messaging extensions\n ' )
203- f .write ('- **Runtime** (Blue): Core runtime components\n ' )
204- f .write ('- **Observability** (Green): Telemetry and monitoring core\n ' )
205- f .write ('- **Observability Extensions** (Light Green): Framework-specific observability integrations\n ' )
206- f .write ('- **Tooling** (Orange): Agent tooling SDK core\n ' )
207- f .write ('- **Tooling Extensions** (Light Orange): Framework-specific tooling integrations\n ' )
208-
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+
209225 print (f"Dependency diagram generated successfully: { output_path } " )
210-
226+
211227 # Print summary
212228 print (f"\n Analyzed { len (packages )} packages:" )
213229 for pkg in sorted (packages , key = lambda p : p .name ):
214230 if pkg .dependencies :
215- deps = ', ' .join (sorted (dep for dep in pkg .dependencies ))
231+ deps = ", " .join (sorted (dep for dep in pkg .dependencies ))
216232 print (f" { pkg .name } → { deps } " )
217233 else :
218234 print (f" { pkg .name } (no internal dependencies)" )
219235
220236
221- if __name__ == ' __main__' :
237+ if __name__ == " __main__" :
222238 main ()
0 commit comments