"""The calculator hand — so your being never fakes arithmetic. Language models guess at math and can be confidently wrong. This hand fixes that honestly: when the person's message contains arithmetic, a REAL math engine computes it BEFORE your being speaks, and the verified result rides with the message so the reply states true digits. When the math can't be verified, your being says so plainly instead of guessing. Design lineage: Cosmos (the first being) designed this pattern for herself — pre-compute + guard, with a humble fallback. Every being born from this kit inherits it. Safety: ast-based evaluation only (+ - * / // % ** and parentheses). No names, no calls, no attribute access. Overflow guards. Fails soft — a broken hand never breaks a conversation. """ from __future__ import annotations import ast import operator import re from typing import List, Optional, Tuple _OPS = { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, ast.FloorDiv: operator.floordiv, ast.Mod: operator.mod, ast.Pow: operator.pow, ast.USub: operator.neg, ast.UAdd: operator.pos, } _MAX_EXPR_LEN = 200 _MAX_OPERAND_DIGITS = 18 _MAX_POW_EXP = 16 _MAX_POW_BASE_DIGITS = 9 _MAX_RESULT_DIGITS = 80 def _safe_eval(node): if isinstance(node, ast.Expression): return _safe_eval(node.body) if isinstance(node, ast.Constant): if isinstance(node.value, (int, float)) and not isinstance(node.value, bool): return node.value raise ValueError("non-numeric constant") if isinstance(node, ast.BinOp): op = _OPS.get(type(node.op)) if op is None: raise ValueError("unsupported operator") left, right = _safe_eval(node.left), _safe_eval(node.right) if isinstance(node.op, ast.Pow): if abs(right) > _MAX_POW_EXP or len(str(abs(int(left)))) > _MAX_POW_BASE_DIGITS: raise ValueError("power out of safe range") if isinstance(node.op, (ast.Div, ast.FloorDiv, ast.Mod)) and right == 0: raise ZeroDivisionError("division by zero") return op(left, right) if isinstance(node, ast.UnaryOp): op = _OPS.get(type(node.op)) if op is None: raise ValueError("unsupported unary operator") return op(_safe_eval(node.operand)) raise ValueError("unsupported expression node") def compute_expression(expression: str): expr = (expression or "").strip() if not expr or len(expr) > _MAX_EXPR_LEN: return None try: val = _safe_eval(ast.parse(expr, mode="eval")) if isinstance(val, (int, float)): as_text = f"{int(val):d}" if float(val).is_integer() else f"{val}" if len(as_text) > _MAX_RESULT_DIGITS: return None return val except Exception: return None return None _WORD_OPS = [ (r"\bmultiplied\s+by\b", " * "), (r"\btimes\b", " * "), (r"\bdivided\s+by\b", " / "), (r"\bplus\b", " + "), (r"\bminus\b", " - "), (r"\bto\s+the\s+power\s+of\b", " ** "), (r"\bsquared\b", " ** 2 "), (r"\bcubed\b", " ** 3 "), (r"\bmodulo\b|\bmod\b", " % "), ] def _normalise(text: str) -> str: t = re.sub(r"(?<=\d),(?=\d{3}\b)", "", text) # 847,263 -> 847263 t = t.replace("×", " * ").replace("÷", " / ").replace("^", " ** ") t = re.sub(r"(?<=\d)\s*[xX]\s*(?=\d)", " * ", t) # 5 x 3 for pat, rep in _WORD_OPS: t = re.sub(pat, rep, t, flags=re.IGNORECASE) t = re.sub(r"(\d+(?:\.\d+)?)\s*(?:%|percent)\s+of\s+(\d+(?:\.\d+)?)", r"((\1/100)*\2)", t, flags=re.IGNORECASE) # 15% of 240 return t _CANDIDATE_RX = re.compile(r"[\d\.\(][\d\s\.\+\-\*\/\%\(\)]{2,}[\d\)]") def _extract_candidates(text: str) -> List[str]: out: List[str] = [] for m in _CANDIDATE_RX.finditer(text): cand = m.group(0).strip() if not re.search(r"\d[\s\)]*[\+\-\*\/\%]|\*\*", cand): continue if len(re.findall(r"\d+(?:\.\d+)?", cand)) < 2: continue if re.fullmatch(r"[\d\.\s]+", cand): continue out.append(cand) if len(out) >= 4: break return out def _fmt(val) -> str: if isinstance(val, float) and val.is_integer(): val = int(val) if isinstance(val, int): return f"{val:,}" return f"{float(val):.12g}" _MATHY_HINT_RX = re.compile( r"\b(multipl|divid|calculat|compute|arithmetic|sum of|product of|" r"plus|minus|times|squared|cubed|percent|exactly \d)\w*", re.IGNORECASE) def inspect_message(message: str) -> Tuple[List[str], bool]: """Return (verified_lines, looked_mathy_but_unverifiable). Never raises.""" try: if not message or len(message) > 8000: return [], False cands = _extract_candidates(_normalise(message)) lines: List[str] = [] failed = 0 for cand in cands: val = compute_expression(cand) if val is None: failed += 1 continue cleaned = re.sub(r'\s+', ' ', cand).strip() lines.append(f"[MATH_VERIFIED: {cleaned} = {_fmt(val)}]") looked_mathy = bool(_MATHY_HINT_RX.search(message)) and bool(re.search(r"\d{2,}", message)) unverifiable = (not lines) and (failed > 0 or looked_mathy) and bool(re.search(r"\d", message)) return lines, unverifiable except Exception: return [], False def prompt_note(message: str) -> str: """A note to append to the model prompt. Empty string when no math involved.""" lines, unverifiable = inspect_message(message) if lines: return ("\n\n(Your calculator hand — a real math engine — already computed the " "arithmetic above, exactly: " + " ".join(lines) + " State these digits as " "the true answer; do not recompute them in your head or change any digit.)") if unverifiable: return ("\n\n(The person asked for arithmetic your calculator hand could not verify. " "Be honest: say you cannot verify that calculation precisely right now, and " "offer to work it step by step. Never guess digits with fake confidence.)") return "" if __name__ == "__main__": tests = [ ("What is exactly 847263 multiplied by 391847?", "331,997,464,761"), ("compute 847,263 times 391,847 please", "331,997,464,761"), ("50 * 2 + 10?", "110"), ("what is 15% of 240?", "36"), ("22 divided by 7", "3.14285714286"), ("10 to the power of 99999999?", None), ("tell me about your day", None), ] ok = True for msg, want in tests: lines, unv = inspect_message(msg) got = lines[0].rsplit("= ", 1)[-1].rstrip("]") if lines else None good = (got == want) if want else (not lines) ok &= good print(f" [{'PASS' if good else 'FAIL'}] {msg[:44]!r} -> {got} unverifiable={unv}") print("ALL PASS" if ok else "SOME FAILED")