Files
FusionAGI/fusionagi/maa/layers/intent_engine.py
Devin AI 445865e429
Some checks failed
Tests / test (3.10) (pull_request) Failing after 40s
Tests / test (3.11) (pull_request) Failing after 39s
Tests / test (3.12) (pull_request) Successful in 49s
Tests / lint (pull_request) Successful in 35s
Tests / docker (pull_request) Successful in 2m27s
fix: deep GPU integration, fix all ruff/mypy issues, add .dockerignore
- 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>
2026-04-28 05:48:37 +00:00

437 lines
15 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Layer 1 — Intent Formalization Engine.
Responsible for:
1. Intent decomposition - breaking natural language into structured requirements
2. Requirement typing - classifying requirements (dimensional, load, environmental, process)
3. Load case enumeration - identifying operational scenarios
"""
import re
import uuid
from typing import Any
from fusionagi._logger import logger
from fusionagi.maa.schemas.intent import (
EngineeringIntentGraph,
IntentNode,
LoadCase,
RequirementType,
)
class IntentIncompleteError(Exception):
"""Raised when intent formalization cannot be completed due to missing information."""
def __init__(self, message: str, missing_fields: list[str] | None = None):
self.missing_fields = missing_fields or []
super().__init__(message)
class IntentEngine:
"""
Intent decomposition, requirement typing, and load case enumeration.
Features:
- Pattern-based requirement extraction from natural language
- Automatic requirement type classification
- Load case identification
- Environmental bounds extraction
- LLM-assisted formalization (optional)
"""
# Patterns for dimensional requirements (measurements, tolerances)
DIMENSIONAL_PATTERNS = [
r"(\d+(?:\.\d+)?)\s*(mm|cm|m|in|inch|inches|ft|feet)\b",
r"tolerance[s]?\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"±\s*(\d+(?:\.\d+)?)",
r"(\d+(?:\.\d+)?)\s*×\s*(\d+(?:\.\d+)?)",
r"diameter\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"radius\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"thickness\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"length\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"width\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"height\s*(?:of\s*)?(\d+(?:\.\d+)?)",
]
# Patterns for load requirements (forces, pressures, stresses)
LOAD_PATTERNS = [
r"(\d+(?:\.\d+)?)\s*(N|kN|MN|lb|lbf|kg|kgf)\b",
r"(\d+(?:\.\d+)?)\s*(MPa|GPa|Pa|psi|ksi)\b",
r"load\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"force\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"stress\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"pressure\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"factor\s*of\s*safety\s*(?:of\s*)?(\d+(?:\.\d+)?)",
r"yield\s*strength",
r"tensile\s*strength",
r"fatigue\s*(?:life|limit|strength)",
]
# Patterns for environmental requirements
ENVIRONMENTAL_PATTERNS = [
r"(\d+(?:\.\d+)?)\s*(?:°|deg|degrees?)?\s*(C|F|K|Celsius|Fahrenheit|Kelvin)\b",
r"temperature\s*(?:range|of)?\s*(\d+)",
r"humidity\s*(?:of\s*)?(\d+)",
r"corrosion\s*resist",
r"UV\s*resist",
r"water\s*(?:proof|resist)",
r"chemical\s*resist",
r"outdoor",
r"marine",
r"aerospace",
]
# Patterns for process requirements
PROCESS_PATTERNS = [
r"CNC|machining|milling|turning|drilling",
r"3D\s*print|additive|FDM|SLA|SLS|DMLS",
r"cast|injection\s*mold|die\s*cast",
r"weld|braze|solder",
r"heat\s*treat|anneal|harden|temper",
r"surface\s*finish|polish|anodize|plate",
r"assembly|sub-assembly",
r"material:\s*(\w+)",
r"aluminum|steel|titanium|plastic|composite",
]
# Load case indicator patterns
LOAD_CASE_PATTERNS = [
r"(?:during|under|in)\s+(\w+(?:\s+\w+)?)\s+(?:conditions?|operation|mode)",
r"(\w+)\s+load\s+case",
r"(?:static|dynamic|cyclic|impact|thermal)\s+load",
r"(?:normal|extreme|emergency|failure)\s+(?:operation|conditions?|mode)",
r"operating\s+(?:at|under|in)",
]
def __init__(self, llm_adapter: Any | None = None):
"""
Initialize the IntentEngine.
Args:
llm_adapter: Optional LLM adapter for enhanced natural language processing.
"""
self._llm = llm_adapter
def formalize(
self,
intent_id: str,
natural_language: str | None = None,
file_refs: list[str] | None = None,
metadata: dict[str, Any] | None = None,
use_llm: bool = True,
) -> 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
if use_llm and self._llm:
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:
nodes.append(
IntentNode(
node_id=f"{intent_id}_file_{uuid.uuid4().hex[:8]}",
requirement_type=RequirementType.OTHER,
description=f"Reference: {ref}",
metadata={"file_ref": ref},
)
)
# If no nodes were extracted, create a general requirement
if not nodes and natural_language:
nodes.append(
IntentNode(
node_id=f"{intent_id}_general_0",
requirement_type=RequirementType.OTHER,
description=natural_language[:500],
)
)
logger.info(
"Intent formalized",
extra={
"intent_id": intent_id,
"num_nodes": len(nodes),
"num_load_cases": len(load_cases),
},
)
return EngineeringIntentGraph(
intent_id=intent_id,
nodes=nodes,
load_cases=load_cases,
environmental_bounds=environmental_bounds,
metadata=metadata or {},
)
def _extract_requirements(
self,
intent_id: str,
text: str,
) -> 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):
nodes.append(
IntentNode(
node_id=f"{intent_id}_dim_{node_counter}",
requirement_type=RequirementType.DIMENSIONAL,
description=sentence,
metadata={"pattern": "dimensional"},
)
)
node_counter += 1
break
# Check for load requirements
for pattern in self.LOAD_PATTERNS:
if re.search(pattern, sentence, re.IGNORECASE):
nodes.append(
IntentNode(
node_id=f"{intent_id}_load_{node_counter}",
requirement_type=RequirementType.LOAD,
description=sentence,
metadata={"pattern": "load"},
)
)
node_counter += 1
break
# Check for environmental requirements
for pattern in self.ENVIRONMENTAL_PATTERNS:
match = re.search(pattern, sentence, re.IGNORECASE)
if match:
nodes.append(
IntentNode(
node_id=f"{intent_id}_env_{node_counter}",
requirement_type=RequirementType.ENVIRONMENTAL,
description=sentence,
metadata={"pattern": "environmental"},
)
)
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):
nodes.append(
IntentNode(
node_id=f"{intent_id}_proc_{node_counter}",
requirement_type=RequirementType.PROCESS,
description=sentence,
metadata={"pattern": "process"},
)
)
node_counter += 1
break
# Check for load cases
for pattern in self.LOAD_CASE_PATTERNS:
match = re.search(pattern, sentence, re.IGNORECASE)
if match:
load_case_desc = match.group(0) if match.group(0) else sentence
load_cases.append(
LoadCase(
load_case_id=f"{intent_id}_lc_{load_case_counter}",
description=load_case_desc,
metadata={"source_sentence": sentence},
)
)
load_case_counter += 1
break
return {
"nodes": nodes,
"load_cases": load_cases,
"environmental_bounds": environmental_bounds,
}
def _formalize_with_llm(
self,
intent_id: str,
natural_language: str,
) -> 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:
- "requirement_type": one of "dimensional", "load", "environmental", "process", "other"
- "description": the requirement text
- "load_cases": list of operational scenarios, each with:
- "description": the scenario description
- "environmental_bounds": dict of environmental limits (e.g., {{"temperature_max": 85, "humidity_max": 95}})
Text: {natural_language[:2000]}
Return only valid JSON, no markdown."""
try:
messages = [
{"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:
# Clean up response
if raw.startswith("```"):
raw = raw.split("```")[1]
if raw.startswith("json"):
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(
self,
intent_id: str,
result: dict[str, Any],
) -> EngineeringIntentGraph:
"""Parse LLM result into EngineeringIntentGraph."""
nodes = []
for i, node_data in enumerate(result.get("nodes", [])):
req_type_str = node_data.get("requirement_type", "other")
try:
req_type = RequirementType(req_type_str)
except ValueError:
req_type = RequirementType.OTHER
nodes.append(
IntentNode(
node_id=f"{intent_id}_llm_{i}",
requirement_type=req_type,
description=node_data.get("description", ""),
metadata={"source": "llm"},
)
)
load_cases = []
for i, lc_data in enumerate(result.get("load_cases", [])):
load_cases.append(
LoadCase(
load_case_id=f"{intent_id}_lc_llm_{i}",
description=lc_data.get("description", ""),
metadata={"source": "llm"},
)
)
environmental_bounds = result.get("environmental_bounds", {})
return EngineeringIntentGraph(
intent_id=intent_id,
nodes=nodes,
load_cases=load_cases,
environmental_bounds=environmental_bounds,
metadata={"formalization_source": "llm"},
)
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)
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