Files
Atomizer/optimization_engine/auto_doc.py
Antoine 0e04457539 feat: Implement Agentic Architecture for robust session workflows
Phase 1 - Session Bootstrap:
- Add .claude/ATOMIZER_CONTEXT.md as single entry point for new sessions
- Add study state detection and task routing

Phase 2 - Code Deduplication:
- Add optimization_engine/base_runner.py (ConfigDrivenRunner)
- Add optimization_engine/generic_surrogate.py (ConfigDrivenSurrogate)
- Add optimization_engine/study_state.py for study detection
- Add optimization_engine/templates/ with registry and templates
- Studies now require ~50 lines instead of ~300

Phase 3 - Skill Consolidation:
- Add YAML frontmatter metadata to all skills (versioning, dependencies)
- Consolidate create-study.md into core/study-creation-core.md
- Update 00_BOOTSTRAP.md, 01_CHEATSHEET.md, 02_CONTEXT_LOADER.md

Phase 4 - Self-Expanding Knowledge:
- Add optimization_engine/auto_doc.py for auto-generating documentation
- Generate docs/generated/EXTRACTORS.md (27 extractors documented)
- Generate docs/generated/TEMPLATES.md (6 templates)
- Generate docs/generated/EXTRACTOR_CHEATSHEET.md

Phase 5 - Subagent Implementation:
- Add .claude/commands/study-builder.md (create studies)
- Add .claude/commands/nx-expert.md (NX Open API)
- Add .claude/commands/protocol-auditor.md (config validation)
- Add .claude/commands/results-analyzer.md (results analysis)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 14:52:25 -05:00

342 lines
12 KiB
Python

"""
Auto-Documentation Generator for Atomizer
This module automatically generates documentation from code, ensuring
that skills and protocols stay in sync with the implementation.
Usage:
python -m optimization_engine.auto_doc extractors
python -m optimization_engine.auto_doc templates
python -m optimization_engine.auto_doc all
"""
import inspect
import importlib
import json
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional
def get_extractor_info() -> List[Dict[str, Any]]:
"""Extract information about all registered extractors."""
from optimization_engine import extractors
extractor_info = []
# Get all exported functions
for name in extractors.__all__:
obj = getattr(extractors, name)
if callable(obj):
# Get function signature
try:
sig = inspect.signature(obj)
params = [
{
'name': p.name,
'default': str(p.default) if p.default != inspect.Parameter.empty else None,
'annotation': str(p.annotation) if p.annotation != inspect.Parameter.empty else None
}
for p in sig.parameters.values()
]
except (ValueError, TypeError):
params = []
# Get docstring
doc = inspect.getdoc(obj) or "No documentation available"
# Determine category
category = "general"
if "stress" in name.lower():
category = "stress"
elif "temperature" in name.lower() or "thermal" in name.lower() or "heat" in name.lower():
category = "thermal"
elif "modal" in name.lower() or "frequency" in name.lower():
category = "modal"
elif "zernike" in name.lower():
category = "optical"
elif "mass" in name.lower():
category = "mass"
elif "strain" in name.lower():
category = "strain"
elif "spc" in name.lower() or "reaction" in name.lower() or "force" in name.lower():
category = "forces"
# Determine phase
phase = "Phase 1"
if name in ['extract_principal_stress', 'extract_max_principal_stress',
'extract_min_principal_stress', 'extract_strain_energy',
'extract_total_strain_energy', 'extract_strain_energy_density',
'extract_spc_forces', 'extract_total_reaction_force',
'extract_reaction_component', 'check_force_equilibrium']:
phase = "Phase 2"
elif name in ['extract_temperature', 'extract_temperature_gradient',
'extract_heat_flux', 'get_max_temperature',
'extract_modal_mass', 'extract_frequencies',
'get_first_frequency', 'get_modal_mass_ratio']:
phase = "Phase 3"
extractor_info.append({
'name': name,
'module': obj.__module__,
'category': category,
'phase': phase,
'parameters': params,
'docstring': doc,
'is_class': inspect.isclass(obj)
})
return extractor_info
def get_template_info() -> List[Dict[str, Any]]:
"""Extract information about available study templates."""
templates_file = Path(__file__).parent / 'templates' / 'registry.json'
if not templates_file.exists():
return []
with open(templates_file) as f:
data = json.load(f)
return data.get('templates', [])
def generate_extractor_markdown(extractors: List[Dict[str, Any]]) -> str:
"""Generate markdown documentation for extractors."""
lines = [
"# Atomizer Extractor Library",
"",
f"*Auto-generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}*",
"",
"This document is automatically generated from the extractor source code.",
"",
"---",
"",
"## Quick Reference",
"",
"| Extractor | Category | Phase | Description |",
"|-----------|----------|-------|-------------|",
]
for ext in sorted(extractors, key=lambda x: (x['category'], x['name'])):
doc_first_line = ext['docstring'].split('\n')[0][:60]
lines.append(f"| `{ext['name']}` | {ext['category']} | {ext['phase']} | {doc_first_line} |")
lines.extend(["", "---", ""])
# Group by category
categories = {}
for ext in extractors:
cat = ext['category']
if cat not in categories:
categories[cat] = []
categories[cat].append(ext)
for cat_name, cat_extractors in sorted(categories.items()):
lines.append(f"## {cat_name.title()} Extractors")
lines.append("")
for ext in sorted(cat_extractors, key=lambda x: x['name']):
lines.append(f"### `{ext['name']}`")
lines.append("")
lines.append(f"**Module**: `{ext['module']}`")
lines.append(f"**Phase**: {ext['phase']}")
lines.append("")
# Parameters
if ext['parameters']:
lines.append("**Parameters**:")
lines.append("")
for param in ext['parameters']:
default_str = f" = `{param['default']}`" if param['default'] else ""
lines.append(f"- `{param['name']}`{default_str}")
lines.append("")
# Docstring
lines.append("**Description**:")
lines.append("")
lines.append("```")
lines.append(ext['docstring'])
lines.append("```")
lines.append("")
lines.append("---")
lines.append("")
return '\n'.join(lines)
def generate_template_markdown(templates: List[Dict[str, Any]]) -> str:
"""Generate markdown documentation for templates."""
lines = [
"# Atomizer Study Templates",
"",
f"*Auto-generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}*",
"",
"Available templates for quick study creation.",
"",
"---",
"",
"## Template Reference",
"",
"| Template | Objectives | Extractors |",
"|----------|------------|------------|",
]
for tmpl in templates:
# Handle objectives that might be dicts or strings
obj_list = tmpl.get('objectives', [])
if obj_list and isinstance(obj_list[0], dict):
objectives = ', '.join([o.get('name', str(o)) for o in obj_list])
else:
objectives = ', '.join(obj_list)
extractors = ', '.join(tmpl.get('extractors', []))
lines.append(f"| `{tmpl['name']}` | {objectives} | {extractors} |")
lines.extend(["", "---", ""])
for tmpl in templates:
lines.append(f"## {tmpl['name']}")
lines.append("")
lines.append(f"**Description**: {tmpl.get('description', 'N/A')}")
lines.append("")
lines.append(f"**Category**: {tmpl.get('category', 'N/A')}")
lines.append(f"**Solver**: {tmpl.get('solver', 'N/A')}")
lines.append(f"**Sampler**: {tmpl.get('sampler', 'N/A')}")
lines.append(f"**Turbo Suitable**: {'Yes' if tmpl.get('turbo_suitable') else 'No'}")
lines.append("")
lines.append(f"**Example Study**: `{tmpl.get('example_study', 'N/A')}`")
lines.append("")
if tmpl.get('objectives'):
lines.append("**Objectives**:")
for obj in tmpl['objectives']:
if isinstance(obj, dict):
lines.append(f"- {obj.get('name', '?')} ({obj.get('direction', '?')}) - Extractor: {obj.get('extractor', '?')}")
else:
lines.append(f"- {obj}")
lines.append("")
if tmpl.get('extractors'):
lines.append("**Extractors Used**:")
for ext in tmpl['extractors']:
lines.append(f"- {ext}")
lines.append("")
if tmpl.get('recommended_trials'):
lines.append("**Recommended Trials**:")
for key, val in tmpl['recommended_trials'].items():
lines.append(f"- {key}: {val}")
lines.append("")
lines.append("---")
lines.append("")
return '\n'.join(lines)
def generate_cheatsheet_update(extractors: List[Dict[str, Any]]) -> str:
"""Generate the extractor quick reference for 01_CHEATSHEET.md."""
lines = [
"## Extractor Quick Reference",
"",
"| Physics | Extractor | Function Call |",
"|---------|-----------|---------------|",
]
# Map categories to physics names
physics_map = {
'stress': 'Von Mises stress',
'thermal': 'Temperature',
'modal': 'Natural frequency',
'optical': 'Zernike WFE',
'mass': 'Mass',
'strain': 'Strain energy',
'forces': 'Reaction forces',
'general': 'Displacement',
}
for ext in sorted(extractors, key=lambda x: x['category']):
if ext['is_class']:
continue
physics = physics_map.get(ext['category'], ext['category'])
# Build function call example
params = ext['parameters'][:2] if ext['parameters'] else []
param_str = ', '.join([p['name'] for p in params])
lines.append(f"| {physics} | {ext['name']} | `{ext['name']}({param_str})` |")
return '\n'.join(lines)
def update_atomizer_context(extractors: List[Dict[str, Any]], templates: List[Dict[str, Any]]):
"""Update ATOMIZER_CONTEXT.md with current extractor count."""
context_file = Path(__file__).parent.parent / '.claude' / 'ATOMIZER_CONTEXT.md'
if not context_file.exists():
print(f"Warning: {context_file} not found")
return
content = context_file.read_text()
# Update extractor library version based on count
extractor_count = len(extractors)
template_count = len(templates)
print(f"Found {extractor_count} extractors and {template_count} templates")
# Could add logic here to update version info based on changes
def main():
import sys
if len(sys.argv) < 2:
print("Usage: python -m optimization_engine.auto_doc [extractors|templates|all]")
sys.exit(1)
command = sys.argv[1]
output_dir = Path(__file__).parent.parent / 'docs' / 'generated'
output_dir.mkdir(parents=True, exist_ok=True)
if command in ['extractors', 'all']:
print("Generating extractor documentation...")
extractors = get_extractor_info()
# Write full documentation
doc_content = generate_extractor_markdown(extractors)
(output_dir / 'EXTRACTORS.md').write_text(doc_content)
print(f" Written: {output_dir / 'EXTRACTORS.md'}")
# Write cheatsheet update
cheatsheet = generate_cheatsheet_update(extractors)
(output_dir / 'EXTRACTOR_CHEATSHEET.md').write_text(cheatsheet)
print(f" Written: {output_dir / 'EXTRACTOR_CHEATSHEET.md'}")
print(f" Found {len(extractors)} extractors")
if command in ['templates', 'all']:
print("Generating template documentation...")
templates = get_template_info()
if templates:
doc_content = generate_template_markdown(templates)
(output_dir / 'TEMPLATES.md').write_text(doc_content)
print(f" Written: {output_dir / 'TEMPLATES.md'}")
print(f" Found {len(templates)} templates")
else:
print(" No templates found")
if command == 'all':
print("\nUpdating ATOMIZER_CONTEXT.md...")
extractors = get_extractor_info()
templates = get_template_info()
update_atomizer_context(extractors, templates)
print("\nDone!")
if __name__ == '__main__':
main()