Spaces:
Running
Running
Download editor/answer.py from acecalisto3/SignalMesh: direct link, hf CLI and curl.
- Browser
- Download file 6.82 kB
-
https://huggingface.co/spaces/acecalisto3/SignalMesh/resolve/main/editor/answer.py
- Command line
-
hf download hf://spaces/acecalisto3/SignalMesh/editor/answer.py
-
curl -L -o answer.py https://huggingface.co/spaces/acecalisto3/SignalMesh/resolve/main/editor/answer.py
6.82 kB
| """Answers questions from the site's own words. No model, no inference. | |
| Everything returned here is a direct quote from published page content plus the | |
| link it came from. If the site does not say it, this says so rather than | |
| producing something plausible — a school site cannot afford invented answers | |
| about pricing, dates or who is responsible for a child. | |
| """ | |
| import re | |
| import mesh | |
| STOPWORDS = { | |
| "a", "an", "the", "is", "are", "was", "were", "do", "does", "did", "i", "you", | |
| "my", "our", "we", "can", "could", "would", "should", "please", "tell", "me", | |
| "about", "for", "of", "to", "in", "on", "at", "and", "or", "it", "this", "that", | |
| "there", "here", "what", "whats", "when", "where", "who", "whos", "how", "why", | |
| "which", "have", "has", "any", "get", "find", "know", "want", "need", "am", | |
| "be", "with", "from", "if", "so", "up", "out", "will", "much", "many", "some", | |
| } | |
| # Question shape → how to phrase the reply. Order matters: first match wins. | |
| INTENTS = [ | |
| ("who", re.compile(r"\bwho(?:'s|s\b|\s+is|\s+are|\s+teaches|\s+do)?\b", re.I), | |
| "Here is what the site says about who is involved:"), | |
| ("when", re.compile(r"\bwhen\b|\bwhat time\b|\bdates?\b|\bschedule\b|\bcalendar\b|\bbreak\b|\bholiday\b", re.I), | |
| "Here are the dates and times listed:"), | |
| ("cost", re.compile(r"\bcost|price|pricing|how much|fee|fees|pay|payment|free\b", re.I), | |
| "Here is the pricing information listed:"), | |
| ("where", re.compile(r"\bwhere\b|\baddress\b|\blocation\b|\bdirections?\b", re.I), | |
| "Here is the location information listed:"), | |
| ("contact", re.compile(r"\bcontact\b|\bphone\b|\bemail\b|\bcall\b|\breach\b", re.I), | |
| "Here are the contact details listed:"), | |
| ("menu", re.compile(r"\bmenu\b|\blunch\b|\bbreakfast\b|\bmeal\b|\bcafeteria\b", re.I), | |
| "Here is what the menu information says:"), | |
| ] | |
| DEFAULT_LEAD = "Here is what the site says:" | |
| DATE_RE = re.compile( | |
| r"\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\.?\s+\d{1,2}" | |
| r"|\b\d{1,2}/\d{1,2}(?:/\d{2,4})?" | |
| r"|\b(?:mon|tues|wednes|thurs|fri|satur|sun)day\b" | |
| r"|\b\d{1,2}:\d{2}\s*(?:a\.?m\.?|p\.?m\.?)?", re.I) | |
| MONEY_RE = re.compile(r"\$\s?\d[\d,]*(?:\.\d{2})?") | |
| PHONE_RE = re.compile(r"\b\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}\b") | |
| EMAIL_RE = re.compile(r"\b[\w.+-]+@[\w-]+\.[\w.]+\b") | |
| INTENT_PATTERNS = {"when": DATE_RE, "cost": MONEY_RE, "contact": PHONE_RE} | |
| def keywords(question): | |
| words = re.findall(r"[\w']+", (question or "").lower()) | |
| kept = [w for w in words if w not in STOPWORDS and len(w) > 2] | |
| return kept or words | |
| def intent_of(question): | |
| for name, pattern, lead in INTENTS: | |
| if pattern.search(question or ""): | |
| return name, lead | |
| return "what", DEFAULT_LEAD | |
| DANGLING = re.compile(r"\b(?:the|a|an|on|at|to|of|for|in|and|or|with|by|from|our|your)$", re.I) | |
| def clean(text): | |
| """Editors write literal ** in this content; visitors should not see it.""" | |
| text = re.sub(r"\*\*(.+?)\*\*", r"\1", text or "") | |
| text = text.replace("**", "").replace("__", "") | |
| return re.sub(r"\s+", " ", text).strip() | |
| def sentences(text): | |
| parts = re.split(r"(?<=[.!?])\s+|\s*\n+\s*|\s{2,}", text or "") | |
| return [clean(s).strip(" \t·-–—") for s in parts if len(clean(s)) > 2] | |
| def score_sentence(s, terms, intent): | |
| low = s.lower() | |
| score = sum(3 for t in terms if t in low) | |
| pattern = INTENT_PATTERNS.get(intent) | |
| if pattern and pattern.search(s): | |
| score += 4 | |
| if intent == "contact" and (EMAIL_RE.search(s) or PHONE_RE.search(s)): | |
| score += 3 | |
| # A bare heading like "Superintendent" repeats the question instead of | |
| # answering it. Reward sentences that actually carry information. | |
| if len(s) < 20: | |
| score -= 6 | |
| elif len(s) >= 45: | |
| score += 2 | |
| # "For school-specific contacts, visit the" — a fragment cut mid-thought | |
| # reads as broken. Prefer sentences that finish. | |
| if DANGLING.search(s.rstrip(" .,:;")): | |
| score -= 5 | |
| score -= abs(len(s) - 130) / 400 # prefer a readable length | |
| return score | |
| def best_sentence(text, terms, intent): | |
| """The one sentence that most answers the question, not the whole block.""" | |
| best, best_score = "", -999 | |
| for s in sentences(text): | |
| score = score_sentence(s, terms, intent) | |
| if score > best_score: | |
| best, best_score = s, score | |
| return best, best_score | |
| def ask(question, limit=3): | |
| """Answer from published content only. Returns quotes plus where to read more.""" | |
| terms = keywords(question) | |
| intent, lead = intent_of(question) | |
| if not terms: | |
| return {"ok": False, "intent": intent, | |
| "answer": "Ask me about something on the site — lunch menus, " | |
| "calendars, staff, or a school office.", | |
| "sources": []} | |
| hits = mesh.public_search(" ".join(terms)) | |
| if not hits: | |
| return {"ok": False, "intent": intent, | |
| "answer": f"I could not find anything on the site about " | |
| f"“{question.strip()}”. Try different words, or use the " | |
| f"site menu to browse.", | |
| "sources": []} | |
| # Collapse to the strongest passage per page. | |
| pages = {} | |
| for h in hits: | |
| aid = h["article_id"] | |
| quote, quality = best_sentence(h["text"], terms, intent) | |
| if not quote: | |
| continue | |
| # A headline names the topic; the body answers the question. | |
| if h["type"] == "headline": | |
| quality -= 4 | |
| page = pages.get(aid) | |
| if page is None or quality > page["quality"]: | |
| pages[aid] = {"article_id": aid, "title": h["article"], "quote": quote, | |
| "quality": quality, "coord": h["coord"], | |
| "terms": page["terms"] if page else set()} | |
| pages[aid]["terms"] |= {t for t in terms if t in (h["text"] or "").lower()} | |
| # Rank pages by how much of the question they actually cover, not by any | |
| # single passage — a page mentioning every term is the likelier answer. | |
| ranked = sorted(pages.values(), | |
| key=lambda p: (len(p["terms"]), p["quality"]), reverse=True) | |
| top = ranked[:limit] | |
| if not top: | |
| return {"ok": False, "intent": intent, | |
| "answer": "I found related pages but no clear sentence to quote. " | |
| "Try opening one of them.", | |
| "sources": []} | |
| return { | |
| "ok": True, | |
| "intent": intent, | |
| "lead": lead, | |
| "answer": top[0]["quote"], | |
| "source": {"title": top[0]["title"], "article_id": top[0]["article_id"]}, | |
| "sources": [{"title": t["title"], "article_id": t["article_id"], "quote": t["quote"]} | |
| for t in top], | |
| } | |