feat: add conversational RollCalc assistant

This commit is contained in:
2026-08-29 22:37:56 +02:00
parent 3b44048250
commit fa3000b325
25 changed files with 6820 additions and 48 deletions
+125
View File
@@ -0,0 +1,125 @@
"""Canonical core presets shared by the UI and deterministic services."""
from __future__ import annotations
from dataclasses import dataclass
import re
@dataclass(frozen=True)
class CorePreset:
key: str
diameter_mm: float
material: str
label: str
aliases: tuple[str, ...]
@property
def ui_diameter(self) -> str:
return f"{self.diameter_mm:g}"
CORE_PRESETS = (
CorePreset(
key="steel_133",
diameter_mm=133.0,
material="steel",
label="133mm Stahl",
aliases=(
"133 mm Stahl",
"133mm Stahlkern",
"133 mm Stahlkern",
"133mm steel",
"133 mm steel",
"133mm steel core",
"133 mm steel core",
),
),
CorePreset(
key="pvc_150",
diameter_mm=150.0,
material="pvc",
label="150mm PVC",
aliases=(
"150 mm PVC",
"150mm PVC-Kern",
"150 mm PVC-Kern",
"150mm plastic core",
"150 mm plastic core",
),
),
CorePreset(
key="pvc_168",
diameter_mm=168.0,
material="pvc",
label="168mm PVC",
aliases=(
"168 mm PVC",
"168mm PVC-Kern",
"168 mm PVC-Kern",
"168mm plastic core",
"168 mm plastic core",
),
),
CorePreset(
key="steel_194",
diameter_mm=194.0,
material="steel",
label="194mm Stahl",
aliases=(
"194 mm Stahl",
"194mm Stahlkern",
"194 mm Stahlkern",
"194mm steel",
"194 mm steel",
"194mm steel core",
"194 mm steel core",
),
),
)
CORE_FAMILY_ALIASES = {
"stahl": "steel",
"stahlkern": "steel",
"stahl kern": "steel",
"steel": "steel",
"steel core": "steel",
"pvc": "pvc",
"pvc kern": "pvc",
"pvc core": "pvc",
"kunststoff": "pvc",
"kunststoffkern": "pvc",
"kunststoff kern": "pvc",
"plastic": "pvc",
"plastic core": "pvc",
}
def normalize_core_reference(value: str) -> str:
normalized = value.casefold().strip()
normalized = re.sub(r"[-_]", " ", normalized)
return re.sub(r"\s+", " ", normalized)
def core_family(value: str) -> str | None:
return CORE_FAMILY_ALIASES.get(normalize_core_reference(value))
def matching_core_presets(value: str) -> tuple[CorePreset, ...]:
"""Return exact preset aliases or all presets in an explicit family alias."""
normalized = normalize_core_reference(value)
exact = tuple(
preset
for preset in CORE_PRESETS
if normalized
in {
normalize_core_reference(preset.label),
*(normalize_core_reference(alias) for alias in preset.aliases),
}
)
if exact:
return exact
material = CORE_FAMILY_ALIASES.get(normalized)
if material is None:
return ()
return tuple(preset for preset in CORE_PRESETS if preset.material == material)