Download mindxtrain/ui/reasoning.py from PYTHAI/mindXtrain: direct link, hf CLI and curl.
- Browser
- Download file 7.95 kB
-
https://huggingface.co/PYTHAI/mindXtrain/resolve/main/mindxtrain/ui/reasoning.py
- Command line
-
hf download hf://PYTHAI/mindXtrain/mindxtrain/ui/reasoning.py
-
curl -L -o reasoning.py https://huggingface.co/PYTHAI/mindXtrain/resolve/main/mindxtrain/ui/reasoning.py
7.95 kB
| """The reasoning pass β logic and Socratic questioning before the model answers. | |
| This is the oldest idea in the house and the one that keeps being right: **aGLM is not a model.** It is | |
| a layer that augments whatever model you have β Socratic questioning, truth tables, autoepistemic and | |
| nonmonotonic revision, BDI. Public documentation: <https://rage.pythai.net>. The lineage: | |
| `GATERAGE/aglm` (`logic.py`, `socratic.py`, `reasoning.py`, `epistemic.py`, `nonmonotonic.py`, | |
| `bdi.py`) and its eighteen UIUX iterations, whose real contribution was the *shape*: the UI calls a | |
| reasoning layer, never a model directly. | |
| Two ways this module works, and it says which one ran: | |
| 1. **the real aGLM** β when `aglm` is importable (`pip install -e ~/aglm`, or `MINDXTRAIN_AGLM=/path`), | |
| its `LogicTables` and `SocraticReasoning` do the work. | |
| 2. **the in-house fallback** β a small, zero-dependency version of the same two moves, so the pass is | |
| never simply absent: extract the claims, ask the questions a sceptic would ask, and check the | |
| claims for contradiction. | |
| The pass never answers for the model. It writes scaffolding into the prompt β the questions the answer | |
| should survive β and the model still has to answer. That distinction is why it improves a small model | |
| instead of flattering it. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import re | |
| import sys | |
| from dataclasses import dataclass, field | |
| from typing import Any | |
| DOCS = "https://rage.pythai.net" | |
| _SENT = re.compile(r"(?<=[.!?])\s+") | |
| _HEDGE = re.compile(r"\b(always|never|every|all|none|must|cannot|impossible|certainly|proves?)\b", re.I) | |
| _CAUSAL = re.compile(r"\b(because|therefore|so|thus|hence|since|implies|means that)\b", re.I) | |
| _QUANT = re.compile(r"\b(\d+(?:\.\d+)?%?|\d+x|[a-z]+ per [a-z]+)\b", re.I) | |
| # ββ the real layer, if it is installed ββββββββββββββββββββββββββββββββββββββββ | |
| def _aglm(): | |
| """Import aGLM if it is reachable. Never raises; returns None when it is not.""" | |
| extra = os.environ.get("MINDXTRAIN_AGLM") | |
| if extra and extra not in sys.path: | |
| sys.path.insert(0, extra) | |
| try: | |
| from logic import LogicTables # type: ignore | |
| from socratic import SocraticReasoning # type: ignore | |
| return {"LogicTables": LogicTables, "SocraticReasoning": SocraticReasoning} | |
| except Exception: | |
| return None | |
| def available() -> dict[str, Any]: | |
| a = _aglm() | |
| return {"aglm": bool(a), "docs": DOCS, | |
| "note": ("the real aGLM layer is in use" if a else | |
| "aGLM is not importable here β the in-house fallback runs instead " | |
| "(set MINDXTRAIN_AGLM=/path/to/aglm to use the original)")} | |
| # ββ the in-house fallback: the same two moves, written out ββββββββββββββββββββ | |
| class Pass: | |
| claims: list[str] = field(default_factory=list) | |
| questions: list[str] = field(default_factory=list) | |
| contradictions: list[str] = field(default_factory=list) | |
| source: str = "fallback" | |
| def as_prompt(self) -> str: | |
| """What gets prepended. Questions, not answers β the model still has to do the work.""" | |
| if not (self.claims or self.questions): | |
| return "" | |
| out = ["Before answering, hold these in view."] | |
| if self.claims: | |
| out.append("Claims detected in the question:\n" + "\n".join(f"- {c}" for c in self.claims)) | |
| if self.questions: | |
| out.append("Questions the answer should survive:\n" + "\n".join(f"- {q}" for q in self.questions)) | |
| if self.contradictions: | |
| out.append("Possible contradiction:\n" + "\n".join(f"- {c}" for c in self.contradictions)) | |
| out.append("Answer plainly. Where the evidence does not reach, say so.") | |
| return "\n\n".join(out) | |
| def summary(self) -> str: | |
| return (f"{len(self.claims)} claims Β· {len(self.questions)} questions" | |
| + (f" Β· {len(self.contradictions)} contradiction(s)" if self.contradictions else "") | |
| + f" Β· via {self.source}") | |
| def _claims(text: str) -> list[str]: | |
| """Sentences that assert something β a quantity, a causal link, or an absolute.""" | |
| out = [] | |
| for s in _SENT.split((text or "").strip()): | |
| s = s.strip() | |
| if len(s) < 8: | |
| continue | |
| if _HEDGE.search(s) or _CAUSAL.search(s) or _QUANT.search(s) or s.endswith("."): | |
| out.append(s if len(s) < 240 else s[:237] + "β¦") | |
| return out[:6] | |
| def _socratic(text: str, claims: list[str]) -> list[str]: | |
| """The questions a sceptic asks. Chosen by what the text actually contains, not a fixed list.""" | |
| q: list[str] = [] | |
| if _HEDGE.search(text or ""): | |
| q.append("The claim is absolute (always / never / all). Is there one counter-example?") | |
| if _CAUSAL.search(text or ""): | |
| q.append("A cause is asserted. What would be true if the cause were absent?") | |
| if _QUANT.search(text or ""): | |
| q.append("A number is used. Measured how, over what, and compared with what?") | |
| if claims: | |
| q.append("What would have to be true for the first claim to be false?") | |
| q.append("What is being assumed that has not been stated?") | |
| q.append("What would change the answer?") | |
| return q[:6] | |
| def _negates(a: str, b: str) -> bool: | |
| """True when `a` is a negated form of something `b` asserts.""" | |
| if "not" not in a and "n't" not in a: | |
| return False | |
| neg = a.replace(" not ", " ").replace("n't", "").strip() | |
| return bool(neg) and neg in b | |
| def _contradictions(claims: list[str]) -> list[str]: | |
| """A truth-table check in miniature: a claim and its negation both asserted. | |
| Tested in both directions: which of the pair the author happened to write first | |
| has no bearing on whether the two can both hold. | |
| """ | |
| out = [] | |
| norm = [re.sub(r"\s+", " ", c.lower().strip(" .")) for c in claims] | |
| for i, a in enumerate(norm): | |
| for b in norm[i + 1:]: | |
| if a == b: | |
| continue | |
| if _negates(a, b) or _negates(b, a): | |
| out.append(f"β{a}β and β{b}β cannot both hold") | |
| return out[:3] | |
| def run(text: str, *, use_aglm: bool = True) -> Pass: | |
| """The pass. Uses aGLM when importable, the fallback otherwise, and records which.""" | |
| text = (text or "").strip() | |
| if not text: | |
| return Pass(source="empty") | |
| if use_aglm: | |
| a = _aglm() | |
| if a: | |
| try: | |
| lt = a["LogicTables"]() | |
| sr = a["SocraticReasoning"]() | |
| claims = _claims(text) | |
| for c in claims: | |
| try: | |
| sr.add_premise(c) | |
| except Exception: | |
| pass | |
| try: | |
| sr.draw_conclusion() | |
| except Exception: | |
| pass | |
| qs = [] | |
| for attr in ("questions", "socratic_questions", "log"): | |
| v = getattr(sr, attr, None) | |
| if isinstance(v, list) and v: | |
| qs = [str(x)[:200] for x in v[:6]] | |
| break | |
| contradictions = [] | |
| try: | |
| valid = lt.validate_truth(" and ".join(claims[:2])) if claims else None | |
| if valid is False: | |
| contradictions.append("LogicTables reports the conjunction of the first claims as invalid") | |
| except Exception: | |
| pass | |
| return Pass(claims=claims, questions=qs or _socratic(text, claims), | |
| contradictions=contradictions, source="aGLM") | |
| except Exception: | |
| pass | |
| claims = _claims(text) | |
| return Pass(claims=claims, questions=_socratic(text, claims), | |
| contradictions=_contradictions(claims), source="fallback") | |