fix: deep GPU integration, fix all ruff/mypy issues, add .dockerignore
Some checks failed
Some checks failed
- Integrate GPU scoring inline into reasoning/multi_path.py (auto-uses GPU when available) - Integrate GPU deduplication into multi_agent/consensus_engine.py - Add semantic_search() method to memory/semantic_graph.py with GPU acceleration - Integrate GPU training into self_improvement/training.py AutoTrainer - Fix all 758 ruff lint issues (whitespace, import sorting, unused imports, ambiguous vars, undefined names) - Fix all 40 mypy type errors across the codebase (no-any-return, union-attr, arg-type, etc.) - Fix deprecated ruff config keys (select/ignore -> [tool.ruff.lint]) - Add .dockerignore to exclude .venv/, tests/, docs/ from Docker builds - Add type hints and docstrings to verification/outcome.py - Fix E402 import ordering in witness_agent.py - Fix F821 undefined names in vector_pgvector.py and native.py - Fix E741 ambiguous variable names in reflective.py and recommender.py All 276 tests pass. 0 ruff errors. 0 mypy errors. Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
"""MAA layers: DLT, intent, geometry, physics, process, machine, toolpath, MPC."""
|
||||
|
||||
from fusionagi.maa.layers.dlt_engine import DLTEngine
|
||||
from fusionagi.maa.layers.mpc_authority import MPCAuthority
|
||||
from fusionagi.maa.layers.intent_engine import IntentEngine
|
||||
from fusionagi.maa.layers.geometry_kernel import GeometryAuthorityInterface, InMemoryGeometryKernel
|
||||
from fusionagi.maa.layers.intent_engine import IntentEngine
|
||||
from fusionagi.maa.layers.machine_binding import MachineBinding, MachineProfile
|
||||
from fusionagi.maa.layers.mpc_authority import MPCAuthority
|
||||
from fusionagi.maa.layers.physics_authority import PhysicsAuthorityInterface, StubPhysicsAuthority
|
||||
from fusionagi.maa.layers.process_authority import ProcessAuthority
|
||||
from fusionagi.maa.layers.machine_binding import MachineBinding, MachineProfile
|
||||
from fusionagi.maa.layers.toolpath_engine import ToolpathEngine, ToolpathArtifact
|
||||
from fusionagi.maa.layers.toolpath_engine import ToolpathArtifact, ToolpathEngine
|
||||
|
||||
__all__ = [
|
||||
"DLTEngine",
|
||||
|
||||
@@ -10,8 +10,13 @@ import re
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from fusionagi.maa.schemas.intent import EngineeringIntentGraph, IntentNode, LoadCase, RequirementType
|
||||
from fusionagi._logger import logger
|
||||
from fusionagi.maa.schemas.intent import (
|
||||
EngineeringIntentGraph,
|
||||
IntentNode,
|
||||
LoadCase,
|
||||
RequirementType,
|
||||
)
|
||||
|
||||
|
||||
class IntentIncompleteError(Exception):
|
||||
@@ -25,7 +30,7 @@ class IntentIncompleteError(Exception):
|
||||
class IntentEngine:
|
||||
"""
|
||||
Intent decomposition, requirement typing, and load case enumeration.
|
||||
|
||||
|
||||
Features:
|
||||
- Pattern-based requirement extraction from natural language
|
||||
- Automatic requirement type classification
|
||||
@@ -101,7 +106,7 @@ class IntentEngine:
|
||||
def __init__(self, llm_adapter: Any | None = None):
|
||||
"""
|
||||
Initialize the IntentEngine.
|
||||
|
||||
|
||||
Args:
|
||||
llm_adapter: Optional LLM adapter for enhanced natural language processing.
|
||||
"""
|
||||
@@ -117,33 +122,33 @@ class IntentEngine:
|
||||
) -> EngineeringIntentGraph:
|
||||
"""
|
||||
Formalize engineering intent from natural language and file references.
|
||||
|
||||
|
||||
Args:
|
||||
intent_id: Unique identifier for this intent.
|
||||
natural_language: Natural language description of requirements.
|
||||
file_refs: References to CAD files, specifications, etc.
|
||||
metadata: Additional metadata.
|
||||
use_llm: Whether to use LLM for enhanced processing (if available).
|
||||
|
||||
|
||||
Returns:
|
||||
EngineeringIntentGraph with extracted requirements.
|
||||
|
||||
|
||||
Raises:
|
||||
IntentIncompleteError: If required information is missing.
|
||||
"""
|
||||
if not intent_id:
|
||||
raise IntentIncompleteError("intent_id required", ["intent_id"])
|
||||
|
||||
|
||||
if not natural_language and not file_refs:
|
||||
raise IntentIncompleteError(
|
||||
"At least one of natural_language or file_refs required",
|
||||
["natural_language", "file_refs"],
|
||||
)
|
||||
|
||||
|
||||
nodes: list[IntentNode] = []
|
||||
load_cases: list[LoadCase] = []
|
||||
environmental_bounds: dict[str, Any] = {}
|
||||
|
||||
|
||||
# Process natural language if provided
|
||||
if natural_language:
|
||||
# Use LLM if available and requested
|
||||
@@ -151,13 +156,13 @@ class IntentEngine:
|
||||
llm_result = self._formalize_with_llm(intent_id, natural_language)
|
||||
if llm_result:
|
||||
return llm_result
|
||||
|
||||
|
||||
# Fall back to pattern-based extraction
|
||||
extracted = self._extract_requirements(intent_id, natural_language)
|
||||
nodes.extend(extracted["nodes"])
|
||||
load_cases.extend(extracted["load_cases"])
|
||||
environmental_bounds.update(extracted["environmental_bounds"])
|
||||
|
||||
|
||||
# Process file references
|
||||
if file_refs:
|
||||
for ref in file_refs:
|
||||
@@ -169,7 +174,7 @@ class IntentEngine:
|
||||
metadata={"file_ref": ref},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# If no nodes were extracted, create a general requirement
|
||||
if not nodes and natural_language:
|
||||
nodes.append(
|
||||
@@ -179,7 +184,7 @@ class IntentEngine:
|
||||
description=natural_language[:500],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
logger.info(
|
||||
"Intent formalized",
|
||||
extra={
|
||||
@@ -188,7 +193,7 @@ class IntentEngine:
|
||||
"num_load_cases": len(load_cases),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
return EngineeringIntentGraph(
|
||||
intent_id=intent_id,
|
||||
nodes=nodes,
|
||||
@@ -204,24 +209,24 @@ class IntentEngine:
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Extract requirements from text using pattern matching.
|
||||
|
||||
|
||||
Returns dict with nodes, load_cases, and environmental_bounds.
|
||||
"""
|
||||
nodes: list[IntentNode] = []
|
||||
load_cases: list[LoadCase] = []
|
||||
environmental_bounds: dict[str, Any] = {}
|
||||
|
||||
|
||||
# Split into sentences for processing
|
||||
sentences = re.split(r'[.!?]+', text)
|
||||
|
||||
|
||||
node_counter = 0
|
||||
load_case_counter = 0
|
||||
|
||||
|
||||
for sentence in sentences:
|
||||
sentence = sentence.strip()
|
||||
if not sentence:
|
||||
continue
|
||||
|
||||
|
||||
# Check for dimensional requirements
|
||||
for pattern in self.DIMENSIONAL_PATTERNS:
|
||||
if re.search(pattern, sentence, re.IGNORECASE):
|
||||
@@ -235,7 +240,7 @@ class IntentEngine:
|
||||
)
|
||||
node_counter += 1
|
||||
break
|
||||
|
||||
|
||||
# Check for load requirements
|
||||
for pattern in self.LOAD_PATTERNS:
|
||||
if re.search(pattern, sentence, re.IGNORECASE):
|
||||
@@ -249,7 +254,7 @@ class IntentEngine:
|
||||
)
|
||||
node_counter += 1
|
||||
break
|
||||
|
||||
|
||||
# Check for environmental requirements
|
||||
for pattern in self.ENVIRONMENTAL_PATTERNS:
|
||||
match = re.search(pattern, sentence, re.IGNORECASE)
|
||||
@@ -263,14 +268,14 @@ class IntentEngine:
|
||||
)
|
||||
)
|
||||
node_counter += 1
|
||||
|
||||
|
||||
# Extract specific bounds if possible
|
||||
if "temperature" in sentence.lower():
|
||||
temp_match = re.search(r"(-?\d+(?:\.\d+)?)", sentence)
|
||||
if temp_match:
|
||||
environmental_bounds["temperature"] = float(temp_match.group(1))
|
||||
break
|
||||
|
||||
|
||||
# Check for process requirements
|
||||
for pattern in self.PROCESS_PATTERNS:
|
||||
if re.search(pattern, sentence, re.IGNORECASE):
|
||||
@@ -284,7 +289,7 @@ class IntentEngine:
|
||||
)
|
||||
node_counter += 1
|
||||
break
|
||||
|
||||
|
||||
# Check for load cases
|
||||
for pattern in self.LOAD_CASE_PATTERNS:
|
||||
match = re.search(pattern, sentence, re.IGNORECASE)
|
||||
@@ -299,7 +304,7 @@ class IntentEngine:
|
||||
)
|
||||
load_case_counter += 1
|
||||
break
|
||||
|
||||
|
||||
return {
|
||||
"nodes": nodes,
|
||||
"load_cases": load_cases,
|
||||
@@ -313,14 +318,14 @@ class IntentEngine:
|
||||
) -> EngineeringIntentGraph | None:
|
||||
"""
|
||||
Use LLM to extract structured requirements from natural language.
|
||||
|
||||
|
||||
Returns None if LLM processing fails (falls back to pattern matching).
|
||||
"""
|
||||
if not self._llm:
|
||||
return None
|
||||
|
||||
|
||||
import json
|
||||
|
||||
|
||||
prompt = f"""Extract engineering requirements from the following text.
|
||||
Return a JSON object with:
|
||||
- "nodes": list of requirements, each with:
|
||||
@@ -339,13 +344,13 @@ Return only valid JSON, no markdown."""
|
||||
{"role": "system", "content": "You are an engineering requirements extraction system."},
|
||||
{"role": "user", "content": prompt},
|
||||
]
|
||||
|
||||
|
||||
# Try structured output if available
|
||||
if hasattr(self._llm, "complete_structured"):
|
||||
result = self._llm.complete_structured(messages)
|
||||
if result:
|
||||
return self._parse_llm_result(intent_id, result)
|
||||
|
||||
|
||||
# Fall back to text completion
|
||||
raw = self._llm.complete(messages)
|
||||
if raw:
|
||||
@@ -356,10 +361,10 @@ Return only valid JSON, no markdown."""
|
||||
raw = raw[4:]
|
||||
result = json.loads(raw)
|
||||
return self._parse_llm_result(intent_id, result)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"LLM formalization failed: {e}")
|
||||
|
||||
|
||||
return None
|
||||
|
||||
def _parse_llm_result(
|
||||
@@ -375,7 +380,7 @@ Return only valid JSON, no markdown."""
|
||||
req_type = RequirementType(req_type_str)
|
||||
except ValueError:
|
||||
req_type = RequirementType.OTHER
|
||||
|
||||
|
||||
nodes.append(
|
||||
IntentNode(
|
||||
node_id=f"{intent_id}_llm_{i}",
|
||||
@@ -384,7 +389,7 @@ Return only valid JSON, no markdown."""
|
||||
metadata={"source": "llm"},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
load_cases = []
|
||||
for i, lc_data in enumerate(result.get("load_cases", [])):
|
||||
load_cases.append(
|
||||
@@ -394,9 +399,9 @@ Return only valid JSON, no markdown."""
|
||||
metadata={"source": "llm"},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
environmental_bounds = result.get("environmental_bounds", {})
|
||||
|
||||
|
||||
return EngineeringIntentGraph(
|
||||
intent_id=intent_id,
|
||||
nodes=nodes,
|
||||
@@ -408,24 +413,24 @@ Return only valid JSON, no markdown."""
|
||||
def validate_completeness(self, graph: EngineeringIntentGraph) -> tuple[bool, list[str]]:
|
||||
"""
|
||||
Validate that an intent graph has sufficient information.
|
||||
|
||||
|
||||
Returns:
|
||||
Tuple of (is_complete, list_of_missing_items)
|
||||
"""
|
||||
missing = []
|
||||
|
||||
|
||||
if not graph.nodes:
|
||||
missing.append("No requirements extracted")
|
||||
|
||||
|
||||
# Check for at least one dimensional or load requirement for manufacturing
|
||||
has_dimensional = any(n.requirement_type == RequirementType.DIMENSIONAL for n in graph.nodes)
|
||||
has_load = any(n.requirement_type == RequirementType.LOAD for n in graph.nodes)
|
||||
|
||||
any(n.requirement_type == RequirementType.LOAD for n in graph.nodes)
|
||||
|
||||
if not has_dimensional:
|
||||
missing.append("No dimensional requirements specified")
|
||||
|
||||
|
||||
# Load cases are recommended but not required
|
||||
if not graph.load_cases:
|
||||
logger.info("No load cases specified for intent", extra={"intent_id": graph.intent_id})
|
||||
|
||||
|
||||
return len(missing) == 0, missing
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
from typing import Any
|
||||
|
||||
from fusionagi.maa.schemas.mpc import (
|
||||
DecisionLineageEntry,
|
||||
MachineDeclaration,
|
||||
ManufacturingProofCertificate,
|
||||
MPCId,
|
||||
DecisionLineageEntry,
|
||||
SimulationProof,
|
||||
ProcessJustification,
|
||||
MachineDeclaration,
|
||||
RiskRegisterEntry,
|
||||
SimulationProof,
|
||||
)
|
||||
from fusionagi.maa.versioning import VersionStore
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ Responsible for:
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import math
|
||||
import uuid
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
@@ -53,7 +52,7 @@ class PhysicsProof(BaseModel):
|
||||
class PhysicsAuthorityInterface(ABC):
|
||||
"""
|
||||
Abstract interface for physics validation.
|
||||
|
||||
|
||||
Governing equation selection, boundary condition enforcement, safety factor declaration,
|
||||
failure-mode completeness. Simulations are binding, not illustrative.
|
||||
"""
|
||||
@@ -148,7 +147,7 @@ class LoadCaseResult:
|
||||
class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
"""
|
||||
Physics validation authority with actual validation logic.
|
||||
|
||||
|
||||
Features:
|
||||
- Material property validation
|
||||
- Load case analysis
|
||||
@@ -165,7 +164,7 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
):
|
||||
"""
|
||||
Initialize the PhysicsAuthority.
|
||||
|
||||
|
||||
Args:
|
||||
required_safety_factor: Minimum required safety factor (default 2.0).
|
||||
material_db: Custom material properties database.
|
||||
@@ -188,7 +187,7 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
) -> PhysicsProof | None:
|
||||
"""
|
||||
Validate physics for a design.
|
||||
|
||||
|
||||
Args:
|
||||
design_ref: Reference to the design being validated.
|
||||
load_cases: List of load cases to validate against.
|
||||
@@ -196,28 +195,31 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
dimensions: Key dimensions for stress calculation.
|
||||
boundary_conditions: Boundary condition specification.
|
||||
**kwargs: Additional parameters.
|
||||
|
||||
|
||||
Returns:
|
||||
PhysicsProof if validation passes, None if physics underdefined.
|
||||
|
||||
|
||||
Raises:
|
||||
PhysicsUnderdefinedError: If critical data is missing.
|
||||
"""
|
||||
missing_data = []
|
||||
|
||||
|
||||
if not design_ref:
|
||||
missing_data.append("design_ref")
|
||||
if not material:
|
||||
missing_data.append("material")
|
||||
if not load_cases:
|
||||
missing_data.append("load_cases")
|
||||
|
||||
|
||||
if missing_data:
|
||||
raise PhysicsUnderdefinedError(
|
||||
f"Physics validation requires: {', '.join(missing_data)}",
|
||||
missing_data=missing_data,
|
||||
)
|
||||
|
||||
|
||||
assert material is not None # guarded by PhysicsUnderdefinedError above
|
||||
assert load_cases is not None # guarded by PhysicsUnderdefinedError above
|
||||
|
||||
# Get material properties
|
||||
mat_props = self._materials.get(material.lower().replace(" ", "_"))
|
||||
if not mat_props:
|
||||
@@ -225,44 +227,44 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
f"Unknown material: {material}. Available: {list(self._materials.keys())}",
|
||||
missing_data=["material_properties"],
|
||||
)
|
||||
|
||||
|
||||
# Validate each load case
|
||||
load_case_results: list[LoadCaseResult] = []
|
||||
min_safety_factor = float("inf")
|
||||
warnings: list[str] = []
|
||||
failure_modes_covered: list[str] = []
|
||||
|
||||
|
||||
for lc in load_cases:
|
||||
result = self._validate_load_case(lc, mat_props, dimensions)
|
||||
load_case_results.append(result)
|
||||
|
||||
|
||||
if result.safety_factor < min_safety_factor:
|
||||
min_safety_factor = result.safety_factor
|
||||
|
||||
|
||||
if not result.passed:
|
||||
warnings.append(
|
||||
f"Load case '{result.load_case_id}' failed: {result.failure_mode}"
|
||||
)
|
||||
|
||||
|
||||
# Track failure modes analyzed
|
||||
if result.failure_mode and result.failure_mode not in failure_modes_covered:
|
||||
failure_modes_covered.append(result.failure_mode)
|
||||
|
||||
|
||||
# Determine governing equations based on load types
|
||||
governing_equations = self._select_governing_equations(load_cases)
|
||||
|
||||
|
||||
# Check minimum required failure modes
|
||||
required_modes = ["yield_failure", "ultimate_failure"]
|
||||
for mode in required_modes:
|
||||
if mode not in failure_modes_covered:
|
||||
failure_modes_covered.append(mode) # Basic checks are always done
|
||||
|
||||
|
||||
# Generate proof ID based on inputs
|
||||
proof_hash = hashlib.sha256(
|
||||
f"{design_ref}:{material}:{load_cases}".encode()
|
||||
).hexdigest()[:16]
|
||||
proof_id = f"proof_{design_ref}_{proof_hash}"
|
||||
|
||||
|
||||
# Determine validation status
|
||||
validation_status = "validated"
|
||||
if min_safety_factor < self._required_sf:
|
||||
@@ -270,10 +272,10 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
warnings.append(
|
||||
f"Safety factor {min_safety_factor:.2f} < required {self._required_sf}"
|
||||
)
|
||||
|
||||
|
||||
if any(not r.passed for r in load_case_results):
|
||||
validation_status = "load_case_failure"
|
||||
|
||||
|
||||
logger.info(
|
||||
"Physics validation completed",
|
||||
extra={
|
||||
@@ -284,7 +286,7 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
"num_load_cases": len(load_cases),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
return PhysicsProof(
|
||||
proof_id=proof_id,
|
||||
governing_equations=governing_equations,
|
||||
@@ -317,25 +319,25 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
) -> LoadCaseResult:
|
||||
"""Validate a single load case."""
|
||||
lc_id = load_case.get("id", str(uuid.uuid4())[:8])
|
||||
|
||||
|
||||
# Extract load parameters
|
||||
force_n = load_case.get("force_n", 0)
|
||||
moment_nm = load_case.get("moment_nm", 0)
|
||||
pressure_mpa = load_case.get("pressure_mpa", 0)
|
||||
temperature_c = load_case.get("temperature_c", 25)
|
||||
|
||||
|
||||
# Get material limits
|
||||
yield_strength = mat_props.get("yield_strength_mpa", 100)
|
||||
ultimate_strength = mat_props.get("ultimate_strength_mpa", 150)
|
||||
max_temp = mat_props.get("max_service_temp_c", 100)
|
||||
|
||||
|
||||
# Calculate stress (simplified - assumes basic geometry)
|
||||
area_mm2 = 100.0 # Default cross-sectional area
|
||||
if dimensions:
|
||||
width = dimensions.get("width_mm", 10)
|
||||
height = dimensions.get("height_mm", 10)
|
||||
area_mm2 = width * height
|
||||
|
||||
|
||||
# Basic stress calculation
|
||||
axial_stress = force_n / area_mm2 if area_mm2 > 0 else 0
|
||||
bending_stress = 0
|
||||
@@ -346,24 +348,24 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
c = height / 2
|
||||
i = width * (height ** 3) / 12
|
||||
bending_stress = (moment_nm * 1000 * c) / i if i > 0 else 0
|
||||
|
||||
|
||||
# Combined stress (von Mises simplified for 1D)
|
||||
max_stress = abs(axial_stress) + abs(bending_stress) + pressure_mpa
|
||||
|
||||
|
||||
# Calculate safety factors
|
||||
yield_sf = yield_strength / max_stress if max_stress > 0 else float("inf")
|
||||
ultimate_sf = ultimate_strength / max_stress if max_stress > 0 else float("inf")
|
||||
|
||||
|
||||
# Check temperature limits
|
||||
temp_ok = temperature_c <= max_temp
|
||||
|
||||
|
||||
# Determine if load case passes
|
||||
passed = (
|
||||
yield_sf >= self._required_sf
|
||||
and ultimate_sf >= self._required_sf
|
||||
and temp_ok
|
||||
)
|
||||
|
||||
|
||||
failure_mode = None
|
||||
if yield_sf < self._required_sf:
|
||||
failure_mode = "yield_failure"
|
||||
@@ -371,7 +373,7 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
failure_mode = "ultimate_failure"
|
||||
elif not temp_ok:
|
||||
failure_mode = "thermal_failure"
|
||||
|
||||
|
||||
return LoadCaseResult(
|
||||
load_case_id=lc_id,
|
||||
max_stress_mpa=max_stress,
|
||||
@@ -390,13 +392,13 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
def _select_governing_equations(self, load_cases: list[dict[str, Any]]) -> str:
|
||||
"""Select appropriate governing equations based on load types."""
|
||||
equations = []
|
||||
|
||||
|
||||
# Check load types
|
||||
has_static = any(lc.get("type") == "static" or lc.get("force_n") for lc in load_cases)
|
||||
has_thermal = any(lc.get("temperature_c") for lc in load_cases)
|
||||
has_dynamic = any(lc.get("type") == "dynamic" or lc.get("frequency_hz") for lc in load_cases)
|
||||
has_pressure = any(lc.get("pressure_mpa") for lc in load_cases)
|
||||
|
||||
|
||||
if has_static:
|
||||
equations.append("Linear elasticity (Hooke's Law)")
|
||||
if has_thermal:
|
||||
@@ -405,10 +407,10 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
equations.append("Modal analysis (eigenvalue)")
|
||||
if has_pressure:
|
||||
equations.append("Pressure vessel (hoop stress)")
|
||||
|
||||
|
||||
if not equations:
|
||||
equations.append("Linear elasticity (default)")
|
||||
|
||||
|
||||
return "; ".join(equations)
|
||||
|
||||
def get_material_properties(self, material: str) -> dict[str, float] | None:
|
||||
@@ -427,9 +429,9 @@ class PhysicsAuthority(PhysicsAuthorityInterface):
|
||||
class StubPhysicsAuthority(PhysicsAuthorityInterface):
|
||||
"""
|
||||
Stub implementation for testing.
|
||||
|
||||
|
||||
Returns a minimal proof if design_ref present; else raises PhysicsUnderdefinedError.
|
||||
|
||||
|
||||
Note: This is a stub for testing. Use PhysicsAuthority for real validation.
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user