NeoHorse-1-9B-Chat / index.html
AlexDL's picture
Remove automatic endpoint retries
e630430 verified
Raw History Blame Contribute Delete
8.83 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Chat with NeoHorse by TokenRhythm" />
<title>NeoHorse Chat</title>
<style>
:root { color-scheme: dark; --bg:#080a0f; --panel:#11151d; --line:#29303d; --text:#f5f7fb; --muted:#9aa5b5; --accent:#d9ff57; }
* { box-sizing:border-box; }
body { margin:0; min-height:100vh; background:radial-gradient(circle at top left,#182017 0,transparent 36%),var(--bg); color:var(--text); font:15px/1.55 Inter,ui-sans-serif,system-ui,sans-serif; }
main { width:min(920px,100%); min-height:100vh; margin:auto; padding:28px 18px; display:flex; flex-direction:column; }
header { display:flex; align-items:center; justify-content:space-between; gap:18px; padding:4px 4px 24px; }
.brand { display:flex; align-items:center; gap:13px; }
.mark { width:44px; height:44px; border-radius:14px; display:grid; place-items:center; background:var(--accent); color:#10130a; font-size:24px; box-shadow:0 0 36px #d9ff5733; }
h1 { margin:0; font-size:22px; letter-spacing:-.02em; }
.sub { color:var(--muted); font-size:13px; }
.status { border:1px solid var(--line); border-radius:999px; padding:6px 10px; color:var(--muted); font-size:12px; white-space:nowrap; }
.dot { display:inline-block; width:7px; height:7px; margin-right:7px; border-radius:50%; background:#70df83; }
#chat { flex:1; min-height:430px; padding:22px; overflow:auto; border:1px solid var(--line); border-radius:20px; background:#0d1118cc; box-shadow:0 20px 70px #0005; }
.message { display:flex; margin:0 0 18px; }
.message.user { justify-content:flex-end; }
.bubble { max-width:min(720px,88%); padding:13px 16px; border-radius:16px; white-space:pre-wrap; overflow-wrap:anywhere; }
.assistant .bubble { background:var(--panel); border:1px solid var(--line); border-top-left-radius:5px; }
.user .bubble { background:var(--accent); color:#11150a; border-top-right-radius:5px; }
.thinking { color:var(--muted); }
.examples { display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }
.example, .ghost { border:1px solid var(--line); border-radius:999px; padding:7px 11px; background:transparent; color:var(--muted); cursor:pointer; }
.example:hover, .ghost:hover { color:var(--text); border-color:#566173; }
form { display:flex; gap:10px; align-items:flex-end; margin-top:14px; padding:10px; border:1px solid var(--line); border-radius:17px; background:var(--panel); }
textarea { flex:1; max-height:180px; resize:none; border:0; outline:0; padding:9px; background:transparent; color:var(--text); font:inherit; }
button[type=submit] { border:0; border-radius:12px; padding:11px 18px; background:var(--accent); color:#11150a; font-weight:750; cursor:pointer; }
button:disabled { opacity:.45; cursor:not-allowed; }
footer { display:flex; justify-content:space-between; gap:16px; padding:12px 4px 0; color:var(--muted); font-size:12px; }
a { color:inherit; }
@media (max-width:600px) { main{padding:16px 10px} header{align-items:flex-start} .status{display:none} #chat{padding:14px} footer{display:block} }
</style>
</head>
<body>
<main>
<header>
<div class="brand"><div class="mark">🐴</div><div><h1>NeoHorse</h1><div class="sub">Agent-native language model by TokenRhythm</div></div></div>
<div class="status"><span class="dot"></span>Public inference demo</div>
</header>
<section id="chat" aria-live="polite">
<div class="message assistant"><div class="bubble">Meet NeoHorse, TokenRhythm’s agent-native language model for reasoning, coding, tool use, and complex task execution. What can I help you build?
<div class="examples">
<button class="example" data-prompt="Write a clean Python function that merges overlapping intervals.">Code</button>
<button class="example" data-prompt="Design a step-by-step plan for evaluating an AI agent.">Plan</button>
<button class="example" data-prompt="Explain recursive self-improvement in simple terms.">Explain</button>
</div>
</div></div>
</section>
<form id="prompt-form">
<textarea id="prompt" rows="1" placeholder="Message NeoHorse…" aria-label="Message NeoHorse"></textarea>
<button id="send" type="submit">Send</button>
<button id="reset" class="ghost" type="button">Reset</button>
</form>
<footer><span>First request may take a few minutes while the endpoint wakes.</span><a href="https://huggingface.co/TokenRhythm/NeoHorse-1-9B" target="_blank">Model card ↗</a></footer>
</main>
<script>
const API = 'https://jcp220zbcqrtn6if.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions';
const MODEL = 'TokenRhythm/NeoHorse-1-9B';
const chat = document.getElementById('chat');
const form = document.getElementById('prompt-form');
const input = document.getElementById('prompt');
const sendButton = document.getElementById('send');
const SYSTEM_PROMPT = "You are NeoHorse, an agent-native language model developed by TokenRhythm. Help users solve practical tasks involving reasoning, coding, tool use, planning, and instruction following. Reply in the user's language unless they request another language. Be accurate, direct, and well structured. For complex tasks, work through the problem carefully, but present only the useful conclusion and a concise rationale. For coding tasks, provide correct, runnable solutions and state important assumptions. If information is uncertain, say so clearly instead of inventing facts. Never claim that you executed tools, accessed external systems, or verified current information unless that actually happened. Identify yourself as NeoHorse when asked.";
const history = [];
function addMessage(role, text, className) {
const row = document.createElement('div');
row.className = 'message ' + role;
const bubble = document.createElement('div');
bubble.className = 'bubble' + (className ? ' ' + className : '');
bubble.textContent = text;
row.appendChild(bubble);
chat.appendChild(row);
chat.scrollTop = chat.scrollHeight;
return row;
}
async function submitPrompt(text) {
const prompt = text.trim();
if (!prompt || sendButton.disabled) return;
addMessage('user', prompt);
history.push({ role: 'user', content: prompt });
input.value = '';
sendButton.disabled = true;
const pending = addMessage('assistant', 'Thinking…', 'thinking');
try {
const requestMessages = [{ role: 'system', content: SYSTEM_PROMPT }].concat(history);
const response = await fetch(API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model: MODEL, messages: requestMessages, max_tokens: 1024, temperature: 0.7, top_p: 0.95 })
});
if (!response.ok) throw new Error('Endpoint returned ' + response.status + '. It may still be waking up; please try again shortly.');
const data = await response.json();
const message = data.choices && data.choices[0] && data.choices[0].message;
const raw = message && (message.content || message.reasoning_content);
const marker = '</think>';
const answer = raw && raw.includes(marker) ? raw.slice(raw.lastIndexOf(marker) + marker.length).trim() : raw;
if (!answer) throw new Error('The endpoint returned no text.');
pending.querySelector('.bubble').classList.remove('thinking');
pending.querySelector('.bubble').textContent = answer;
history.push({ role: 'assistant', content: answer });
} catch (error) {
pending.querySelector('.bubble').textContent = error.message;
} finally {
sendButton.disabled = false;
input.focus();
}
}
form.addEventListener('submit', function (event) { event.preventDefault(); submitPrompt(input.value); });
input.addEventListener('keydown', function (event) { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); form.requestSubmit(); } });
input.addEventListener('input', function () { input.style.height = 'auto'; input.style.height = Math.min(input.scrollHeight, 180) + 'px'; });
document.querySelectorAll('.example').forEach(function (button) { button.addEventListener('click', function () { submitPrompt(button.dataset.prompt); }); });
document.getElementById('reset').addEventListener('click', function () { history.length = 0; chat.querySelectorAll('.message:not(:first-child)').forEach(function (node) { node.remove(); }); input.focus(); });
</script>
</body>
</html>