From the uDemy course on LLM engineering.
https://www.udemy.com/course/llm-engineering-master-ai-and-large-language-models
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
7.3 KiB
222 lines
7.3 KiB
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>WebSocket Chat with Markdown Streaming</title> |
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap" rel="stylesheet"> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> |
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> |
|
<style> |
|
body { |
|
font-family: 'Poppins', sans-serif; |
|
background-color: #e0eafc; |
|
text-align: center; |
|
padding: 20px; |
|
} |
|
h1 { |
|
color: #333; |
|
font-size: 2em; |
|
margin-bottom: 20px; |
|
} |
|
.input-group { |
|
position: relative; |
|
margin: 10px 0; |
|
} |
|
.input-group i { |
|
position: absolute; |
|
left: 10px; |
|
top: 50%; |
|
transform: translateY(-50%); |
|
color: #999; |
|
} |
|
input { |
|
padding: 10px; |
|
padding-left: 30px; /* Space for the icon */ |
|
font-size: 1em; |
|
border: 1px solid #ccc; |
|
border-radius: 20px; |
|
width: 300px; |
|
margin: 5px 0; |
|
} |
|
button { |
|
padding: 10px 20px; |
|
font-size: 1em; |
|
background: linear-gradient(to right, #ff7e5f, #feb47b); |
|
color: white; |
|
border: none; |
|
border-radius: 20px; |
|
cursor: pointer; |
|
margin-top: 10px; |
|
} |
|
button:hover { |
|
background: linear-gradient(to right, #ff9170, #fec581); |
|
} |
|
#messages { |
|
list-style-type: none; |
|
padding: 0; |
|
margin-top: 20px; |
|
text-align: left; |
|
max-width: 1200px; |
|
margin: 0 auto; |
|
background-color: #fff; |
|
border-radius: 20px; |
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1); |
|
overflow-y: auto; |
|
max-height: 500px; |
|
padding: 10px; |
|
} |
|
#messages li { |
|
padding: 10px; |
|
margin: 5px 0; |
|
border-radius: 10px; |
|
background-color: #d1e7dd; |
|
animation: fadeIn 0.5s; |
|
} |
|
@keyframes fadeIn { |
|
from { opacity: 0; } |
|
to { opacity: 1; } |
|
} |
|
#status { |
|
margin-top: 20px; |
|
font-size: 1em; |
|
color: #007bff; |
|
} |
|
.error { |
|
color: red; |
|
font-weight: bold; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<h1>WebSocket Chat with Markdown Streaming</h1> |
|
<div class="input-group"> |
|
<i class="fas fa-link"></i> |
|
<input type="text" id="urlInput" placeholder="Enter URL"> |
|
</div> |
|
<div class="input-group"> |
|
<i class="fas fa-align-left"></i> |
|
<input type="text" id="descriptionInput" placeholder="Enter Description (optional)"> |
|
</div> |
|
<div class="input-group"> |
|
<i class="fas fa-globe"></i> |
|
<input type="text" id="siteTypeInput" placeholder="Enter Site Type (optional)"> |
|
</div> |
|
<button onclick="sendMessage()">Send</button> |
|
<ul id="messages"></ul> |
|
<div id="status">✅ Ready to chat!</div> |
|
|
|
<script> |
|
let ws; |
|
let currentMessageElement = null; |
|
let buffer = ''; // Buffer to accumulate chunks |
|
|
|
// Connect to WebSocket server |
|
function connectWebSocket() { |
|
ws = new WebSocket("ws://127.0.0.1:8912/socket/ws"); |
|
|
|
ws.onopen = () => { |
|
console.log("WebSocket connection established."); |
|
updateStatus("✅ Connected to server."); |
|
}; |
|
|
|
ws.onmessage = (event) => { |
|
const data = JSON.parse(event.data); |
|
|
|
if (data.type === "status") { |
|
updateStatus(data.message); |
|
} else if (data.type === "message") { |
|
handleStreamedMessage(data.message, data.is_complete); |
|
} |
|
}; |
|
|
|
ws.onerror = (error) => { |
|
console.error("WebSocket error:", error); |
|
updateStatus("Oops! 😢 An error occurred. Please try again.", true); |
|
}; |
|
|
|
ws.onclose = () => { |
|
console.log("WebSocket connection closed."); |
|
updateStatus("Connection closed. Click 'Send' to reconnect.", true); |
|
}; |
|
} |
|
|
|
// Update status message |
|
function updateStatus(message, isError = false) { |
|
const statusDiv = document.getElementById("status"); |
|
if (isError) { |
|
message = "Oops! 😢 " + message; |
|
} else { |
|
message = "✅ " + message; |
|
} |
|
statusDiv.innerHTML = isError ? `<div class="error">${message}</div>` : message; |
|
} |
|
|
|
// Handle streamed messages |
|
function handleStreamedMessage(chunk, isComplete) { |
|
const messages = document.getElementById("messages"); |
|
buffer += chunk; // Append chunk to buffer |
|
|
|
// Split buffer by newline characters |
|
const parts = buffer.split(/\r?\n/); |
|
buffer = parts.pop(); // Keep the last part in buffer if it doesn't end with newline |
|
|
|
// Render each part as Markdown |
|
for (let part of parts) { |
|
if (!currentMessageElement) { |
|
currentMessageElement = document.createElement("li"); |
|
messages.appendChild(currentMessageElement); |
|
} |
|
currentMessageElement.innerHTML += marked.parse(part + ' '); // Add a space to prevent Markdown issues |
|
} |
|
|
|
// If the message is complete, render the remaining buffer |
|
if (isComplete && buffer !== '') { |
|
if (!currentMessageElement) { |
|
currentMessageElement = document.createElement("li"); |
|
messages.appendChild(currentMessageElement); |
|
} |
|
currentMessageElement.innerHTML += marked.parse(buffer); |
|
buffer = ''; |
|
currentMessageElement = null; |
|
} |
|
|
|
// Auto-scroll to the bottom |
|
messages.scrollTop = messages.scrollHeight; |
|
} |
|
|
|
// Send message to WebSocket server |
|
function sendMessage() { |
|
const url = document.getElementById("urlInput").value.trim(); |
|
const description = document.getElementById("descriptionInput").value.trim(); |
|
const siteType = document.getElementById("siteTypeInput").value.trim(); |
|
|
|
if (!url) { |
|
updateStatus("Please enter a valid URL.", true); |
|
return; |
|
} |
|
|
|
if (!ws || ws.readyState !== WebSocket.OPEN) { |
|
connectWebSocket(); |
|
} |
|
|
|
const data = { |
|
url: url, |
|
description: description, |
|
siteType: siteType, |
|
crawlType: "normal" |
|
}; |
|
|
|
ws.send(JSON.stringify(data)); |
|
|
|
// Clear input fields |
|
document.getElementById("urlInput").value = ""; |
|
document.getElementById("descriptionInput").value = ""; |
|
document.getElementById("siteTypeInput").value = ""; |
|
} |
|
|
|
// Initialize WebSocket connection on page load |
|
connectWebSocket(); |
|
</script> |
|
</body> |
|
</html> |