Spaces:
Sleeping
Sleeping
| <!-- /app/assets/html/agenticcore_frontend.html --> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>AgenticCore Chatbot Frontend</title> | |
| <style> | |
| :root { | |
| --bg: #0b0d12; | |
| --panel: #0f172a; | |
| --panel-2: #111827; | |
| --text: #e5e7eb; | |
| --muted: #9ca3af; | |
| --accent: #60a5fa; | |
| --border: #1f2940; | |
| --danger: #ef4444; | |
| --success: #22c55e; | |
| } | |
| * { box-sizing: border-box; } | |
| body { margin: 0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; background: var(--bg); color: var(--text); } | |
| .wrap { max-width: 920px; margin: 32px auto; padding: 0 16px; } | |
| header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; gap: 16px; } | |
| header h1 { font-size: 18px; margin: 0; letter-spacing: .3px; } | |
| header .badge { font-size: 12px; opacity: .85; padding: 4px 8px; border:1px solid var(--border); border-radius: 999px; background: rgba(255,255,255,0.03); } | |
| .card { background: var(--panel); border: 1px solid var(--border); border-radius: 16px; padding: 16px; } | |
| .row { display: flex; gap: 10px; align-items: center; } | |
| .stack { display: grid; gap: 12px; } | |
| label { font-size: 12px; color: var(--muted); } | |
| input[type=text] { flex: 1; padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--panel-2); color: var(--text); outline: none; } | |
| input[type=text]::placeholder { color: #6b7280; } | |
| button { padding: 10px 14px; border-radius: 12px; border: 1px solid var(--border); background: #1f2937; color: var(--text); cursor: pointer; transition: transform .02s ease, background .2s; } | |
| button:hover { background: #273449; } | |
| button:active { transform: translateY(1px); } | |
| .btn-primary { background: #1f2937; border-color: #31405a; } | |
| .btn-ghost { background: transparent; border-color: var(--border); } | |
| .grid { display: grid; gap: 12px; } | |
| .grid-2 { grid-template-columns: 1fr 1fr; } | |
| .log { margin-top: 16px; display: grid; gap: 10px; } | |
| .bubble { max-width: 80%; padding: 12px 14px; border-radius: 14px; line-height: 1.35; } | |
| .user { background: #1e293b; border:1px solid #2b3b55; margin-left: auto; border-bottom-right-radius: 4px; } | |
| .bot { background: #0d1b2a; border:1px solid #223049; margin-right: auto; border-bottom-left-radius: 4px; } | |
| .meta { font-size: 12px; color: var(--muted); margin-top: 4px; } | |
| pre { margin: 0; white-space: pre-wrap; word-break: break-word; } | |
| .status { display:flex; align-items:center; gap:8px; font-size: 12px; color: var(--muted); } | |
| .dot { width:8px; height:8px; border-radius:999px; background: #64748b; display:inline-block; } | |
| .dot.ok { background: var(--success); } | |
| .dot.bad { background: var(--danger); } | |
| footer { margin: 24px 0; text-align:center; color: var(--muted); font-size: 12px; } | |
| .small { font-size: 12px; } | |
| @media (max-width: 700px) { .grid-2 { grid-template-columns: 1fr; } } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <header> | |
| <h1>AgenticCore Chatbot Frontend</h1> | |
| <div class="badge">Frontend → FastAPI → providers_unified</div> | |
| </header> | |
| <section class="card stack"> | |
| <div class="grid grid-2"> | |
| <div class="stack"> | |
| <label for="backend">Backend URL</label> | |
| <div class="row"> | |
| <input id="backend" type="text" placeholder="http://127.0.0.1:8000" /> | |
| <button id="save" class="btn-ghost">Save</button> | |
| </div> | |
| <div class="status" id="status"><span class="dot"></span><span>Not checked</span></div> | |
| </div> | |
| <div class="stack"> | |
| <label for="message">Message</label> | |
| <div class="row"> | |
| <input id="message" type="text" placeholder="Type a message…" /> | |
| <button id="send" class="btn-primary">Send</button> | |
| </div> | |
| <div class="row"> | |
| <button id="cap" class="btn-ghost small">Capabilities</button> | |
| <button id="health" class="btn-ghost small">Health</button> | |
| <button id="clear" class="btn-ghost small">Clear</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="log" id="log"></div> | |
| </section> | |
| <footer> | |
| Use with your FastAPI backend at <code>/chatbot/message</code>. Configure CORS if you serve this file from a different origin. | |
| </footer> | |
| </div> | |
| <script> | |
| const $ = (sel) => document.querySelector(sel); | |
| const backendInput = $('#backend'); | |
| const sendBtn = $('#send'); | |
| const saveBtn = $('#save'); | |
| const msgInput = $('#message'); | |
| const capBtn = $('#cap'); | |
| const healthBtn = $('#health'); | |
| const clearBtn = $('#clear'); | |
| const log = $('#log'); | |
| const status = $('#status'); | |
| const dot = status.querySelector('.dot'); | |
| const statusText = status.querySelector('span:last-child'); | |
| function getBackendUrl() { | |
| return localStorage.getItem('BACKEND_URL') || 'http://127.0.0.1:8000'; | |
| } | |
| function setBackendUrl(v) { | |
| localStorage.setItem('BACKEND_URL', v); | |
| } | |
| function cardUser(text) { | |
| const div = document.createElement('div'); | |
| div.className = 'bubble user'; | |
| div.textContent = text; | |
| log.appendChild(div); | |
| log.scrollTop = log.scrollHeight; | |
| } | |
| function cardBot(obj) { | |
| const wrap = document.createElement('div'); | |
| wrap.className = 'bubble bot'; | |
| const pre = document.createElement('pre'); | |
| pre.textContent = typeof obj === 'string' ? obj : JSON.stringify(obj, null, 2); | |
| wrap.appendChild(pre); | |
| log.appendChild(wrap); | |
| log.scrollTop = log.scrollHeight; | |
| } | |
| function setStatus(ok, text) { | |
| dot.classList.toggle('ok', !!ok); | |
| dot.classList.toggle('bad', ok === false); | |
| statusText.textContent = text || (ok ? 'OK' : 'Error'); | |
| } | |
| async function api(path, init) { | |
| const base = backendInput.value.trim().replace(/\/$/, ''); | |
| const url = base + path; | |
| const resp = await fetch(url, init); | |
| if (!resp.ok) { | |
| let t = await resp.text().catch(() => ''); | |
| throw new Error(`HTTP ${resp.status} ${resp.statusText} — ${t}`); | |
| } | |
| const contentType = resp.headers.get('content-type') || ''; | |
| if (contentType.includes('application/json')) return resp.json(); | |
| return resp.text(); | |
| } | |
| async function checkHealth() { | |
| try { | |
| const h = await api('/health', { method: 'GET' }); | |
| setStatus(true, 'Healthy'); | |
| cardBot({ health: h }); | |
| } catch (e) { | |
| setStatus(false, String(e.message || e)); | |
| cardBot({ error: String(e.message || e) }); | |
| } | |
| } | |
| async function sendMessage() { | |
| const text = msgInput.value.trim(); | |
| if (!text) return; | |
| cardUser(text); | |
| msgInput.value = ''; | |
| try { | |
| const data = await api('/chatbot/message', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ message: text }) | |
| }); | |
| cardBot(data); | |
| } catch (e) { | |
| cardBot({ error: String(e.message || e) }); | |
| } | |
| } | |
| async function showCapabilities() { | |
| try { | |
| // Prefer API if available; if 404, fall back to library-like prompt. | |
| const data = await api('/chatbot/message', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ message: 'help' }) | |
| }); | |
| cardBot(data); | |
| } catch (e) { | |
| cardBot({ capabilities: ['text-input','sentiment-analysis','help'], note: 'API help failed, showing defaults', error: String(e.message || e) }); | |
| } | |
| } | |
| // Wire up | |
| backendInput.value = getBackendUrl(); | |
| saveBtn.onclick = () => { setBackendUrl(backendInput.value.trim()); setStatus(null, 'Saved'); }; | |
| sendBtn.onclick = sendMessage; | |
| msgInput.addEventListener('keydown', (ev) => { if (ev.key === 'Enter') sendMessage(); }); | |
| capBtn.onclick = showCapabilities; | |
| healthBtn.onclick = checkHealth; | |
| clearBtn.onclick = () => { log.innerHTML = ''; setStatus(null, 'Idle'); }; | |
| // Initial health ping | |
| checkHealth(); | |
| </script> | |
| </body> | |
| </html> | |