orchestrata_ultimate.orchestrator.parameter_extractor
Reference › orchestrata_ultimate › orchestrator › parameter_extractor
Pulls structured parameters out of the free text of an intent. Its only importer is intent_parser, which accepts a ParameterExtractor through its constructor. It is traced to spec 003 story US-001.
The single class has one method per kind of parameter: extract_file_params (file paths, directories and glob patterns), extract_numeric_params (numbers with optional units), extract_constraint_params (clauses such as must or only, returned as key and value dictionaries), extract_list_params (comma or "and" separated lists) and extract_boolean_params (flags). extract_all runs them all and merges the results into one dictionary. resolve_ambiguity takes the text and a list of candidate interpretations and scores each one, returning the result as a dictionary. A separate module, ambiguity_resolver, chooses between whole intent readings. The module reads nothing from the environment and touches no storage.
At a glance
| Source | src/orchestrata_ultimate/orchestrator/parameter_extractor.py |
| Lines of code | 286 |
| Classes | 1 |
| Functions | 0 |
| Imports (internal) | orchestrata_ultimate.core.base.traces |
| Imported by | orchestrata_ultimate.orchestrator.intent_parser |
| Tests that import it | tests/generated_orchestrator/test_parameter_extractor_generated.py, tests/generated_recovery/test_orchestrator_parameter_extractor.py, tests/orchestrator/test_intent_parser_behavioral.py, tests/orchestrator/test_parameter_extractor.py, tests/test_orchestrator.py |
Class diagram
Dependency diagram
API
Classes
ParameterExtractor
Decorators: traces(spec='003', story='US-001', reqs=['REQ-003-US-001-005', 'REQ-003-US-001-006', 'REQ-003-US-001-015', 'REQ-003-US-… · Line 27
Extracts structured parameters from natural language intent text.
| Method | Signature | Summary |
|---|---|---|
extract_file_params |
extract_file_params(text: str) -> dict[str, Any] |
Extract file paths, directories, and glob patterns. |
extract_numeric_params |
extract_numeric_params(text: str) -> dict[str, Any] |
Extract numeric values with optional units. |
extract_constraint_params |
extract_constraint_params(text: str) -> list[dict[str, str]] |
Extract constraint clauses (must, within, before, at most, etc.). |
extract_list_params |
extract_list_params(text: str) -> dict[str, list[str]] |
Extract comma/and-separated lists. |
extract_boolean_params |
extract_boolean_params(text: str) -> dict[str, bool] |
Extract boolean flags from text. |
extract_all |
extract_all(text: str) -> dict[str, Any] |
Extract all parameter types from text. |
resolve_ambiguity |
resolve_ambiguity(text: str, candidates: list[str]) -> dict[str, Any] |
Score multiple interpretation candidates and pick best. |
Source
# mypy: ignore-errors
"""Advanced parameter extraction from natural language.
Extracts file paths, numeric values, constraints, lists,
and resolves ambiguous parameters from intent text.
"""
import logging
import re
from typing import Any
from orchestrata_ultimate.core.base.traces import traces
logger = logging.getLogger(__name__)
@traces(
spec="003",
story="US-001",
reqs=[
"REQ-003-US-001-005",
"REQ-003-US-001-006",
"REQ-003-US-001-015",
"REQ-003-US-001-016",
],
)
class ParameterExtractor:
"""Extracts structured parameters from natural language intent text."""
# File path patterns
_FILE_PATH_RE = re.compile(
r"(?:^|\s)(/(?:[a-zA-Z0-9_.\-]+/)*[a-zA-Z0-9_.\-]+(?:\.[a-zA-Z0-9]+)?)"
)
_GLOB_PATTERN_RE = re.compile(
r"(?:^|\s)(\*\*?/[^\s]+|[^\s]*\*[^\s]*\.[a-zA-Z0-9]+)"
)
_RELATIVE_PATH_RE = re.compile(r"(?:^|\s)(\.{1,2}/[^\s]+)")
# Numeric patterns with units
_NUMERIC_RE = re.compile(
r"(\d+(?:\.\d+)?)\s*(bytes?|[kKmMgGtT][bB]|ms|seconds?|secs?|minutes?|mins?|hours?|hrs?|days?|%|count)?",
re.IGNORECASE,
)
# Constraint patterns
_CONSTRAINT_PATTERNS = [
(re.compile(r"must\s+(?:be\s+)?(.+?)(?:\.|$)", re.IGNORECASE), "must"),
(re.compile(r"within\s+(\d+\s*\w+)", re.IGNORECASE), "within"),
(re.compile(r"before\s+(.+?)(?:\.|$)", re.IGNORECASE), "before"),
(re.compile(r"at\s+most\s+(\d+\s*\w+)", re.IGNORECASE), "at_most"),
(re.compile(r"at\s+least\s+(\d+\s*\w+)", re.IGNORECASE), "at_least"),
(re.compile(r"no\s+more\s+than\s+(\d+\s*\w+)", re.IGNORECASE), "no_more_than"),
(re.compile(r"timeout\s+(?:of\s+)?(\d+\s*\w+)", re.IGNORECASE), "timeout"),
]
# Boolean indicators
_ENABLE_WORDS = frozenset({"enable", "on", "yes", "true", "activate", "start"})
_DISABLE_WORDS = frozenset({"disable", "off", "no", "false", "deactivate", "stop"})
# List separators
_LIST_SEP_RE = re.compile(r",\s*(?:and\s+)?|\s+and\s+")
# Unit multipliers (to bytes)
_UNIT_BYTES = {
"b": 1,
"byte": 1,
"bytes": 1,
"kb": 1024,
"mb": 1024**2,
"gb": 1024**3,
"tb": 1024**4,
}
# Unit multipliers (to seconds)
_UNIT_SECONDS = {
"ms": 0.001,
"s": 1,
"sec": 1,
"secs": 1,
"second": 1,
"seconds": 1,
"m": 60,
"min": 60,
"mins": 60,
"minute": 60,
"minutes": 60,
"h": 3600,
"hr": 3600,
"hrs": 3600,
"hour": 3600,
"hours": 3600,
"d": 86400,
"day": 86400,
"days": 86400,
}
@traces(spec="003", story="US-001", reqs=["REQ-003-US-001-005"])
def extract_file_params(self, text: str) -> dict[str, Any]:
"""Extract file paths, directories, and glob patterns."""
result: dict[str, Any] = {}
# Absolute paths
paths = self._FILE_PATH_RE.findall(text)
if paths:
result["paths"] = paths
# Glob patterns
globs = self._GLOB_PATTERN_RE.findall(text)
if globs:
result["glob_patterns"] = [g.strip() for g in globs]
# Relative paths
relatives = self._RELATIVE_PATH_RE.findall(text)
if relatives:
result["relative_paths"] = [r.strip() for r in relatives]
return result
@traces(spec="003", story="US-001", reqs=["REQ-003-US-001-005"])
def extract_numeric_params(self, text: str) -> dict[str, Any]:
"""Extract numeric values with optional units."""
result: dict[str, Any] = {}
values = []
for match in self._NUMERIC_RE.finditer(text):
raw_value = float(match.group(1))
unit = (match.group(2) or "").lower().rstrip("s")
entry: dict[str, Any] = {"raw": raw_value, "unit": match.group(2) or ""}
# Normalize to base units
if unit in self._UNIT_BYTES or unit + "s" in self._UNIT_BYTES:
key = unit if unit in self._UNIT_BYTES else unit + "s"
entry["bytes"] = int(raw_value * self._UNIT_BYTES.get(key, 1))
entry["type"] = "size"
elif unit in self._UNIT_SECONDS or unit + "s" in self._UNIT_SECONDS:
key = unit if unit in self._UNIT_SECONDS else unit + "s"
entry["seconds"] = raw_value * self._UNIT_SECONDS.get(key, 1)
entry["type"] = "duration"
elif unit == "%":
entry["type"] = "percentage"
elif unit == "count" or unit == "":
entry["type"] = "count"
values.append(entry)
if values:
result["numeric_values"] = values
return result
@traces(
spec="003",
story="US-001",
reqs=["REQ-003-US-001-006", "REQ-003-US-001-016"],
)
def extract_constraint_params(self, text: str) -> list[dict[str, str]]:
"""Extract constraint clauses (must, within, before, at most, etc.)."""
constraints = [
{
"type": constraint_type,
"value": match.group(1).strip(),
}
for pattern, constraint_type in self._CONSTRAINT_PATTERNS
for match in pattern.finditer(text)
]
return constraints
@traces(spec="003", story="US-001", reqs=["REQ-003-US-001-005"])
def extract_list_params(self, text: str) -> dict[str, list[str]]:
"""Extract comma/and-separated lists."""
result: dict[str, list[str]] = {}
# Look for "install X, Y, and Z" or similar patterns
list_patterns = [
re.compile(
r"(?:install|add|include|import|use)\s+(.+?)(?:\s*$|\s*\.)",
re.IGNORECASE,
),
re.compile(
r"(?:files?|packages?|modules?|items?):\s*(.+?)(?:\s*$|\s*\.)",
re.IGNORECASE,
),
]
for pattern in list_patterns:
match = pattern.search(text)
if match:
items_text = match.group(1)
items = self._LIST_SEP_RE.split(items_text)
items = [item.strip() for item in items if item.strip()]
if len(items) > 1:
result["items"] = items
return result
@traces(spec="003", story="US-001", reqs=["REQ-003-US-001-005"])
def extract_boolean_params(self, text: str) -> dict[str, bool]:
"""Extract boolean flags from text."""
result: dict[str, bool] = {}
words = text.lower().split()
for i, word in enumerate(words):
if word in self._ENABLE_WORDS:
# Look for the thing being enabled
if i + 1 < len(words):
key = words[i + 1]
result[key] = True
elif word in self._DISABLE_WORDS:
if i + 1 < len(words):
key = words[i + 1]
result[key] = False
return result
@traces(
spec="003",
story="US-001",
reqs=["REQ-003-US-001-005", "REQ-003-US-001-015"],
)
def extract_all(self, text: str) -> dict[str, Any]:
"""Extract all parameter types from text."""
if not text:
return {}
result: dict[str, Any] = {}
file_params = self.extract_file_params(text)
if file_params:
result["files"] = file_params
numeric_params = self.extract_numeric_params(text)
if numeric_params:
result["numeric"] = numeric_params
constraints = self.extract_constraint_params(text)
if constraints:
result["constraints"] = constraints
list_params = self.extract_list_params(text)
if list_params:
result["lists"] = list_params
boolean_params = self.extract_boolean_params(text)
if boolean_params:
result["booleans"] = boolean_params
return result
@traces(spec="003", story="US-001", reqs=["REQ-003-US-001-017"])
def resolve_ambiguity(self, text: str, candidates: list[str]) -> dict[str, Any]:
"""Score multiple interpretation candidates and pick best."""
if not candidates:
return {"best": None, "scores": {}}
scores: dict[str, float] = {}
text_lower = text.lower()
for candidate in candidates:
score = 0.0
candidate_lower = candidate.lower()
# Exact match bonus
if candidate_lower in text_lower:
score += 1.0
# Word overlap score
text_words = set(text_lower.split())
candidate_words = set(candidate_lower.split())
overlap = text_words & candidate_words
if text_words:
score += len(overlap) / len(text_words) * 0.5
# Length similarity
len_ratio = min(len(candidate), len(text)) / max(
len(candidate), len(text), 1
)
score += len_ratio * 0.3
scores[candidate] = round(score, 4)
# `scores[key]` (not `scores.get`, which types as `-> float | None`
# and isn't a valid `SupportsRichComparison` key function): every
# candidate scored in the loop above is a key in `scores`.
best = max(scores, key=lambda k: scores[k])
return {"best": best, "scores": scores, "confidence": scores[best]}