261 lines
8.3 KiB
Python
261 lines
8.3 KiB
Python
|
|
"""
|
||
|
|
Test Phase 3.1: End-to-End Integration
|
||
|
|
|
||
|
|
This test demonstrates the complete automation pipeline:
|
||
|
|
Phase 2.7 LLM Output → Phase 3 Research Agent → Generated Extractor → Execution on OP2
|
||
|
|
|
||
|
|
Author: Atomizer Development Team
|
||
|
|
Date: 2025-01-16
|
||
|
|
"""
|
||
|
|
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
# Add project root to path
|
||
|
|
project_root = Path(__file__).parent.parent
|
||
|
|
sys.path.insert(0, str(project_root))
|
||
|
|
|
||
|
|
from optimization_engine.extractor_orchestrator import ExtractorOrchestrator
|
||
|
|
|
||
|
|
|
||
|
|
def test_end_to_end_workflow():
|
||
|
|
"""
|
||
|
|
Complete end-to-end test:
|
||
|
|
1. Phase 2.7 LLM output (simulated)
|
||
|
|
2. Phase 3.1 orchestrator generates extractors
|
||
|
|
3. Execute extractors on real OP2 file
|
||
|
|
"""
|
||
|
|
print("=" * 80)
|
||
|
|
print("Phase 3.1: End-to-End Integration Test")
|
||
|
|
print("=" * 80)
|
||
|
|
print()
|
||
|
|
|
||
|
|
# ========================================================================
|
||
|
|
# STEP 1: Phase 2.7 LLM Output (User Request Analysis)
|
||
|
|
# ========================================================================
|
||
|
|
|
||
|
|
print("STEP 1: Simulating Phase 2.7 LLM Analysis")
|
||
|
|
print("-" * 80)
|
||
|
|
print()
|
||
|
|
|
||
|
|
# User request:
|
||
|
|
# "Extract displacement from OP2, calculate average and normalize by max allowed"
|
||
|
|
|
||
|
|
llm_output = {
|
||
|
|
"engineering_features": [
|
||
|
|
{
|
||
|
|
"action": "extract_displacement",
|
||
|
|
"domain": "result_extraction",
|
||
|
|
"description": "Extract displacement results from OP2 file",
|
||
|
|
"params": {
|
||
|
|
"result_type": "displacement"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"inline_calculations": [
|
||
|
|
{
|
||
|
|
"action": "find_maximum",
|
||
|
|
"params": {"input": "max_displacement", "operation": "max"}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"action": "normalize",
|
||
|
|
"params": {
|
||
|
|
"input": "max_displacement",
|
||
|
|
"reference": "max_allowed_disp",
|
||
|
|
"value": 5.0 # mm
|
||
|
|
}
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"post_processing_hooks": [
|
||
|
|
{
|
||
|
|
"action": "weighted_objective",
|
||
|
|
"params": {
|
||
|
|
"inputs": ["norm_disp"],
|
||
|
|
"weights": [1.0],
|
||
|
|
"objective": "minimize"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
|
||
|
|
print("User Request (natural language):")
|
||
|
|
print(" 'Extract displacement, normalize by 5mm, minimize'")
|
||
|
|
print()
|
||
|
|
print("LLM Analysis Result:")
|
||
|
|
print(f" - Engineering features: {len(llm_output['engineering_features'])}")
|
||
|
|
print(f" - Inline calculations: {len(llm_output['inline_calculations'])}")
|
||
|
|
print(f" - Post-processing hooks: {len(llm_output['post_processing_hooks'])}")
|
||
|
|
print()
|
||
|
|
|
||
|
|
# ========================================================================
|
||
|
|
# STEP 2: Phase 3.1 Orchestrator (Auto-Generate Extractors)
|
||
|
|
# ========================================================================
|
||
|
|
|
||
|
|
print("STEP 2: Phase 3.1 Orchestrator - Generating Extractors")
|
||
|
|
print("-" * 80)
|
||
|
|
print()
|
||
|
|
|
||
|
|
# Initialize orchestrator
|
||
|
|
orchestrator = ExtractorOrchestrator()
|
||
|
|
|
||
|
|
# Process LLM workflow
|
||
|
|
print("Processing LLM workflow...")
|
||
|
|
extractors = orchestrator.process_llm_workflow(llm_output)
|
||
|
|
|
||
|
|
print(f"Generated {len(extractors)} extractor(s):")
|
||
|
|
for ext in extractors:
|
||
|
|
print(f" - {ext.name}")
|
||
|
|
print(f" Function: {ext.function_name}()")
|
||
|
|
print(f" Pattern: {ext.extraction_pattern.name}")
|
||
|
|
print(f" File: {ext.file_path.name}")
|
||
|
|
print()
|
||
|
|
|
||
|
|
# ========================================================================
|
||
|
|
# STEP 3: Execute Extractor on Real OP2 File
|
||
|
|
# ========================================================================
|
||
|
|
|
||
|
|
print("STEP 3: Executing Generated Extractor on Real OP2")
|
||
|
|
print("-" * 80)
|
||
|
|
print()
|
||
|
|
|
||
|
|
# Use bracket OP2 file
|
||
|
|
op2_file = project_root / "tests" / "bracket_sim1-solution_1.op2"
|
||
|
|
|
||
|
|
if not op2_file.exists():
|
||
|
|
print(f" [WARNING] OP2 file not found: {op2_file}")
|
||
|
|
print(" Skipping execution test")
|
||
|
|
return
|
||
|
|
|
||
|
|
print(f"OP2 File: {op2_file.name}")
|
||
|
|
print()
|
||
|
|
|
||
|
|
# Execute extractor
|
||
|
|
try:
|
||
|
|
print("Executing extractor...")
|
||
|
|
result = orchestrator.execute_extractor(
|
||
|
|
'extract_displacement',
|
||
|
|
op2_file,
|
||
|
|
subcase=1
|
||
|
|
)
|
||
|
|
|
||
|
|
print(" [OK] Extraction successful!")
|
||
|
|
print()
|
||
|
|
print("Extraction Results:")
|
||
|
|
for key, value in result.items():
|
||
|
|
if isinstance(value, float):
|
||
|
|
print(f" {key}: {value:.6f}")
|
||
|
|
else:
|
||
|
|
print(f" {key}: {value}")
|
||
|
|
print()
|
||
|
|
|
||
|
|
# ========================================================================
|
||
|
|
# STEP 4: Simulate Inline Calculations (Phase 2.8)
|
||
|
|
# ========================================================================
|
||
|
|
|
||
|
|
print("STEP 4: Simulating Phase 2.8 Inline Calculations")
|
||
|
|
print("-" * 80)
|
||
|
|
print()
|
||
|
|
|
||
|
|
# Auto-generated inline code (Phase 2.8):
|
||
|
|
max_disp = result['max_displacement']
|
||
|
|
max_allowed_disp = 5.0 # From LLM params
|
||
|
|
norm_disp = max_disp / max_allowed_disp
|
||
|
|
|
||
|
|
print(f" max_displacement = {max_disp:.6f} mm")
|
||
|
|
print(f" max_allowed_disp = {max_allowed_disp} mm")
|
||
|
|
print(f" norm_disp = max_displacement / max_allowed_disp = {norm_disp:.6f}")
|
||
|
|
print()
|
||
|
|
|
||
|
|
# ========================================================================
|
||
|
|
# STEP 5: Simulate Post-Processing Hook (Phase 2.9)
|
||
|
|
# ========================================================================
|
||
|
|
|
||
|
|
print("STEP 5: Simulating Phase 2.9 Post-Processing Hook")
|
||
|
|
print("-" * 80)
|
||
|
|
print()
|
||
|
|
|
||
|
|
# Auto-generated hook (Phase 2.9):
|
||
|
|
# weighted_objective = 1.0 * norm_disp
|
||
|
|
objective = norm_disp
|
||
|
|
|
||
|
|
print(f" weighted_objective = 1.0 * norm_disp = {objective:.6f}")
|
||
|
|
print()
|
||
|
|
|
||
|
|
# ========================================================================
|
||
|
|
# FINAL RESULT
|
||
|
|
# ========================================================================
|
||
|
|
|
||
|
|
print("=" * 80)
|
||
|
|
print("END-TO-END TEST: PASSED!")
|
||
|
|
print("=" * 80)
|
||
|
|
print()
|
||
|
|
print("Complete Automation Pipeline Verified:")
|
||
|
|
print(" ✓ Phase 2.7: LLM analyzed user request")
|
||
|
|
print(" ✓ Phase 3.0: Research agent found extraction pattern")
|
||
|
|
print(" ✓ Phase 3.1: Orchestrator generated extractor code")
|
||
|
|
print(" ✓ Phase 3.1: Dynamic loading and execution on OP2")
|
||
|
|
print(" ✓ Phase 2.8: Inline calculations executed")
|
||
|
|
print(" ✓ Phase 2.9: Post-processing hook applied")
|
||
|
|
print()
|
||
|
|
print(f"Final objective value: {objective:.6f}")
|
||
|
|
print()
|
||
|
|
print("🚀 ZERO MANUAL CODING - COMPLETE AUTOMATION!")
|
||
|
|
print()
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f" [ERROR] Execution failed: {e}")
|
||
|
|
import traceback
|
||
|
|
traceback.print_exc()
|
||
|
|
return
|
||
|
|
|
||
|
|
|
||
|
|
def test_multiple_extractors():
|
||
|
|
"""Test generating multiple extractors in one workflow."""
|
||
|
|
print("=" * 80)
|
||
|
|
print("Phase 3.1: Multiple Extractors Test")
|
||
|
|
print("=" * 80)
|
||
|
|
print()
|
||
|
|
|
||
|
|
# Simulate request with both displacement AND stress extraction
|
||
|
|
llm_output = {
|
||
|
|
"engineering_features": [
|
||
|
|
{
|
||
|
|
"action": "extract_displacement",
|
||
|
|
"domain": "result_extraction",
|
||
|
|
"description": "Extract displacement from OP2",
|
||
|
|
"params": {"result_type": "displacement"}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"action": "extract_solid_stress",
|
||
|
|
"domain": "result_extraction",
|
||
|
|
"description": "Extract von Mises stress from solid elements",
|
||
|
|
"params": {
|
||
|
|
"element_types": ["CTETRA", "CHEXA"],
|
||
|
|
"result_type": "stress"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
|
||
|
|
orchestrator = ExtractorOrchestrator()
|
||
|
|
extractors = orchestrator.process_llm_workflow(llm_output)
|
||
|
|
|
||
|
|
print(f"Generated {len(extractors)} extractors:")
|
||
|
|
for ext in extractors:
|
||
|
|
print(f" - {ext.name} ({ext.extraction_pattern.name})")
|
||
|
|
|
||
|
|
print()
|
||
|
|
print("Multiple extractors generation: PASSED!")
|
||
|
|
print()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
# Run tests
|
||
|
|
test_end_to_end_workflow()
|
||
|
|
print()
|
||
|
|
test_multiple_extractors()
|
||
|
|
|
||
|
|
print("=" * 80)
|
||
|
|
print("All Phase 3.1 Integration Tests Complete!")
|
||
|
|
print("=" * 80)
|