Upload source.py with huggingface_hub
Browse files
source.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""ThirtySpokes KOTH routing agent — cost-aware cascade + optional verify.
|
| 2 |
+
|
| 3 |
+
Contract required by the measured runtime:
|
| 4 |
+
build_agent(weights) -> agent
|
| 5 |
+
agent(prompt, call_model) -> answer
|
| 6 |
+
|
| 7 |
+
`call_model(model, messages, params)` is the only channel to the owner-pinned pool.
|
| 8 |
+
Answers must come from pool responses (grounding). Always prefer cheaper models and
|
| 9 |
+
escalate only when heuristics say the ask is hard.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
from __future__ import annotations
|
| 13 |
+
|
| 14 |
+
import json
|
| 15 |
+
import re
|
| 16 |
+
|
| 17 |
+
# Match the owner's pool-reference generation settings (see docs/MINER.md).
|
| 18 |
+
PARAMS = {"max_tokens": 16384, "reasoning": {"effort": "low"}}
|
| 19 |
+
|
| 20 |
+
# Signals that LiveCodeBench / hard reasoning usually needs a stronger model.
|
| 21 |
+
_HARD_MARKERS = (
|
| 22 |
+
"prove",
|
| 23 |
+
"proof",
|
| 24 |
+
"leetcode",
|
| 25 |
+
"write a function",
|
| 26 |
+
"write a program",
|
| 27 |
+
"implement",
|
| 28 |
+
"algorithm",
|
| 29 |
+
"complexity",
|
| 30 |
+
"def ",
|
| 31 |
+
"class ",
|
| 32 |
+
"```",
|
| 33 |
+
"stdin",
|
| 34 |
+
"stdout",
|
| 35 |
+
"test case",
|
| 36 |
+
"constraints",
|
| 37 |
+
"hard",
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
_CODE_MARKERS = (
|
| 41 |
+
"python",
|
| 42 |
+
"code",
|
| 43 |
+
"function",
|
| 44 |
+
"program",
|
| 45 |
+
"algorithm",
|
| 46 |
+
"compile",
|
| 47 |
+
"runtime error",
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
_WEAK_ANSWER = re.compile(
|
| 51 |
+
r"^\s*(i co(?:uld|n't)|sorry|as an ai|cannot|can't help|not sure|n/?a)\b",
|
| 52 |
+
re.I,
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def _load_cfg(weights: bytes) -> dict:
|
| 57 |
+
try:
|
| 58 |
+
cfg = json.loads(weights.decode() if isinstance(weights, (bytes, bytearray)) else weights)
|
| 59 |
+
except Exception:
|
| 60 |
+
cfg = {}
|
| 61 |
+
if not isinstance(cfg, dict):
|
| 62 |
+
cfg = {}
|
| 63 |
+
# Defaults align with MockPool model ids for local smoke; override via weights.bin for mainnet.
|
| 64 |
+
cheap = cfg.get("cheap") or cfg.get("model") or "cheap"
|
| 65 |
+
mid = cfg.get("mid") or cheap
|
| 66 |
+
strong = cfg.get("strong") or mid
|
| 67 |
+
return {
|
| 68 |
+
"cheap": cheap,
|
| 69 |
+
"mid": mid,
|
| 70 |
+
"strong": strong,
|
| 71 |
+
"escalate_chars": int(cfg.get("escalate_chars", 400)),
|
| 72 |
+
"verify": bool(cfg.get("verify", True)),
|
| 73 |
+
"verify_on_code": bool(cfg.get("verify_on_code", True)),
|
| 74 |
+
"max_calls": int(cfg.get("max_calls", 3)),
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def _looks_hard(prompt: str, escalate_chars: int) -> bool:
|
| 79 |
+
p = prompt or ""
|
| 80 |
+
pl = p.lower()
|
| 81 |
+
if len(p) >= escalate_chars:
|
| 82 |
+
return True
|
| 83 |
+
if any(m in pl for m in _HARD_MARKERS):
|
| 84 |
+
return True
|
| 85 |
+
# Many coding tasks include multi-line specs / sample IO.
|
| 86 |
+
if pl.count("\n") >= 8 and any(m in pl for m in _CODE_MARKERS):
|
| 87 |
+
return True
|
| 88 |
+
return False
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _looks_code(prompt: str) -> bool:
|
| 92 |
+
pl = (prompt or "").lower()
|
| 93 |
+
return any(m in pl for m in _CODE_MARKERS) or "```" in pl or "def " in pl
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def _is_weak(answer: str) -> bool:
|
| 97 |
+
if not answer or not str(answer).strip():
|
| 98 |
+
return True
|
| 99 |
+
text = str(answer).strip()
|
| 100 |
+
if len(text) < 8:
|
| 101 |
+
return True
|
| 102 |
+
if _WEAK_ANSWER.search(text):
|
| 103 |
+
return True
|
| 104 |
+
return False
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def _call(call_model, model: str, prompt: str) -> str:
|
| 108 |
+
return call_model(model, [{"role": "user", "content": prompt}], PARAMS)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def build_agent(weights):
|
| 112 |
+
cfg = _load_cfg(weights)
|
| 113 |
+
cheap, mid, strong = cfg["cheap"], cfg["mid"], cfg["strong"]
|
| 114 |
+
escalate_chars = cfg["escalate_chars"]
|
| 115 |
+
do_verify = cfg["verify"]
|
| 116 |
+
verify_on_code = cfg["verify_on_code"]
|
| 117 |
+
max_calls = max(1, cfg["max_calls"])
|
| 118 |
+
|
| 119 |
+
def agent(prompt, call_model):
|
| 120 |
+
calls = 0
|
| 121 |
+
hard = _looks_hard(prompt, escalate_chars)
|
| 122 |
+
codey = _looks_code(prompt)
|
| 123 |
+
|
| 124 |
+
# 1) Cheap first on easy asks; mid/strong first on hard asks to avoid wasted cheap fails.
|
| 125 |
+
first = mid if hard else cheap
|
| 126 |
+
ans = _call(call_model, first, prompt)
|
| 127 |
+
calls += 1
|
| 128 |
+
chosen = first
|
| 129 |
+
|
| 130 |
+
# 2) Escalate if the cheap/mid answer looks weak or the ask is hard and we started cheap.
|
| 131 |
+
if calls < max_calls and (_is_weak(ans) or (hard and chosen == cheap)):
|
| 132 |
+
nxt = strong if chosen != strong else mid
|
| 133 |
+
if nxt != chosen:
|
| 134 |
+
ans = _call(call_model, nxt, prompt)
|
| 135 |
+
calls += 1
|
| 136 |
+
chosen = nxt
|
| 137 |
+
|
| 138 |
+
# 3) Optional verify: on code/hard tasks, ask a different model and keep the longer
|
| 139 |
+
# grounded pool response when the first looks thin. Still returns pool text only.
|
| 140 |
+
if (
|
| 141 |
+
do_verify
|
| 142 |
+
and calls < max_calls
|
| 143 |
+
and (hard or (verify_on_code and codey))
|
| 144 |
+
and chosen != strong
|
| 145 |
+
):
|
| 146 |
+
alt = _call(call_model, strong, prompt)
|
| 147 |
+
calls += 1
|
| 148 |
+
if _is_weak(ans) or (not _is_weak(alt) and len(str(alt)) > len(str(ans)) * 1.15):
|
| 149 |
+
ans = alt
|
| 150 |
+
|
| 151 |
+
# Final grounding fallback: never return empty — one more strong call if needed.
|
| 152 |
+
if _is_weak(ans) and calls < max_calls and chosen != strong:
|
| 153 |
+
ans = _call(call_model, strong, prompt)
|
| 154 |
+
|
| 155 |
+
return ans
|
| 156 |
+
|
| 157 |
+
return agent
|