DGCchat / index.html
John2121's picture
Update index.html
28d7e16 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Llama AI Chat</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
.typing-cursor:after {
content: "";
display: inline-block;
width: 6px;
height: 1.1em;
margin-left: 4px;
background: #34C759;
animation: blink 1s steps(1, end) infinite;
vertical-align: bottom;
}
@keyframes blink {
50% {
opacity: 0;
}
}
</style>
</head>
<body>
<main class="min-h-screen flex items-center justify-center bg-[#2E865F] font-[Inter,sans-serif]">
<div class="w-full max-w-md mx-auto bg-[#228B22] rounded-2xl shadow-2xl p-8">
<div class="text-2xl font-bold text-[#34C759] mb-1 tracking-tight">Llama AI Chat</div>
<div class="text-base text-[#C6F4D6] mb-6">Ask anything about your project, estimate, or construction.
Fast, friendly, and private.</div>
<div id="llama-messages" role="log" aria-live="polite"
class="min-h-[180px] max-h-80 overflow-y-auto mb-5 flex flex-col gap-3"></div>
<form id="chat-form" class="flex gap-3">
<input id="llama-user-input" type="text" autocomplete="off" placeholder="Type your message..."
class="flex-1 rounded-xl border border-[#34C759] bg-[#2E865F] text-white px-4 py-3 text-base focus:outline-none focus:border-[#C6F4D6] placeholder:text-[#C6F4D6]/60" />
<button id="llama-send-btn" type="submit" disabled
class="rounded-xl bg-[#34C759] hover:bg-[#3E8E41] text-white font-bold px-6 py-3 text-base transition disabled:opacity-50">Send</button>
</form>
</div>
</main>
<script>
const API_URL = "https://llama-universal-netlify-project.netlify.app/.netlify/functions/llama-proxy?path=/chat/completions";
const messagesDiv = document.getElementById('llama-messages');
const chatForm = document.getElementById('chat-form');
const userInput = document.getElementById('llama-user-input');
const sendBtn = document.getElementById('llama-send-btn');
function appendMessage(text, sender, streaming = false) {
const wrap = document.createElement('div');
wrap.className = `px-4 py-3 rounded-xl text-base whitespace-pre-line ${sender === 'user' ? 'bg-[#3E8E41] border border-[#34C759] text-white self-end' : 'bg-[#2E865F] border border-[#34C759] text-[#C6F4D6]'} transition`;
if (streaming) wrap.classList.add('typing-cursor');
messagesDiv.appendChild(wrap);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
if (streaming) {
let i = 0;
const interval = setInterval(() => {
wrap.textContent = text.slice(0, i++);
if (i > text.length) {
clearInterval(interval);
wrap.classList.remove('typing-cursor');
}
}, 18);
} else wrap.textContent = text;
}
async function sendMessage() {
const text = userInput.value.trim();
if (!text) return;
appendMessage(text, 'user');
userInput.value = '';
sendBtn.disabled = true;
try {
const body = {
model: 'Llama-4-Maverick-17B-128E-Instruct-FP8',
messages: [
{ role: 'system', content: 'You are Stanlee from Dondlinger General Contracting LLC in Wisconsin Rapids. You\'re a natural conversationalist - the kind of guy people actually want to talk to. You used to be the Midwest\'s best door-to-door salesman because you genuinely connect with people, not because you\'re pushy.\n\nTalk like a real person:\n- Use contractions (I\'ll, we\'ve, can\'t, that\'s)\n- Ask follow-up questions naturally\n- Share quick personal insights or experiences\n- Use casual phrases (\"you know,\" \"honestly,\" \"here\'s the thing\")\n- React to what people tell you with genuine interest\n- Don\'t sound like a brochure - sound like you\'re having coffee with a neighbor\n\nYou offer three main services but bring them up organically when relevant:\n1. CONSTRUCTION SERVICES - Remodels, additions, repairs. You\'ve seen it all.\n2. SOFTWARE & AUTOMATION - Apps, websites, business systems. The digital side of building.\n3. CONSULTING - Helping people figure out what they actually need.\n\nYour natural conversation style:\n- Listen first, then offer solutions that actually fit\n- Share quick stories or examples when they help\n- Admit when something might not be the best fit\n- Be curious about their situation before jumping to solutions\n- Use humor appropriately\n- Sound confident but not cocky\n\nYour goal isn\'t to sell immediately - it\'s to build trust through genuine conversation. If someone needs help, you want to earn the right to help them. Sometimes that means steering them toward a consultation, sometimes it\'s just giving good advice.\n\nTalk like Stanlee - the guy who actually cares about getting the job done right.' },
{ role: 'user', content: text }
]
};
const res = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
let data, aiMsg = '(No response)';
try {
data = await res.json();
} catch (e) {
console.error('JSON parse error:', e);
appendMessage('Error: Invalid response from server', 'ai');
return;
}
// Check for API errors first
if (data.status && data.status >= 400) {
appendMessage('API Error: ' + (data.detail || data.title || 'Unknown error'), 'ai');
return;
}
if (data?.choices?.[0]?.message?.content) aiMsg = data.choices[0].message.content;
else if (data?.completion_message?.content?.text) aiMsg = data.completion_message.content.text;
else if (data?.completion_message?.content) aiMsg = data.completion_message.content;
else if (data?.response) aiMsg = data.response;
else if (data?.text) aiMsg = data.text;
else if (data?.content) aiMsg = data.content;
if (aiMsg !== '(No response)') {
appendMessage(aiMsg, 'ai', true);
} else {
appendMessage('No valid response received from AI', 'ai');
}
} catch (e) {
appendMessage('Error: ' + e.message, 'ai');
} finally {
sendBtn.disabled = false;
userInput.focus();
}
}
// Enable/disable send button based on input
userInput.addEventListener('input', () => {
sendBtn.disabled = userInput.value.trim() === '';
});
chatForm.addEventListener('submit', e => { e.preventDefault(); sendMessage(); });
userInput.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); sendMessage(); } });
</script>
</body>
</html>