hypervision-chat / index.html
Yethikrishna's picture
Deploy HyperVision chat with tool calling
8a5f850 verified
Raw History Blame Contribute Delete
19.9 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HyperVision Chat — Tool Calling</title>
<style>
:root {
--bg: #0f1117;
--surface: #1a1d27;
--surface-2: #242836;
--border: #2e3345;
--text: #e4e6f0;
--text-dim: #8b90a5;
--accent: #6c5ce7;
--accent-hover: #7c6ef7;
--user-msg: #2d1b69;
--assistant-msg: #1a1d27;
--tool-msg: #1a2d1b;
--success: #2ecc71;
--warning: #f39c12;
--radius: 12px;
--font: 'Segoe UI', system-ui, -apple-system, sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--font);
background: var(--bg);
color: var(--text);
height: 100vh;
display: flex;
flex-direction: column;
}
/* Header */
header {
background: var(--surface);
border-bottom: 1px solid var(--border);
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
header h1 {
font-size: 20px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
header h1 span { color: var(--accent); }
header .subtitle {
font-size: 13px;
color: var(--text-dim);
}
header a {
color: var(--accent);
text-decoration: none;
}
header a:hover { text-decoration: underline; }
/* Main Layout */
.container {
display: flex;
flex: 1;
overflow: hidden;
}
/* Chat Area */
.chat-panel {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
}
.messages {
flex: 1;
overflow-y: auto;
padding: 20px 24px;
display: flex;
flex-direction: column;
gap: 16px;
}
.message {
max-width: 80%;
padding: 12px 16px;
border-radius: var(--radius);
line-height: 1.5;
font-size: 14px;
animation: fadeIn 0.2s ease;
}
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
.message.user {
align-self: flex-end;
background: var(--user-msg);
border: 1px solid #4a2d9e;
border-bottom-right-radius: 4px;
}
.message.assistant {
align-self: flex-start;
background: var(--assistant-msg);
border: 1px solid var(--border);
border-bottom-left-radius: 4px;
}
.message.tool-call {
align-self: flex-start;
background: var(--tool-msg);
border: 1px solid #1a4a2b;
font-family: 'Cascadia Code', 'Fira Code', monospace;
font-size: 12px;
max-width: 90%;
}
.message .label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 4px;
opacity: 0.7;
}
.message .tool-name { color: var(--success); }
.message .tool-result {
color: var(--text-dim);
white-space: pre-wrap;
word-break: break-all;
margin-top: 4px;
}
.message .error { color: #e74c3c; }
.typing-indicator {
align-self: flex-start;
display: none;
padding: 12px 16px;
background: var(--assistant-msg);
border: 1px solid var(--border);
border-radius: var(--radius);
border-bottom-left-radius: 4px;
}
.typing-indicator.active { display: flex; gap: 4px; }
.typing-indicator span {
width: 8px; height: 8px;
background: var(--text-dim);
border-radius: 50%;
animation: bounce 1.4s infinite ease-in-out;
}
.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0.6); }
40% { transform: scale(1); }
}
/* Input Area */
.input-area {
padding: 16px 24px;
background: var(--surface);
border-top: 1px solid var(--border);
display: flex;
gap: 12px;
flex-shrink: 0;
}
.input-area textarea {
flex: 1;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px 16px;
color: var(--text);
font-family: var(--font);
font-size: 14px;
resize: none;
outline: none;
min-height: 48px;
max-height: 120px;
transition: border-color 0.2s;
}
.input-area textarea:focus { border-color: var(--accent); }
.input-area textarea::placeholder { color: var(--text-dim); }
.input-area button {
background: var(--accent);
color: white;
border: none;
border-radius: 8px;
padding: 0 24px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
white-space: nowrap;
}
.input-area button:hover { background: var(--accent-hover); }
.input-area button:disabled {
background: var(--surface-2);
color: var(--text-dim);
cursor: not-allowed;
}
/* Sidebar */
.sidebar {
width: 280px;
background: var(--surface);
border-left: 1px solid var(--border);
padding: 20px;
overflow-y: auto;
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 16px;
}
.sidebar h3 {
font-size: 13px;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--text-dim);
}
.tool-list { display: flex; flex-direction: column; gap: 8px; }
.tool-item {
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 8px;
padding: 10px 12px;
font-size: 13px;
}
.tool-item .tool-name { color: var(--accent); font-weight: 600; }
.tool-item .tool-desc { color: var(--text-dim); font-size: 12px; margin-top: 2px; }
.sidebar .info-box {
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px;
font-size: 12px;
color: var(--text-dim);
line-height: 1.6;
}
.sidebar .info-box code {
background: var(--bg);
padding: 2px 6px;
border-radius: 4px;
font-size: 11px;
}
.examples {
display: flex;
flex-direction: column;
gap: 6px;
}
.examples button {
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 6px;
padding: 8px 12px;
color: var(--text-dim);
font-size: 12px;
cursor: pointer;
text-align: left;
transition: all 0.2s;
}
.examples button:hover {
border-color: var(--accent);
color: var(--text);
}
/* Footer */
footer {
background: var(--surface);
border-top: 1px solid var(--border);
padding: 8px 24px;
text-align: center;
font-size: 12px;
color: var(--text-dim);
flex-shrink: 0;
}
footer a { color: var(--accent); text-decoration: none; }
@media (max-width: 768px) {
.sidebar { display: none; }
.message { max-width: 95%; }
}
</style>
</head>
<body>
<header>
<div>
<h1>🤖 <span>HyperVision</span> Chat</h1>
<div class="subtitle">Tool Calling · <a href="https://myndlabs.tech" target="_blank">Myndlabs.tech</a></div>
</div>
<div class="subtitle">llama3.1-8B · jimmy.aikit.club</div>
</header>
<div class="container">
<!-- Chat Panel -->
<div class="chat-panel">
<div class="messages" id="messages">
<div class="message assistant">
Hello! I'm HyperVision AI. I can use tools to help you — try asking about weather, calculations, web searches, or time in any timezone.
</div>
</div>
<div class="typing-indicator" id="typingIndicator">
<span></span><span></span><span></span>
</div>
<div class="input-area">
<textarea id="userInput" rows="1" placeholder="Ask me anything..." onkeydown="handleKeyDown(event)"></textarea>
<button id="sendBtn" onclick="sendMessage()">Send</button>
</div>
</div>
<!-- Sidebar -->
<div class="sidebar">
<div>
<h3>Available Tools</h3>
<div class="tool-list">
<div class="tool-item">
<div class="tool-name">🌤️ get_weather</div>
<div class="tool-desc">Get current weather for any city</div>
</div>
<div class="tool-item">
<div class="tool-name">🔍 search_web</div>
<div class="tool-desc">Search the web for information</div>
</div>
<div class="tool-item">
<div class="tool-name">🧮 calculate</div>
<div class="tool-desc">Perform mathematical calculations</div>
</div>
<div class="tool-item">
<div class="tool-name">⏰ get_time</div>
<div class="tool-desc">Current time in any timezone</div>
</div>
</div>
</div>
<div>
<h3>Try Examples</h3>
<div class="examples">
<button onclick="setExample('What is the weather in Tokyo?')">🌤️ What is the weather in Tokyo?</button>
<button onclick="setExample('Calculate 2 + 2')">🧮 Calculate 2 + 2</button>
<button onclick="setExample('What time is it in India?')">⏰ What time is it in India?</button>
<button onclick="setExample('Search the web for HyperVision AI')">🔍 Search the web for HyperVision AI</button>
</div>
</div>
<div>
<h3>How It Works</h3>
<div class="info-box">
<ol style="padding-left: 16px;">
<li>You ask a question</li>
<li>The model decides which tool to use</li>
<li>The tool executes and returns results</li>
<li>The model formulates a response</li>
</ol>
<br/>
<strong>API:</strong> <code>https://jimmy.aikit.club/v1</code>
</div>
</div>
</div>
</div>
<footer>
<a href="https://myndlabs.tech" target="_blank">Myndlabs.tech</a> — Enterprise-grade AI solutions
</footer>
<script>
// ── Configuration ──────────────────────────────────────────────────────
const API_BASE = 'https://jimmy.aikit.club/v1';
const API_KEY = 'tarun-mykey';
const MODEL = 'llama3.1-8B';
const MAX_TOKENS = 2048;
const SYSTEM_PROMPT = 'You are HyperVision AI, an intelligent assistant by Myndlabs.tech. You have access to tools. Use them when the user asks for something the tool can do. Respond in a helpful, concise manner.';
const TOOLS = [
{
type: 'function',
function: {
name: 'get_weather',
description: 'Get current weather for a city',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name, e.g. Tokyo, London, New York' },
unit: { type: 'string', enum: ['celsius', 'fahrenheit'], description: 'Temperature unit' },
},
required: ['city'],
},
},
},
{
type: 'function',
function: {
name: 'search_web',
description: 'Search the web for information',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
max_results: { type: 'integer', description: 'Number of results (max 10)' },
},
required: ['query'],
},
},
},
{
type: 'function',
function: {
name: 'calculate',
description: 'Perform a mathematical calculation',
parameters: {
type: 'object',
properties: {
expression: { type: 'string', description: 'Math expression, e.g. 2 + 2' },
},
required: ['expression'],
},
},
},
{
type: 'function',
function: {
name: 'get_time',
description: 'Get current time for a timezone',
parameters: {
type: 'object',
properties: {
timezone: { type: 'string', description: "Timezone, e.g. 'Asia/Kolkata', 'America/New_York'" },
},
required: ['timezone'],
},
},
},
];
// ── Tool Execution ─────────────────────────────────────────────────────
function executeTool(name, args) {
switch (name) {
case 'get_weather': {
const city = args.city || 'unknown';
const unit = args.unit || 'celsius';
const temp = unit === 'celsius' ? 28 : 82;
return JSON.stringify({
city,
temperature: temp,
condition: 'partly cloudy',
humidity: 65,
unit,
});
}
case 'search_web': {
const query = args.query || '';
const maxResults = Math.min(args.max_results || 5, 10);
const results = [];
for (let i = 0; i < maxResults; i++) {
results.push({ title: `Result ${i + 1} for '${query}'`, url: `https://example.com/result-${i + 1}` });
}
return JSON.stringify({ query, results, total_results: maxResults });
}
case 'calculate': {
try {
const result = Function('"use strict"; return (' + args.expression + ')')();
return JSON.stringify({ expression: args.expression, result });
} catch (e) {
return JSON.stringify({ expression: args.expression, error: e.message });
}
}
case 'get_time': {
const tz = args.timezone || 'UTC';
try {
const now = new Date();
const options = { timeZone: tz, weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', timeZoneName: 'short' };
const timeStr = new Intl.DateTimeFormat('en-US', options).format(now);
return JSON.stringify({ timezone: tz, time: timeStr });
} catch (e) {
return JSON.stringify({ timezone: tz, error: 'Invalid timezone', time: new Date().toUTCString() });
}
}
default:
return JSON.stringify({ error: `Unknown tool: ${name}` });
}
}
// ── Chat State ─────────────────────────────────────────────────────────
let messages = [
{ role: 'system', content: SYSTEM_PROMPT }
];
const messagesEl = document.getElementById('messages');
const userInput = document.getElementById('userInput');
const sendBtn = document.getElementById('sendBtn');
const typingIndicator = document.getElementById('typingIndicator');
// ── UI Functions ───────────────────────────────────────────────────────
function addMessage(role, content, extraClass = '') {
const div = document.createElement('div');
div.className = `message ${role} ${extraClass}`.trim();
div.textContent = content;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function addToolCallMessage(toolName, args, result) {
const div = document.createElement('div');
div.className = 'message tool-call';
div.innerHTML = `
<div class="label">🔧 Tool Call: <span class="tool-name">${toolName}</span></div>
<div class="tool-result">Arguments: ${JSON.stringify(args, null, 2)}</div>
<div class="tool-result">Result: ${result}</div>
`;
messagesEl.appendChild(div);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
function setLoading(loading) {
typingIndicator.classList.toggle('active', loading);
sendBtn.disabled = loading;
userInput.disabled = loading;
}
function setExample(text) {
userInput.value = text;
userInput.focus();
}
function handleKeyDown(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
}
// ── API Call ───────────────────────────────────────────────────────────
async function chatWithTools(currentMessages) {
const res = await fetch(`${API_BASE}/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: MODEL,
messages: currentMessages,
tools: TOOLS,
max_tokens: MAX_TOKENS,
temperature: 0.7,
}),
});
if (!res.ok) {
const err = await res.text();
throw new Error(`API Error (${res.status}): ${err}`);
}
return await res.json();
}
// ── Send Message ───────────────────────────────────────────────────────
async function sendMessage() {
const text = userInput.value.trim();
if (!text) return;
// Add user message
addMessage('user', text);
messages.push({ role: 'user', content: text });
userInput.value = '';
setLoading(true);
try {
// Tool calling loop (max 5 iterations)
let toolCallCount = 0;
while (toolCallCount < 5) {
const data = await chatWithTools(messages);
const choice = data.choices?.[0];
const msg = choice?.message;
if (!msg) {
addMessage('assistant', 'No response from API.');
break;
}
const toolCalls = msg.tool_calls;
if (toolCalls && toolCalls.length > 0) {
// Add assistant message with tool calls
messages.push(msg);
toolCallCount++;
// Execute each tool call
for (const tc of toolCalls) {
const toolName = tc.function.name;
const toolArgs = JSON.parse(tc.function.arguments);
const toolResult = executeTool(toolName, toolArgs);
addToolCallMessage(toolName, toolArgs, toolResult);
messages.push({
role: 'tool',
tool_call_id: tc.id,
content: toolResult,
});
}
// Continue the loop
continue;
}
// No tool calls — show the text response
const content = msg.content || 'No text response.';
addMessage('assistant', content);
messages.push({ role: 'assistant', content });
break;
}
if (toolCallCount >= 5) {
addMessage('assistant', 'Tool calling loop exceeded maximum iterations.');
}
} catch (err) {
addMessage('assistant', `Error: ${err.message}`);
} finally {
setLoading(false);
}
}
// Auto-resize textarea
userInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 120) + 'px';
});
</script>
</body>
</html>