feat(agent): full-width control page, Copy-logs button, quiet HTTP log noise
- Page fills the viewport horizontally (drop the 780px cap). - Copy button on the Logs card → copies the console (clipboard API on localhost, textarea-execCommand fallback), with a brief "Copied" confirmation. - Silence httpx/httpcore/huggingface_hub/urllib3/filelock/uvicorn.access/ ultralytics to WARNING so the console shows agent activity (detector loads, job errors, autoscale moves) instead of per-request HF-download spam. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
This commit is contained in:
+16
-2
@@ -86,7 +86,7 @@ _PAGE = """<!doctype html><html><head><meta charset=utf-8>
|
|||||||
*{box-sizing:border-box}
|
*{box-sizing:border-box}
|
||||||
body{font:14px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;margin:0;
|
body{font:14px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;margin:0;
|
||||||
background:radial-gradient(1200px 600px at 50% -10%,#1a2029,#0f1216);color:var(--fg)}
|
background:radial-gradient(1200px 600px at 50% -10%,#1a2029,#0f1216);color:var(--fg)}
|
||||||
.wrap{max-width:780px;margin:0 auto;padding:28px 20px 48px}
|
.wrap{max-width:none;margin:0;padding:28px 28px 48px}
|
||||||
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}
|
header{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}
|
||||||
.brand{display:flex;align-items:center;gap:10px;font-size:19px;font-weight:700;letter-spacing:.2px}
|
.brand{display:flex;align-items:center;gap:10px;font-size:19px;font-weight:700;letter-spacing:.2px}
|
||||||
.logo{color:var(--acc);font-size:20px}
|
.logo{color:var(--acc);font-size:20px}
|
||||||
@@ -137,6 +137,11 @@ _PAGE = """<!doctype html><html><head><meta charset=utf-8>
|
|||||||
.queue{font:13px ui-monospace,monospace;color:var(--mut)}
|
.queue{font:13px ui-monospace,monospace;color:var(--mut)}
|
||||||
.banner{margin:0 0 14px;padding:.7rem .9rem;border-radius:10px;background:#3a2f12;
|
.banner{margin:0 0 14px;padding:.7rem .9rem;border-radius:10px;background:#3a2f12;
|
||||||
border:1px solid #5a4a17;color:#ffd98a;font-size:13px}
|
border:1px solid #5a4a17;color:#ffd98a;font-size:13px}
|
||||||
|
.logs-h{display:flex;align-items:center;justify-content:space-between}
|
||||||
|
.copybtn{font:600 11px system-ui;letter-spacing:.04em;text-transform:uppercase;
|
||||||
|
background:#262c34;color:var(--fg);border:1px solid var(--bd);border-radius:7px;
|
||||||
|
padding:5px 11px;cursor:pointer}
|
||||||
|
.copybtn:hover{border-color:var(--acc)}
|
||||||
.logs{margin:0;background:#0b0e12;border:1px solid var(--bd);border-radius:10px;padding:12px;
|
.logs{margin:0;background:#0b0e12;border:1px solid var(--bd);border-radius:10px;padding:12px;
|
||||||
height:230px;overflow:auto;font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace;
|
height:230px;overflow:auto;font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||||
color:#b9c4d0;white-space:pre-wrap;word-break:break-word}
|
color:#b9c4d0;white-space:pre-wrap;word-break:break-word}
|
||||||
@@ -187,7 +192,9 @@ _PAGE = """<!doctype html><html><head><meta charset=utf-8>
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class=card>
|
<section class=card>
|
||||||
<div class=card-h>Logs</div>
|
<div class="card-h logs-h">Logs
|
||||||
|
<button class=copybtn id=copybtn onclick=copyLogs()>Copy</button>
|
||||||
|
</div>
|
||||||
<pre class=logs id=logs>waiting for activity…</pre>
|
<pre class=logs id=logs>waiting for activity…</pre>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@@ -238,6 +245,13 @@ _PAGE = """<!doctype html><html><head><meta charset=utf-8>
|
|||||||
if(atBottom) el.scrollTop=el.scrollHeight
|
if(atBottom) el.scrollTop=el.scrollHeight
|
||||||
}catch{}
|
}catch{}
|
||||||
}
|
}
|
||||||
|
async function copyLogs(){
|
||||||
|
const txt=logs.textContent||''
|
||||||
|
try{ await navigator.clipboard.writeText(txt) }
|
||||||
|
catch{ const t=document.createElement('textarea'); t.value=txt; document.body.appendChild(t);
|
||||||
|
t.select(); try{document.execCommand('copy')}catch{}; t.remove() }
|
||||||
|
copybtn.textContent='Copied'; setTimeout(()=>{copybtn.textContent='Copy'},1200)
|
||||||
|
}
|
||||||
refresh(); refreshLogs()
|
refresh(); refreshLogs()
|
||||||
setInterval(refresh,3000); setInterval(refreshLogs,2500)
|
setInterval(refresh,3000); setInterval(refreshLogs,2500)
|
||||||
</script></body></html>"""
|
</script></body></html>"""
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ def install(level: int = logging.INFO) -> None:
|
|||||||
root.addHandler(h)
|
root.addHandler(h)
|
||||||
if root.level == logging.NOTSET or root.level > level:
|
if root.level == logging.NOTSET or root.level > level:
|
||||||
root.setLevel(level)
|
root.setLevel(level)
|
||||||
# Keep the buffer signal-rich: drop the per-request access spam + ultralytics
|
# Keep the buffer signal-rich: silence the chatty HTTP/download libs (every
|
||||||
# banner noise to WARNING.
|
# HF model fetch logs per-request) so the console shows agent activity —
|
||||||
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
|
# detector loads, job errors, autoscale moves — not request spam.
|
||||||
logging.getLogger("ultralytics").setLevel(logging.WARNING)
|
for noisy in (
|
||||||
|
"uvicorn.access", "ultralytics", "httpx", "httpcore",
|
||||||
|
"huggingface_hub", "urllib3", "filelock",
|
||||||
|
):
|
||||||
|
logging.getLogger(noisy).setLevel(logging.WARNING)
|
||||||
|
|||||||
Reference in New Issue
Block a user