From 81f422614ae89bfca1e20bb3eeee23b8aa0a8cc6 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 16:45:41 +0000 Subject: [PATCH 1/2] docs: automate markdown list fix and generate LLM Skill.md - Add pyadm1/utils/fix_markdown_lists.py to enforce trailing spaces in lists. - Add pyadm1/utils/generate_skill_md.py to create comprehensive API docs for LLMs. - Update .github/workflows/docs.yml to integrate these tools into the build process. - Add download links for Skill.md to LLM API documentation pages. Co-authored-by: dgaida <23057824+dgaida@users.noreply.github.com> --- .github/workflows/docs.yml | 7 ++++ docs/de/user_guide/llm_api.md | 3 ++ docs/en/user_guide/llm_api.md | 3 ++ pyadm1/utils/fix_markdown_lists.py | 39 ++++++++++++++++++++ pyadm1/utils/generate_skill_md.py | 57 ++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+) create mode 100644 pyadm1/utils/fix_markdown_lists.py create mode 100644 pyadm1/utils/generate_skill_md.py diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e201e2e..d977d58 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -47,6 +47,13 @@ jobs: - name: Check API Doc Coverage run: | interrogate pyadm1 + - name: Fix Markdown List Line Endings + run: | + python pyadm1/utils/fix_markdown_lists.py docs + - name: Generate Skill.md for LLMs + run: | + export PYTHONPATH=$PYTHONPATH:. + python pyadm1/utils/generate_skill_md.py - name: Generate Changelog if: github.event_name == 'push' && github.ref == 'refs/heads/master' diff --git a/docs/de/user_guide/llm_api.md b/docs/de/user_guide/llm_api.md index 9c67b54..08b91a9 100644 --- a/docs/de/user_guide/llm_api.md +++ b/docs/de/user_guide/llm_api.md @@ -2,6 +2,9 @@ Diese Seite bietet eine strukturierte Referenz der Methoden und Klassen, die benötigt werden, um ein PyADM1ODE-Simulationsmodell automatisch zu erstellen. Diese Dokumentation ist darauf optimiert, von Large Language Models (LLMs) gelesen zu werden, um Biogasanlagenkonfigurationen zu generieren. +!!! abstract "Skill für LLMs" + Sie können die vollständige API-Dokumentation für LLMs als "Skill"-Datei hier herunterladen: [Skill.md](Skill.md) + ## Kern-Workflow Um eine Simulation zu erstellen, folgen Sie diesem Ablauf: diff --git a/docs/en/user_guide/llm_api.md b/docs/en/user_guide/llm_api.md index b8bb4fc..076c2ed 100644 --- a/docs/en/user_guide/llm_api.md +++ b/docs/en/user_guide/llm_api.md @@ -2,6 +2,9 @@ This page provides a structured reference of the methods and classes required to automatically create a PyADM1ODE simulation model. This documentation is optimized for reading by Large Language Models (LLMs) to generate biogas plant configurations. +!!! abstract "Skill for LLMs" + You can download the full API documentation for LLMs as a "Skill" file here: [Skill.md](Skill.md) + ## Core Workflow To create a simulation, follow this sequence: diff --git a/pyadm1/utils/fix_markdown_lists.py b/pyadm1/utils/fix_markdown_lists.py new file mode 100644 index 0000000..c1df43b --- /dev/null +++ b/pyadm1/utils/fix_markdown_lists.py @@ -0,0 +1,39 @@ +import os +import re +import sys + +def fix_markdown_file(filepath): + with open(filepath, 'r', encoding='utf-8') as f: + lines = f.readlines() + + modified = False + new_lines = [] + + # Pattern for markdown lists: starts with -, *, +, or 1., 2. etc. + list_pattern = re.compile(r'^(\s*)([-*+]|\d+\.)\s+') + + for line in lines: + stripped_line = line.rstrip('\n') + + # Check if it's a list item and doesn't already end with two spaces + if list_pattern.match(stripped_line): + if not stripped_line.endswith(' '): + stripped_line = stripped_line.rstrip() + ' ' + modified = True + + new_lines.append(stripped_line + '\n') + + if modified: + with open(filepath, 'w', encoding='utf-8') as f: + f.writelines(new_lines) + print(f"Fixed lists in: {filepath}") + +def main(directory): + for root, dirs, files in os.walk(directory): + for file in files: + if file.endswith('.md'): + fix_markdown_file(os.path.join(root, file)) + +if __name__ == "__main__": + target_dir = sys.argv[1] if len(sys.argv) > 1 else 'docs' + main(target_dir) diff --git a/pyadm1/utils/generate_skill_md.py b/pyadm1/utils/generate_skill_md.py new file mode 100644 index 0000000..3086cb0 --- /dev/null +++ b/pyadm1/utils/generate_skill_md.py @@ -0,0 +1,57 @@ +import inspect +import os +from pyadm1.substrates.feedstock import Feedstock +from pyadm1.configurator.plant_builder import BiogasPlant +from pyadm1.configurator.plant_configurator import PlantConfigurator +from pyadm1.components.mechanical.pump import Pump +from pyadm1.components.mechanical.mixer import Mixer +from pyadm1.components.feeding.substrate_storage import SubstrateStorage +from pyadm1.components.feeding.feeder import Feeder + +def get_full_doc(obj): + doc = inspect.getdoc(obj) + if not doc: + return "No documentation available." + return doc + +def generate_skill_md(output_path): + classes_to_document = [ + Feedstock, + BiogasPlant, + PlantConfigurator, + Pump, + Mixer, + SubstrateStorage, + Feeder + ] + + content = "# PyADM1ODE Simulation Model Creation Skill\n\n" + content += "This document provides the full API documentation for the classes and methods required to build a PyADM1ODE biogas plant simulation model.\n\n" + + for cls in classes_to_document: + content += f"## {cls.__name__}\n\n" + content += f"```python\n{cls.__name__}\n```\n\n" + content += f"{get_full_doc(cls)}\n\n" + + methods = [m for m in inspect.getmembers(cls, predicate=inspect.isroutine) + if not m[0].startswith('_') or m[0] == '__init__'] + + if methods: + content += f"### Methods for {cls.__name__}\n\n" + for name, func in methods: + try: + sig = str(inspect.signature(func)) + except (ValueError, TypeError): + sig = "(...)" + content += f"#### {name}\n\n" + content += f"```python\n{name}{sig}\n```\n\n" + content += f"{get_full_doc(func)}\n\n" + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + with open(output_path, 'w', encoding='utf-8') as f: + f.write(content) + print(f"Generated Skill.md at: {output_path}") + +if __name__ == "__main__": + generate_skill_md('docs/en/user_guide/Skill.md') + generate_skill_md('docs/de/user_guide/Skill.md') From fbeabe88bd71994f2234156b3f90286a3177dd4a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 16:55:09 +0000 Subject: [PATCH 2/2] style: fix linting issues in doc utility scripts Reformatted pyadm1/utils/fix_markdown_lists.py and pyadm1/utils/generate_skill_md.py using black to comply with project linting standards. Co-authored-by: dgaida <23057824+dgaida@users.noreply.github.com> --- pyadm1/utils/fix_markdown_lists.py | 21 ++++++++++++--------- pyadm1/utils/generate_skill_md.py | 24 ++++++++++-------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/pyadm1/utils/fix_markdown_lists.py b/pyadm1/utils/fix_markdown_lists.py index c1df43b..67f12a8 100644 --- a/pyadm1/utils/fix_markdown_lists.py +++ b/pyadm1/utils/fix_markdown_lists.py @@ -2,38 +2,41 @@ import re import sys + def fix_markdown_file(filepath): - with open(filepath, 'r', encoding='utf-8') as f: + with open(filepath, "r", encoding="utf-8") as f: lines = f.readlines() modified = False new_lines = [] # Pattern for markdown lists: starts with -, *, +, or 1., 2. etc. - list_pattern = re.compile(r'^(\s*)([-*+]|\d+\.)\s+') + list_pattern = re.compile(r"^(\s*)([-*+]|\d+\.)\s+") for line in lines: - stripped_line = line.rstrip('\n') + stripped_line = line.rstrip("\n") # Check if it's a list item and doesn't already end with two spaces if list_pattern.match(stripped_line): - if not stripped_line.endswith(' '): - stripped_line = stripped_line.rstrip() + ' ' + if not stripped_line.endswith(" "): + stripped_line = stripped_line.rstrip() + " " modified = True - new_lines.append(stripped_line + '\n') + new_lines.append(stripped_line + "\n") if modified: - with open(filepath, 'w', encoding='utf-8') as f: + with open(filepath, "w", encoding="utf-8") as f: f.writelines(new_lines) print(f"Fixed lists in: {filepath}") + def main(directory): for root, dirs, files in os.walk(directory): for file in files: - if file.endswith('.md'): + if file.endswith(".md"): fix_markdown_file(os.path.join(root, file)) + if __name__ == "__main__": - target_dir = sys.argv[1] if len(sys.argv) > 1 else 'docs' + target_dir = sys.argv[1] if len(sys.argv) > 1 else "docs" main(target_dir) diff --git a/pyadm1/utils/generate_skill_md.py b/pyadm1/utils/generate_skill_md.py index 3086cb0..ab5ff0c 100644 --- a/pyadm1/utils/generate_skill_md.py +++ b/pyadm1/utils/generate_skill_md.py @@ -8,22 +8,16 @@ from pyadm1.components.feeding.substrate_storage import SubstrateStorage from pyadm1.components.feeding.feeder import Feeder + def get_full_doc(obj): doc = inspect.getdoc(obj) if not doc: return "No documentation available." return doc + def generate_skill_md(output_path): - classes_to_document = [ - Feedstock, - BiogasPlant, - PlantConfigurator, - Pump, - Mixer, - SubstrateStorage, - Feeder - ] + classes_to_document = [Feedstock, BiogasPlant, PlantConfigurator, Pump, Mixer, SubstrateStorage, Feeder] content = "# PyADM1ODE Simulation Model Creation Skill\n\n" content += "This document provides the full API documentation for the classes and methods required to build a PyADM1ODE biogas plant simulation model.\n\n" @@ -33,8 +27,9 @@ def generate_skill_md(output_path): content += f"```python\n{cls.__name__}\n```\n\n" content += f"{get_full_doc(cls)}\n\n" - methods = [m for m in inspect.getmembers(cls, predicate=inspect.isroutine) - if not m[0].startswith('_') or m[0] == '__init__'] + methods = [ + m for m in inspect.getmembers(cls, predicate=inspect.isroutine) if not m[0].startswith("_") or m[0] == "__init__" + ] if methods: content += f"### Methods for {cls.__name__}\n\n" @@ -48,10 +43,11 @@ def generate_skill_md(output_path): content += f"{get_full_doc(func)}\n\n" os.makedirs(os.path.dirname(output_path), exist_ok=True) - with open(output_path, 'w', encoding='utf-8') as f: + with open(output_path, "w", encoding="utf-8") as f: f.write(content) print(f"Generated Skill.md at: {output_path}") + if __name__ == "__main__": - generate_skill_md('docs/en/user_guide/Skill.md') - generate_skill_md('docs/de/user_guide/Skill.md') + generate_skill_md("docs/en/user_guide/Skill.md") + generate_skill_md("docs/de/user_guide/Skill.md")