2525 "handling, and MIMO connections."
2626)
2727
28+ # Number of example notebooks to include full code for in llms-full.txt
29+ MAX_FULL_EXAMPLES = 3
30+
2831
2932def load_package_manifest (package_id : str ) -> dict | None :
3033 path = STATIC_DIR / package_id / "manifest.json"
@@ -52,6 +55,70 @@ def load_version_data(package_id: str, tag: str) -> tuple[dict | None, dict | No
5255 return api_data , manifest
5356
5457
58+ def load_notebook_code (package_id : str , tag : str , filename : str ) -> list [str ]:
59+ """Extract code cells from a notebook, filtering out boilerplate."""
60+ nb_path = STATIC_DIR / package_id / tag / "notebooks" / filename
61+ if not nb_path .exists ():
62+ return []
63+
64+ with open (nb_path , "r" , encoding = "utf-8" ) as f :
65+ nb = json .load (f )
66+
67+ code_blocks = []
68+ for cell in nb .get ("cells" , []):
69+ if cell .get ("cell_type" ) != "code" :
70+ continue
71+ src = "" .join (cell .get ("source" , [])).strip ()
72+ if not src :
73+ continue
74+ # Skip pure matplotlib/style boilerplate
75+ lines = src .split ("\n " )
76+ meaningful = [
77+ l for l in lines
78+ if not l .strip ().startswith ("plt.style.use" )
79+ and not l .strip ().startswith ("plt.show" )
80+ and not l .strip ().startswith ("plt.figure" )
81+ and not l .strip ().startswith ("plt.subplot" )
82+ and not l .strip ().startswith ("plt.tight_layout" )
83+ and not l .strip ().startswith ("fig," )
84+ and not l .strip ().startswith ("fig =" )
85+ and not l .strip ().startswith ("ax." )
86+ and not l .strip ().startswith ("ax," )
87+ and not l .strip ().startswith ("axes" )
88+ ]
89+ cleaned = "\n " .join (meaningful ).strip ()
90+ if cleaned :
91+ code_blocks .append (cleaned )
92+
93+ return code_blocks
94+
95+
96+ def extract_quickstart (api_data : dict ) -> str | None :
97+ """Extract a minimal quickstart from the API data.
98+
99+ Builds a quickstart showing the basic pattern:
100+ import -> create blocks -> connect -> simulate.
101+ """
102+ if not api_data :
103+ return None
104+
105+ modules = api_data .get ("modules" , {})
106+
107+ # Collect block class names from blocks modules
108+ block_names = []
109+ for mod_name , mod in modules .items ():
110+ if ".blocks." in mod_name :
111+ for cls in mod .get ("classes" , []):
112+ name = cls ["name" ]
113+ if not name .startswith ("_" ):
114+ block_names .append (name )
115+
116+ if not block_names :
117+ return None
118+
119+ return None # We'll use real notebook examples instead
120+
121+
55122def generate_llms_txt () -> str :
56123 """Generate the lightweight llms.txt index."""
57124 lines = []
@@ -60,6 +127,14 @@ def generate_llms_txt() -> str:
60127 lines .append (f"> { DESCRIPTION } " )
61128 lines .append ("" )
62129
130+ # Installation
131+ lines .append ("## Installation" )
132+ lines .append ("" )
133+ lines .append ("```" )
134+ lines .append ("pip install pathsim" )
135+ lines .append ("```" )
136+ lines .append ("" )
137+
63138 for package_id , pkg_config in PACKAGES .items ():
64139 pkg_manifest = load_package_manifest (package_id )
65140 if not pkg_manifest :
@@ -118,6 +193,7 @@ def generate_llms_txt() -> str:
118193 lines .append ("" )
119194 lines .append ("- [PathSim Homepage](https://pathsim.org): Project homepage" )
120195 lines .append ("- [PathView Editor](https://view.pathsim.org): Browser-based visual block diagram editor" )
196+ lines .append ("- [PathSim Codegen](https://code.pathsim.org): Generate C99 code from PathSim models" )
121197 lines .append ("- [GitHub](https://github.com/pathsim): Source code repositories" )
122198 lines .append ("- [PyPI](https://pypi.org/project/pathsim): Python package" )
123199 lines .append ("- [JOSS Paper](https://doi.org/10.21105/joss.07484): Published paper" )
@@ -127,13 +203,23 @@ def generate_llms_txt() -> str:
127203
128204
129205def generate_llms_full_txt () -> str :
130- """Generate llms-full.txt with complete API documentation content ."""
206+ """Generate llms-full.txt with complete API documentation and code examples ."""
131207 lines = []
132208 lines .append ("# PathSim Documentation (Full)" )
133209 lines .append ("" )
134210 lines .append (f"> { DESCRIPTION } " )
135211 lines .append ("" )
136212
213+ # Installation
214+ lines .append ("## Installation" )
215+ lines .append ("" )
216+ lines .append ("```bash" )
217+ lines .append ("pip install pathsim" )
218+ lines .append ("pip install pathsim-chem # Chemical engineering toolbox" )
219+ lines .append ("pip install pathsim-rf # RF/microwave toolbox" )
220+ lines .append ("```" )
221+ lines .append ("" )
222+
137223 for package_id , pkg_config in PACKAGES .items ():
138224 pkg_manifest = load_package_manifest (package_id )
139225 if not pkg_manifest :
@@ -147,11 +233,35 @@ def generate_llms_full_txt() -> str:
147233 lines .append (f"## { display_name } ({ latest } )" )
148234 lines .append ("" )
149235
236+ # Quickstart from first example notebook
237+ if manifest :
238+ notebooks = manifest .get ("notebooks" , [])
239+ if notebooks :
240+ first_nb = notebooks [0 ]
241+ code_blocks = load_notebook_code (
242+ package_id , latest , first_nb .get ("file" , "" )
243+ )
244+ if code_blocks :
245+ lines .append ("### Quickstart" )
246+ lines .append ("" )
247+ lines .append (
248+ f"From the [{ first_nb ['title' ]} ]"
249+ f"({ BASE_URL } /{ package_id } /{ latest } /examples/{ first_nb ['slug' ]} ) example:"
250+ )
251+ lines .append ("" )
252+ lines .append ("```python" )
253+ lines .append ("\n \n " .join (code_blocks ))
254+ lines .append ("```" )
255+ lines .append ("" )
256+
150257 # Full API content
151258 if api_data :
259+ lines .append ("### API Reference" )
260+ lines .append ("" )
261+
152262 modules = api_data .get ("modules" , {})
153263 for module_name , module in modules .items ():
154- lines .append (f"### { module_name } " )
264+ lines .append (f"#### { module_name } " )
155265 lines .append ("" )
156266
157267 desc = module .get ("description" , "" )
@@ -165,76 +275,128 @@ def generate_llms_full_txt() -> str:
165275 bases = cls .get ("bases" , [])
166276
167277 base_str = f"({ ', ' .join (bases )} )" if bases else ""
168- lines .append (f"#### class { cls_name } { base_str } " )
278+ lines .append (f"##### class { cls_name } { base_str } " )
169279 lines .append ("" )
170280
171281 if cls_desc :
172282 lines .append (cls_desc )
173283 lines .append ("" )
174284
285+ # Parameters (constructor args)
286+ params = cls .get ("parameters" , [])
287+ if params :
288+ lines .append ("**Parameters:**" )
289+ lines .append ("" )
290+ for p in params :
291+ p_name = p .get ("name" , "" )
292+ p_type = p .get ("type" , "" )
293+ p_default = p .get ("default" , None )
294+ p_desc = p .get ("description" , "" )
295+ type_str = f" ({ p_type } )" if p_type else ""
296+ default_str = f", default={ p_default } " if p_default is not None else ""
297+ entry = f"- `{ p_name } { type_str } { default_str } `"
298+ if p_desc :
299+ entry += f" — { p_desc } "
300+ lines .append (entry )
301+ lines .append ("" )
302+
175303 # Attributes
176- for attr in cls .get ("attributes" , []):
177- attr_name = attr .get ("name" , "" )
178- attr_type = attr .get ("type" , "" )
179- attr_desc = attr .get ("description" , "" )
180- if attr_name .startswith ("_" ):
181- continue
182- type_str = f": { attr_type } " if attr_type else ""
183- entry = f"- `{ attr_name } { type_str } `"
184- if attr_desc :
185- entry += f" — { attr_desc } "
186- lines .append (entry )
187-
188- if cls .get ("attributes" ):
304+ attrs = [
305+ a for a in cls .get ("attributes" , [])
306+ if not a .get ("name" , "" ).startswith ("_" )
307+ ]
308+ if attrs :
309+ lines .append ("**Attributes:**" )
310+ lines .append ("" )
311+ for attr in attrs :
312+ attr_name = attr .get ("name" , "" )
313+ attr_type = attr .get ("type" , "" )
314+ attr_desc = attr .get ("description" , "" )
315+ type_str = f": { attr_type } " if attr_type else ""
316+ entry = f"- `{ attr_name } { type_str } `"
317+ if attr_desc :
318+ entry += f" — { attr_desc } "
319+ lines .append (entry )
189320 lines .append ("" )
190321
191322 # Methods
192- for method in cls .get ("methods" , []):
193- method_name = method .get ("name" , "" )
194- if method_name .startswith ("_" ) and method_name != "__init__" :
195- continue
196- sig = method .get ("signature" , "()" )
197- method_desc = method .get ("description" , "" )
198- lines .append (f"**{ cls_name } .{ method_name } **`{ sig } `" )
199- if method_desc :
200- lines .append (f": { method_desc } " )
201- lines .append ("" )
323+ methods = [
324+ m for m in cls .get ("methods" , [])
325+ if not m .get ("name" , "" ).startswith ("_" )
326+ or m .get ("name" ) == "__init__"
327+ ]
328+ if methods :
329+ for method in methods :
330+ method_name = method .get ("name" , "" )
331+ sig = method .get ("signature" , "()" )
332+ method_desc = method .get ("description" , "" )
333+ lines .append (f"**{ cls_name } .{ method_name } **`{ sig } `" )
334+ if method_desc :
335+ lines .append (f": { method_desc } " )
336+ # Method parameters
337+ m_params = method .get ("parameters" , [])
338+ if m_params :
339+ for p in m_params :
340+ p_name = p .get ("name" , "" )
341+ if p_name in ("self" , "cls" ):
342+ continue
343+ p_desc = p .get ("description" , "" )
344+ p_default = p .get ("default" , None )
345+ default_str = f", default={ p_default } " if p_default is not None else ""
346+ entry = f" - `{ p_name } { default_str } `"
347+ if p_desc :
348+ entry += f" — { p_desc } "
349+ lines .append (entry )
350+ lines .append ("" )
202351
203352 for func in module .get ("functions" , []):
204353 func_name = func .get ("name" , "" )
205354 sig = func .get ("signature" , "()" )
206355 func_desc = func .get ("description" , "" )
207- lines .append (f"#### { func_name } `{ sig } `" )
356+ lines .append (f"##### { func_name } `{ sig } `" )
208357 if func_desc :
209358 lines .append (func_desc )
210359 lines .append ("" )
211360
212- # Examples with descriptions
361+ # Example code from notebooks
213362 if manifest :
214363 notebooks = manifest .get ("notebooks" , [])
215364 if notebooks :
216- lines .append (f "### Examples" )
365+ lines .append ("### Examples" )
217366 lines .append ("" )
218- for nb in notebooks :
367+
368+ for i , nb in enumerate (notebooks ):
219369 slug = nb .get ("slug" , "" )
220370 title = nb .get ("title" , slug )
221371 desc = nb .get ("description" , "" )
222372 tags = nb .get ("tags" , [])
223- category = nb .get ("category" , "" )
224373 url = f"{ BASE_URL } /{ package_id } /{ latest } /examples/{ slug } "
225374
226375 lines .append (f"#### [{ title } ]({ url } )" )
227376 if desc :
228377 lines .append (desc )
229378 if tags :
230379 lines .append (f"Tags: { ', ' .join (tags )} " )
380+
381+ # Include full code for the first few examples
382+ if i < MAX_FULL_EXAMPLES :
383+ code_blocks = load_notebook_code (
384+ package_id , latest , nb .get ("file" , "" )
385+ )
386+ if code_blocks :
387+ lines .append ("" )
388+ lines .append ("```python" )
389+ lines .append ("\n \n " .join (code_blocks ))
390+ lines .append ("```" )
391+
231392 lines .append ("" )
232393
233394 # Links
234395 lines .append ("## Links" )
235396 lines .append ("" )
236397 lines .append ("- [PathSim Homepage](https://pathsim.org): Project homepage" )
237398 lines .append ("- [PathView Editor](https://view.pathsim.org): Browser-based visual block diagram editor" )
399+ lines .append ("- [PathSim Codegen](https://code.pathsim.org): Generate C99 code from PathSim models" )
238400 lines .append ("- [GitHub](https://github.com/pathsim): Source code repositories" )
239401 lines .append ("- [PyPI](https://pypi.org/project/pathsim): Python package" )
240402 lines .append ("- [JOSS Paper](https://doi.org/10.21105/joss.07484): Published paper" )
0 commit comments