Files
Atomizer/optimization_engine/step_classifier.py
Anto01 0a7cca9c6a feat: Complete Phase 2.5-2.7 - Intelligent LLM-Powered Workflow Analysis
This commit implements three major architectural improvements to transform
Atomizer from static pattern matching to intelligent AI-powered analysis.

## Phase 2.5: Intelligent Codebase-Aware Gap Detection 

Created intelligent system that understands existing capabilities before
requesting examples:

**New Files:**
- optimization_engine/codebase_analyzer.py (379 lines)
  Scans Atomizer codebase for existing FEA/CAE capabilities

- optimization_engine/workflow_decomposer.py (507 lines, v0.2.0)
  Breaks user requests into atomic workflow steps
  Complete rewrite with multi-objective, constraints, subcase targeting

- optimization_engine/capability_matcher.py (312 lines)
  Matches workflow steps to existing code implementations

- optimization_engine/targeted_research_planner.py (259 lines)
  Creates focused research plans for only missing capabilities

**Results:**
- 80-90% coverage on complex optimization requests
- 87-93% confidence in capability matching
- Fixed expression reading misclassification (geometry vs result_extraction)

## Phase 2.6: Intelligent Step Classification 

Distinguishes engineering features from simple math operations:

**New Files:**
- optimization_engine/step_classifier.py (335 lines)

**Classification Types:**
1. Engineering Features - Complex FEA/CAE needing research
2. Inline Calculations - Simple math to auto-generate
3. Post-Processing Hooks - Middleware between FEA steps

## Phase 2.7: LLM-Powered Workflow Intelligence 

Replaces static regex patterns with Claude AI analysis:

**New Files:**
- optimization_engine/llm_workflow_analyzer.py (395 lines)
  Uses Claude API for intelligent request analysis
  Supports both Claude Code (dev) and API (production) modes

- .claude/skills/analyze-workflow.md
  Skill template for LLM workflow analysis integration

**Key Breakthrough:**
- Detects ALL intermediate steps (avg, min, normalization, etc.)
- Understands engineering context (CBUSH vs CBAR, directions, metrics)
- Distinguishes OP2 extraction from part expression reading
- Expected 95%+ accuracy with full nuance detection

## Test Coverage

**New Test Files:**
- tests/test_phase_2_5_intelligent_gap_detection.py (335 lines)
- tests/test_complex_multiobj_request.py (130 lines)
- tests/test_cbush_optimization.py (130 lines)
- tests/test_cbar_genetic_algorithm.py (150 lines)
- tests/test_step_classifier.py (140 lines)
- tests/test_llm_complex_request.py (387 lines)

All tests include:
- UTF-8 encoding for Windows console
- atomizer environment (not test_env)
- Comprehensive validation checks

## Documentation

**New Documentation:**
- docs/PHASE_2_5_INTELLIGENT_GAP_DETECTION.md (254 lines)
- docs/PHASE_2_7_LLM_INTEGRATION.md (227 lines)
- docs/SESSION_SUMMARY_PHASE_2_5_TO_2_7.md (252 lines)

**Updated:**
- README.md - Added Phase 2.5-2.7 completion status
- DEVELOPMENT_ROADMAP.md - Updated phase progress

## Critical Fixes

1. **Expression Reading Misclassification** (lines cited in session summary)
   - Updated codebase_analyzer.py pattern detection
   - Fixed workflow_decomposer.py domain classification
   - Added capability_matcher.py read_expression mapping

2. **Environment Standardization**
   - All code now uses 'atomizer' conda environment
   - Removed test_env references throughout

3. **Multi-Objective Support**
   - WorkflowDecomposer v0.2.0 handles multiple objectives
   - Constraint extraction and validation
   - Subcase and direction targeting

## Architecture Evolution

**Before (Static & Dumb):**
User Request → Regex Patterns → Hardcoded Rules → Missed Steps 

**After (LLM-Powered & Intelligent):**
User Request → Claude AI Analysis → Structured JSON →
├─ Engineering (research needed)
├─ Inline (auto-generate Python)
├─ Hooks (middleware scripts)
└─ Optimization (config) 

## LLM Integration Strategy

**Development Mode (Current):**
- Use Claude Code directly for interactive analysis
- No API consumption or costs
- Perfect for iterative development

**Production Mode (Future):**
- Optional Anthropic API integration
- Falls back to heuristics if no API key
- For standalone batch processing

## Next Steps

- Phase 2.8: Inline Code Generation
- Phase 2.9: Post-Processing Hook Generation
- Phase 3: MCP Integration for automated documentation research

🚀 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 13:35:41 -05:00

333 lines
12 KiB
Python

"""
Step Classifier - Phase 2.6
Classifies workflow steps into:
1. Engineering Features - Complex FEA/CAE operations needing research/documentation
2. Inline Calculations - Simple math operations to generate on-the-fly
3. Post-Processing Hooks - Middleware scripts between engineering steps
Author: Atomizer Development Team
Version: 0.1.0 (Phase 2.6)
Last Updated: 2025-01-16
"""
from typing import Dict, List, Any, Optional
from dataclasses import dataclass
from pathlib import Path
import re
@dataclass
class StepClassification:
"""Classification result for a workflow step."""
step_type: str # 'engineering_feature', 'inline_calculation', 'post_processing_hook'
complexity: str # 'simple', 'moderate', 'complex'
requires_research: bool
requires_documentation: bool
auto_generate: bool
reasoning: str
class StepClassifier:
"""
Intelligently classifies workflow steps to determine if they need:
- Full feature engineering (FEA/CAE operations)
- Inline code generation (simple math)
- Post-processing hooks (middleware)
"""
def __init__(self):
# Engineering operations that require research/documentation
self.engineering_operations = {
# FEA Result Extraction
'extract_result': ['displacement', 'stress', 'strain', 'reaction_force',
'element_force', 'temperature', 'modal', 'buckling'],
# FEA Property Modifications
'update_fea_property': ['cbush_stiffness', 'pcomp_layup', 'mat1_properties',
'pshell_thickness', 'pbeam_properties', 'contact_stiffness'],
# Geometry/CAD Operations
'modify_geometry': ['extrude', 'revolve', 'boolean', 'fillet', 'chamfer'],
'read_expression': ['part_expression', 'assembly_expression'],
# Simulation Setup
'run_analysis': ['sol101', 'sol103', 'sol106', 'sol111', 'sol400'],
'create_material': ['mat1', 'mat8', 'mat9', 'physical_material'],
'apply_loads': ['force', 'moment', 'pressure', 'thermal_load'],
'create_mesh': ['tetra', 'hex', 'shell', 'beam'],
}
# Simple mathematical operations (no feature needed)
self.simple_math_operations = {
'average', 'mean', 'max', 'maximum', 'min', 'minimum',
'sum', 'total', 'count', 'ratio', 'percentage',
'compare', 'difference', 'delta', 'absolute',
'normalize', 'scale', 'round', 'floor', 'ceil'
}
# Statistical operations (still simple, but slightly more complex)
self.statistical_operations = {
'std', 'stddev', 'variance', 'median', 'mode',
'percentile', 'quartile', 'range', 'iqr'
}
# Post-processing indicators
self.post_processing_indicators = {
'custom objective', 'metric', 'criteria', 'evaluation',
'transform', 'filter', 'aggregate', 'combine'
}
def classify_step(self, action: str, domain: str, params: Dict[str, Any],
request_context: str = "") -> StepClassification:
"""
Classify a workflow step into engineering feature, inline calc, or hook.
Args:
action: The action type (e.g., 'extract_result', 'update_parameters')
domain: The domain (e.g., 'result_extraction', 'optimization')
params: Step parameters
request_context: Original user request for context
Returns:
StepClassification with type and reasoning
"""
action_lower = action.lower()
request_lower = request_context.lower()
# Check for engineering operations
if self._is_engineering_operation(action, params):
return StepClassification(
step_type='engineering_feature',
complexity='complex',
requires_research=True,
requires_documentation=True,
auto_generate=False,
reasoning=f"FEA/CAE operation '{action}' requires specialized knowledge and documentation"
)
# Check for simple mathematical calculations
if self._is_simple_calculation(action, params, request_lower):
return StepClassification(
step_type='inline_calculation',
complexity='simple',
requires_research=False,
requires_documentation=False,
auto_generate=True,
reasoning=f"Simple mathematical operation that can be generated inline"
)
# Check for post-processing hooks
if self._is_post_processing_hook(action, params, request_lower):
return StepClassification(
step_type='post_processing_hook',
complexity='moderate',
requires_research=False,
requires_documentation=False,
auto_generate=True,
reasoning=f"Post-processing calculation between FEA steps"
)
# Check if it's a known simple action
if action in ['identify_parameters', 'update_parameters', 'optimize']:
return StepClassification(
step_type='engineering_feature',
complexity='moderate',
requires_research=False, # May already exist
requires_documentation=True,
auto_generate=False,
reasoning=f"Standard optimization workflow step"
)
# Default: treat as engineering feature to be safe
return StepClassification(
step_type='engineering_feature',
complexity='moderate',
requires_research=True,
requires_documentation=True,
auto_generate=False,
reasoning=f"Unknown action type, treating as engineering feature"
)
def _is_engineering_operation(self, action: str, params: Dict[str, Any]) -> bool:
"""Check if this is a complex engineering operation."""
# Check action type
if action in self.engineering_operations:
return True
# Check for FEA-specific parameters
fea_indicators = [
'result_type', 'solver', 'element_type', 'material_type',
'mesh_type', 'load_type', 'subcase', 'solution'
]
for indicator in fea_indicators:
if indicator in params:
return True
# Check for specific result types that need FEA extraction
if 'result_type' in params:
result_type = params['result_type']
engineering_results = ['displacement', 'stress', 'strain', 'reaction_force',
'element_force', 'temperature', 'modal', 'buckling']
if result_type in engineering_results:
return True
return False
def _is_simple_calculation(self, action: str, params: Dict[str, Any],
request_context: str) -> bool:
"""Check if this is a simple mathematical calculation."""
# Check for math keywords in action
action_words = set(action.lower().split('_'))
if action_words & self.simple_math_operations:
return True
# Check for statistical operations
if action_words & self.statistical_operations:
return True
# Check for calculation keywords in request
calc_patterns = [
r'\b(calculate|compute|find)\s+(average|mean|max|min|sum)\b',
r'\b(average|mean)\s+of\b',
r'\bfind\s+the\s+(maximum|minimum)\b',
r'\bcompare\s+.+\s+to\s+',
]
for pattern in calc_patterns:
if re.search(pattern, request_context):
return True
return False
def _is_post_processing_hook(self, action: str, params: Dict[str, Any],
request_context: str) -> bool:
"""Check if this is a post-processing hook between steps."""
# Look for custom objective/metric definitions
for indicator in self.post_processing_indicators:
if indicator in request_context:
# Check if it involves multiple inputs (sign of post-processing)
if 'average' in request_context and 'maximum' in request_context:
return True
if 'compare' in request_context:
return True
if 'assign' in request_context and 'metric' in request_context:
return True
return False
def classify_workflow(self, workflow_steps: List[Any],
request_context: str = "") -> Dict[str, List[Any]]:
"""
Classify all steps in a workflow.
Returns:
{
'engineering_features': [...],
'inline_calculations': [...],
'post_processing_hooks': [...]
}
"""
classified = {
'engineering_features': [],
'inline_calculations': [],
'post_processing_hooks': []
}
for step in workflow_steps:
classification = self.classify_step(
step.action,
step.domain,
step.params,
request_context
)
step_with_classification = {
'step': step,
'classification': classification
}
if classification.step_type == 'engineering_feature':
classified['engineering_features'].append(step_with_classification)
elif classification.step_type == 'inline_calculation':
classified['inline_calculations'].append(step_with_classification)
elif classification.step_type == 'post_processing_hook':
classified['post_processing_hooks'].append(step_with_classification)
return classified
def get_summary(self, classified_workflow: Dict[str, List[Any]]) -> str:
"""Get human-readable summary of classification."""
lines = []
lines.append("Workflow Classification Summary")
lines.append("=" * 80)
lines.append("")
# Engineering features
eng_features = classified_workflow['engineering_features']
lines.append(f"Engineering Features (Need Research): {len(eng_features)}")
for item in eng_features:
step = item['step']
classification = item['classification']
lines.append(f" - {step.action} ({step.domain})")
lines.append(f" Reason: {classification.reasoning}")
lines.append("")
# Inline calculations
inline_calcs = classified_workflow['inline_calculations']
lines.append(f"Inline Calculations (Auto-Generate): {len(inline_calcs)}")
for item in inline_calcs:
step = item['step']
lines.append(f" - {step.action}: {step.params}")
lines.append("")
# Post-processing hooks
hooks = classified_workflow['post_processing_hooks']
lines.append(f"Post-Processing Hooks (Auto-Generate): {len(hooks)}")
for item in hooks:
step = item['step']
lines.append(f" - {step.action}: {step.params}")
return "\n".join(lines)
def main():
"""Test the step classifier."""
from optimization_engine.workflow_decomposer import WorkflowDecomposer
print("Step Classifier Test")
print("=" * 80)
print()
# Test with CBUSH optimization request
request = """I want to extract forces in direction Z of all the 1D elements and find the average of it,
then find the maximum value and compare it to the average, then assign it to a objective metric that needs to be minimized."""
decomposer = WorkflowDecomposer()
classifier = StepClassifier()
print("Request:")
print(request)
print()
# Decompose workflow
steps = decomposer.decompose(request)
print("Workflow Steps:")
for i, step in enumerate(steps, 1):
print(f"{i}. {step.action} ({step.domain})")
print()
# Classify steps
classified = classifier.classify_workflow(steps, request)
# Display summary
print(classifier.get_summary(classified))
if __name__ == '__main__':
main()