| Server IP : 157.230.181.24 / Your IP : 216.73.217.11 Web Server : Apache/2.4.58 (Ubuntu) System : Linux conductive 6.8.0-117-generic #117-Ubuntu SMP PREEMPT_DYNAMIC Tue May 5 19:26:24 UTC 2026 x86_64 User : ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /opt/aim-bots/ |
Upload File : |
"""Fuzzy retrieval over archived AIM user→assistant pairs."""
from __future__ import annotations
import gzip
import json
import re
from difflib import SequenceMatcher
from pathlib import Path
from coherence import is_followup
def normalize(text: str) -> str:
text = re.sub(r"\s+", " ", text.lower().strip())
return re.sub(r"[^\w\s!?=.\-]", "", text)
class ReplyRetriever:
"""Map incoming lines to archived replies when the match is close enough."""
def __init__(self, pairs_path: Path, min_ratio: float = 0.72) -> None:
self.min_ratio = min_ratio
if not pairs_path.exists():
self.pairs: list[dict] = []
self.index: list[tuple[str, dict]] = []
return
with gzip.open(pairs_path, "rt", encoding="utf-8") as fh:
self.pairs = json.load(fh)
self.index = [(normalize(p["user"]), p) for p in self.pairs if p.get("user") and p.get("assistant")]
def match(self, message: str, limit: int = 5) -> list[tuple[float, dict]]:
q = normalize(message)
if not q or not self.index:
return []
scored: list[tuple[float, dict]] = []
qlen = len(q)
for norm, pair in self.index:
if abs(len(norm) - qlen) > max(24, qlen * 2):
continue
ratio = SequenceMatcher(None, q, norm).ratio()
if ratio >= self.min_ratio:
scored.append((ratio, pair))
scored.sort(key=lambda x: -x[0])
return scored[:limit]
def _context_boost(self, history: list[dict] | None, pair: dict) -> float:
if not history or len(history) < 2:
return 0.0
prev_user = None
for msg in reversed(history[:-1]):
if msg.get("role") == "user":
prev_user = normalize(msg["content"].split("\n")[-1])
break
if not prev_user:
return 0.0
# archived pair user line often follows similar prior banter
return SequenceMatcher(None, prev_user, normalize(pair.get("user", ""))).ratio() * 0.08
def best_reply(
self,
message: str,
history: list[dict] | None = None,
relaxed: bool = False,
) -> str | None:
# Short follow-ups (y?, why?, huh?) need full thread context — archive fuzzy match misfires.
if is_followup(message):
return None
hits = self.match(message, 8)
if not hits:
return None
scored = []
for ratio, pair in hits:
boost = self._context_boost(history, pair)
scored.append((ratio + boost, ratio, pair))
scored.sort(key=lambda x: -x[0])
best_total, best_ratio, best = scored[0]
short = len(message.split()) <= 5
threshold = self.min_ratio if relaxed else 0.78
if best_ratio >= 0.86:
return best["assistant"]
if short and best_ratio >= threshold:
# Extra strict for tiny messages that aren't follow-ups (sup, hey, yo).
if len(message.split()) <= 2 and best_ratio < 0.88:
return None
return best["assistant"]
if best_ratio >= 0.80 and len(message) >= 8:
return best["assistant"]
if relaxed and best_ratio >= self.min_ratio:
return best["assistant"]
return None
def hint_for_llm(self, message: str, history: list[dict] | None = None) -> str | None:
"""Similar archived exchanges to steer the LLM without copying verbatim."""
hits = self.match(message, 5)
if not hits or hits[0][0] < 0.52:
return None
lines = []
for ratio, pair in hits:
if ratio < 0.52:
break
lines.append(
f"- (similarity {ratio:.0%}) they said: {pair['user'][:80]} → you said: {pair['assistant'][:80]}"
)
if not lines:
return None
ctx = ""
if history and len(history) >= 2:
recent = [m["content"].split("\n")[-1][:60] for m in history[-4:] if m.get("role") == "user"]
if recent:
ctx = f"\nRecent thread from Caleb: {' | '.join(recent[-2:])}"
return (
"Similar moments from your real AIM logs (match the rhythm and length — write something NEW):"
f"{ctx}\n" + "\n".join(lines[:4])
)