Python 64.6%
TypeScript 33.7%
CSS 0.8%
1"""Tolerant value coercion for tool arguments (models send '185 000 $', 'Oui', '12 %', …)."""23from __future__ import annotations45import re6from typing import Any78_NUM_RE = re.compile(r"^[+-]?\d+(?:[.,]\d+)?$")9TRUE_WORDS = {"oui", "yes", "vrai", "true", "o", "y", "x", "✓"}10FALSE_WORDS = {"non", "no", "faux", "false", "n", "-", "—", ""}111213def num(value: Any, default: float | None = None) -> float | None:14 """Parse a number from int/float/str (French formats, currency, percent). None if impossible.1516 '185 000 $' → 185000 ; '12,5 %' → 0.125 ; '+3 %' → 0.03 ; '-5 000' → -5000 ; 'Oui' → None.17 """18 if value is None or isinstance(value, bool):19 return default20 if isinstance(value, (int, float)):21 return float(value)22 if not isinstance(value, str):23 return default24 s = value.strip().replace(" ", "").replace(" ", "").replace(" ", "")25 if not s or s.startswith("="):26 return default27 pct = s.endswith("%")28 s = re.sub(r"/?(?:[$€£%]|CAD|USD|pi²|pi2|m²|m2|mois|ans?\b)", "", s, flags=re.I).strip()29 s = s.replace("'", "")30 if s.count(",") == 1 and s.count(".") == 0:31 s = s.replace(",", ".")32 elif s.count(",") >= 1 and s.count(".") == 1:33 s = s.replace(",", "")34 if not _NUM_RE.match(s):35 return default36 v = float(s)37 return v / 100.0 if pct else v383940def pct(value: Any, default: float | None = None) -> float | None:41 """Percent as a ratio. 3 → 0.03 (if |v| > 1), '3 %' → 0.03, 0.03 → 0.03."""42 v = num(value, None)43 if v is None:44 return default45 if isinstance(value, str) and value.strip().endswith("%"):46 return v47 return v / 100.0 if abs(v) > 1 else v484950def boolish(value: Any) -> bool | None:51 if isinstance(value, bool):52 return value53 if isinstance(value, (int, float)):54 return bool(value)55 if isinstance(value, str):56 s = value.strip().lower()57 if s in TRUE_WORDS:58 return True59 if s in FALSE_WORDS:60 return False61 return None626364def cell(value: Any) -> Any:65 """Value safe for openpyxl: numbers parsed, formulas kept, everything else → text."""66 if value is None or isinstance(value, (int, float, bool)):67 return value68 if isinstance(value, str):69 s = value.strip()70 if s.startswith("="):71 return s72 n = num(s, None)73 stripped = re.sub(r"(pi²|pi2|m²|m2|CAD|USD|ans?\b|mois)", "", s, flags=re.I)74 if n is not None and not re.search(r"[A-Za-z]{2,}", stripped):75 return n76 return s77 if isinstance(value, dict):78 return ", ".join(f"{k}: {v}" for k, v in value.items())[:250]79 if isinstance(value, (list, tuple)):80 return ", ".join(str(v) for v in value)[:250]81 return str(value)82