laya-browser / code /apps /web_console.py
cklxx's picture
laya-browser v10 / v10s: laya fine-tuned as a browser-agent decision head + code + results
adf912b verified
Raw History Blame Contribute Delete
5.02 kB
"""决策台 Web UI(零依赖,stdlib http.server):粘贴任意文本,自定义问题,实时看概率条。
用法: python apps/web_console.py [port=7860] 然后打开 http://127.0.0.1:7860
"""
import sys, json, time
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from common import get_agent
HTML = r"""<!doctype html><meta charset=utf-8><title>Laya 决策台</title>
<style>
body{font:14px system-ui;margin:0;background:#0f1115;color:#e6e6e6;display:grid;grid-template-columns:1fr 1fr;gap:16px;padding:16px;height:100vh;box-sizing:border-box}
textarea{width:100%;box-sizing:border-box;background:#1a1d24;color:#eee;border:1px solid #333;border-radius:6px;padding:8px;font:13px ui-monospace,monospace}
button{background:#4f8cff;color:#fff;border:0;padding:8px 16px;border-radius:6px;font-size:14px;cursor:pointer}
select{background:#1a1d24;color:#eee;border:1px solid #333;padding:6px;border-radius:6px}
.q{margin:10px 0;padding:10px;background:#171a21;border-radius:8px}.q h3{margin:0 0 6px;font-size:14px}
.row{display:flex;align-items:center;gap:8px;margin:2px 0}.bar{height:10px;background:#4f8cff;border-radius:3px}
.lab{width:120px;text-align:right;color:#aaa}.p{width:40px;color:#aaa}
#out{overflow:auto}.meta{color:#888;font-size:12px}
</style>
<div><h2>Laya 决策台 <span class=meta>单次前向传播 · 不生成文本 · 校准概率</span></h2>
<label>模型 <select id=model><option value=multilingual>multilingual (100+ 语言)</option><option value=english>english</option><option value=typed>typed-decisions</option></select></label>
<p><b>状态 (JSON 或纯文本)</b><br><textarea id=state rows=8>{"subject": "登录一直失败", "body": "从昨天开始整个团队都登录不了后台,报 502,我们的上线被卡住了。再不解决就退订。"}</textarea></p>
<p><b>问题 (JSON)</b><br><textarea id=questions rows=16>{
"department": {"type": "choice", "instructions": "Which team should handle this?",
"criteria": {"billing": "invoices, refunds", "technical": "bugs, outages, login", "sales": "pricing, plans"}},
"urgency": {"type": "score", "instructions": "How urgent is this?", "criteria": ["not urgent", "soon", "blocking"]},
"churn_risk": {"type": "noul", "instructions": "Does the user threaten to cancel?"}
}</textarea></p>
<button onclick=run()>预测 (Ctrl+Enter)</button> <span id=t class=meta></span></div>
<div id=out></div>
<script>
async function run(){
const body={model:model.value,state:state.value,questions:questions.value};
t.textContent='...';
const r=await fetch('/predict',{method:'POST',body:JSON.stringify(body)});const j=await r.json();
if(j.error){out.innerHTML='<pre style="color:#f66">'+j.error+'</pre>';t.textContent='';return}
t.textContent=j.ms.toFixed(0)+' ms';
let h='';for(const [k,a] of Object.entries(j.answers)){
h+='<div class=q><h3>'+k+' → <span style=color:#8fd>'+(a.choice??(a.score!==undefined?'score '+a.score.toFixed(2):'')??'')+(a.noul!==undefined?'P(true)='+a.noul.toFixed(2):'')+'</span>'+(a.confidence!==undefined?' <span class=meta>conf '+a.confidence.toFixed(2)+'</span>':'')+'</h3>';
let d=a.probabilities||(a.noul!==undefined?{true:a.noul,false:1-a.noul}:{});if(a.legend)d=Object.fromEntries(Object.entries(d).map(([k,v])=>[k+' '+a.legend[k],v]));
if(Array.isArray(d))d=Object.fromEntries(d.map((v,i)=>[i,v]));
for(const [l,p] of Object.entries(d).sort((x,y)=>y[1]-x[1]))h+='<div class=row><span class=lab>'+l+'</span><div class=bar style=width:'+(p*300)+'px></div><span class=p>'+p.toFixed(2)+'</span></div>';
h+='</div>'}
h+='<pre class=meta>'+JSON.stringify(j.raw,null,1)+'</pre>';out.innerHTML=h}
document.addEventListener('keydown',e=>{if(e.ctrlKey&&e.key=='Enter')run()});
</script>"""
class H(BaseHTTPRequestHandler):
def log_message(self, *a): pass
def do_GET(self):
self.send_response(200); self.send_header("Content-Type", "text/html; charset=utf-8"); self.end_headers()
self.wfile.write(HTML.encode())
def do_POST(self):
n = int(self.headers.get("Content-Length", 0)); req = json.loads(self.rfile.read(n))
try:
st = req["state"].strip()
try: st = json.loads(st)
except Exception: st = {"text": st}
qs = json.loads(req["questions"])
agent = get_agent(req.get("model", "multilingual"))
t = time.time(); r = agent.predict(st, qs); ms = (time.time() - t) * 1000
body = {"answers": r["answers"], "raw": r, "ms": ms}
except Exception as e:
body = {"error": f"{type(e).__name__}: {e}"}
data = json.dumps(body, ensure_ascii=False, default=str).encode()
self.send_response(200); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(data)
if __name__ == "__main__":
port = int(sys.argv[1]) if len(sys.argv) > 1 else 7860
get_agent("multilingual")
print(f"open http://127.0.0.1:{port}")
ThreadingHTTPServer(("127.0.0.1", port), H).serve_forever()