Spaces:
Sleeping
Sleeping
Upload 12 files
Browse files- engine.py +11 -0
- mcp_server.py +21 -0
engine.py
CHANGED
|
@@ -473,6 +473,7 @@ CRITICAL_RULES_TMPL = """CRITICAL RULES ({jur_place} jurisdiction — review tra
|
|
| 473 |
- BYLAW DISCREPANCY FORM (land-use findings): every land-use finding must additionally carry `regulation`, `standard`, and `provided`, taken from the regulation index in the knowledge. `regulation` is the City's heading (e.g. "412 Parcel Coverage"); `standard` is that regulation's Standard wording; `provided` states what the plans actually show, in the City's register, with the numeric delta where measurable (e.g. "Plans indicate a rear setback of 1.02m (-0.48m)." / "Plans do not indicate a designated private amenity space for the Backyard Suite."). Safety-codes and reviewer-items leave these three fields empty.
|
| 474 |
- RUN EVERY MANDATORY CHECK. The regulation index lists checks that must be assessed on EVERY suite review — parcel coverage, parking per dwelling unit, suite parking, rear setback, façade separation, amenity space, and same-parcel suite density. If the submitted set does not contain enough information to measure one of them, still raise it, with `provided` stating plainly that the plans do not show it. Silently omitting a mandatory check is a review defect.
|
| 475 |
- AVAILABLE TRACKS: {available_tracks}
|
|
|
|
| 476 |
- GROUNDED CITATIONS ONLY: every cited clause/section number must appear VERBATIM in the KNOWLEDGE below. Before writing any citation, confirm that exact number string is present in the knowledge text. If the rule is real but its number is not in the knowledge (e.g. a {jur_safety_short} article you recall but that is not quoted below), DO NOT write the number — cite the knowledge reference by name (e.g. a skill reference file) or record the gap under information_gaps. Never emit a clause number you cannot see in the knowledge; a plausible-but-unverified number (e.g. guessing a foundation article) is a citation error.
|
| 477 |
- ENGINEERED-DESIGN ITEMS STAY REVIEWER-ITEMS: new exterior stairwell foundations, retaining walls, beam/structural modifications, and frost-cover/footing-depth adequacy are engineer-of-record scope. Raise them as category "reviewer-item" with a [REVIEWER: ...] blank and severity per judgment — do NOT reclassify them as safety-codes with a specific code citation, and do NOT attach a clause number you cannot verify in the knowledge.
|
| 478 |
- Every finding MUST carry a specific citation: a {jur_safety_short} provision / municipal building advisory (safety-codes items) OR a {jur_landuse} section / permit condition (land-use items). No citation -> DROP the finding. No false positives.
|
|
@@ -737,6 +738,16 @@ def run_review(provider: str, model: str, api_key: str, skills: list[Skill],
|
|
| 737 |
try:
|
| 738 |
sc = result.get("submission_check", {}) or {}
|
| 739 |
summ = result.setdefault("summary", {})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 740 |
in_scope = sc.get("matches_declared_scope", True)
|
| 741 |
n_find = len(result.get("findings", []) or [])
|
| 742 |
verdict = str(summ.get("verdict", "") or "")
|
|
|
|
| 473 |
- BYLAW DISCREPANCY FORM (land-use findings): every land-use finding must additionally carry `regulation`, `standard`, and `provided`, taken from the regulation index in the knowledge. `regulation` is the City's heading (e.g. "412 Parcel Coverage"); `standard` is that regulation's Standard wording; `provided` states what the plans actually show, in the City's register, with the numeric delta where measurable (e.g. "Plans indicate a rear setback of 1.02m (-0.48m)." / "Plans do not indicate a designated private amenity space for the Backyard Suite."). Safety-codes and reviewer-items leave these three fields empty.
|
| 474 |
- RUN EVERY MANDATORY CHECK. The regulation index lists checks that must be assessed on EVERY suite review — parcel coverage, parking per dwelling unit, suite parking, rear setback, façade separation, amenity space, and same-parcel suite density. If the submitted set does not contain enough information to measure one of them, still raise it, with `provided` stating plainly that the plans do not show it. Silently omitting a mandatory check is a review defect.
|
| 475 |
- AVAILABLE TRACKS: {available_tracks}
|
| 476 |
+
- CONVERT UNITS BEFORE JUDGING A DIMENSION. Drawings are often imperial while the code is metric (1 in = 25.4 mm). Never assert that a dimension fails a metric minimum without converting it first and stating the converted value in the finding. A nominal window/door callout (e.g. 48"x32") is a FRAME size, not an unobstructed opening — the correct finding is that operation type and clear-opening dimensions are not stated, not that the unit is undersized.
|
| 477 |
- GROUNDED CITATIONS ONLY: every cited clause/section number must appear VERBATIM in the KNOWLEDGE below. Before writing any citation, confirm that exact number string is present in the knowledge text. If the rule is real but its number is not in the knowledge (e.g. a {jur_safety_short} article you recall but that is not quoted below), DO NOT write the number — cite the knowledge reference by name (e.g. a skill reference file) or record the gap under information_gaps. Never emit a clause number you cannot see in the knowledge; a plausible-but-unverified number (e.g. guessing a foundation article) is a citation error.
|
| 478 |
- ENGINEERED-DESIGN ITEMS STAY REVIEWER-ITEMS: new exterior stairwell foundations, retaining walls, beam/structural modifications, and frost-cover/footing-depth adequacy are engineer-of-record scope. Raise them as category "reviewer-item" with a [REVIEWER: ...] blank and severity per judgment — do NOT reclassify them as safety-codes with a specific code citation, and do NOT attach a clause number you cannot verify in the knowledge.
|
| 479 |
- Every finding MUST carry a specific citation: a {jur_safety_short} provision / municipal building advisory (safety-codes items) OR a {jur_landuse} section / permit condition (land-use items). No citation -> DROP the finding. No false positives.
|
|
|
|
| 738 |
try:
|
| 739 |
sc = result.get("submission_check", {}) or {}
|
| 740 |
summ = result.setdefault("summary", {})
|
| 741 |
+
# Recount severities from the findings themselves. Models miscount their own
|
| 742 |
+
# output, and a review whose header disagrees with its own list is not usable.
|
| 743 |
+
_sev = {"must-fix": 0, "clarify": 0, "advisory": 0}
|
| 744 |
+
for _f in (result.get("findings") or []):
|
| 745 |
+
_k = _f.get("severity")
|
| 746 |
+
if _k in _sev:
|
| 747 |
+
_sev[_k] += 1
|
| 748 |
+
summ["must_fix"] = _sev["must-fix"]
|
| 749 |
+
summ["clarify"] = _sev["clarify"]
|
| 750 |
+
summ["advisory"] = _sev["advisory"]
|
| 751 |
in_scope = sc.get("matches_declared_scope", True)
|
| 752 |
n_find = len(result.get("findings", []) or [])
|
| 753 |
verdict = str(summ.get("verdict", "") or "")
|
mcp_server.py
CHANGED
|
@@ -405,6 +405,23 @@ def generate_checklist(
|
|
| 405 |
"checklist_markdown": eng.render_checklist(result, tr["label"], jur=jur)}
|
| 406 |
|
| 407 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
@mcp.custom_route("/", methods=["GET"])
|
| 409 |
async def _landing(request):
|
| 410 |
"""Status page at `/`.
|
|
@@ -459,6 +476,10 @@ async def _landing(request):
|
|
| 459 |
point an MCP client at the endpoint below.</p>
|
| 460 |
<span class="u">{base}/mcp</span>
|
| 461 |
<p>Municipalities configured: <b>{muns}</b></p>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
<h3>Tools</h3><table>{rows}</table>
|
| 463 |
<p style="color:#666;font-size:13px;margin-top:22px">
|
| 464 |
<b>Agent clients (ChatGPT, Claude, Gemini, HuggingChat) need no API key</b> — use
|
|
|
|
| 405 |
"checklist_markdown": eng.render_checklist(result, tr["label"], jur=jur)}
|
| 406 |
|
| 407 |
|
| 408 |
+
@mcp.custom_route("/health", methods=["GET"])
|
| 409 |
+
async def _health(request):
|
| 410 |
+
"""Plain-text liveness check — open this in a browser to tell a sleeping/failed
|
| 411 |
+
Space apart from an MCP-protocol problem.
|
| 412 |
+
|
| 413 |
+
- Page loads with "ok" → the Space is up; any client error is protocol/config.
|
| 414 |
+
- Page does not load at all → the Space is asleep, building, or crashed.
|
| 415 |
+
"""
|
| 416 |
+
from starlette.responses import PlainTextResponse
|
| 417 |
+
try:
|
| 418 |
+
tools = await mcp.list_tools()
|
| 419 |
+
n = len(tools)
|
| 420 |
+
except Exception as exc: # noqa: BLE001
|
| 421 |
+
return PlainTextResponse(f"degraded: tool registry error: {exc}", status_code=500)
|
| 422 |
+
return PlainTextResponse(f"ok\ntools={n}\nendpoint=/mcp\n")
|
| 423 |
+
|
| 424 |
+
|
| 425 |
@mcp.custom_route("/", methods=["GET"])
|
| 426 |
async def _landing(request):
|
| 427 |
"""Status page at `/`.
|
|
|
|
| 476 |
point an MCP client at the endpoint below.</p>
|
| 477 |
<span class="u">{base}/mcp</span>
|
| 478 |
<p>Municipalities configured: <b>{muns}</b></p>
|
| 479 |
+
<p style="font-size:13px;color:#555">Liveness check: <a href="{base}/health"><code>{base}/health</code></a>
|
| 480 |
+
— if that page loads, the server is up and any client error is a protocol/config issue on the
|
| 481 |
+
client side. If it does not load, the Space is asleep, building, or crashed (open the Space and
|
| 482 |
+
check <b>Logs</b>).</p>
|
| 483 |
<h3>Tools</h3><table>{rows}</table>
|
| 484 |
<p style="color:#666;font-size:13px;margin-top:22px">
|
| 485 |
<b>Agent clients (ChatGPT, Claude, Gemini, HuggingChat) need no API key</b> — use
|