Add LLM endpoints, web frontend, and rate limiting config
- Added OpenAI-compatible LLM endpoints to API backend - Introduced web frontend with Jinja2 templates and static assets - Implemented API proxy routes in web service - Added sample db.json data for items, users, orders, reviews, categories, llm_requests - Updated ADC and Helm configs for separate AI and standard rate limiting - Upgraded FastAPI, Uvicorn, and added httpx, Jinja2, python-multipart dependencies - Added API configuration modal and client-side JS for web app
This commit is contained in:
135
web/templates/llm.html
Normal file
135
web/templates/llm.html
Normal file
@@ -0,0 +1,135 @@
|
||||
{% extends "base.html" %} {% block title %}LLM Chat - API Demo{% endblock %} {%
|
||||
block content %}
|
||||
<div class="page-header">
|
||||
<h1>🤖 AI Chat - Videogame Expert</h1>
|
||||
<p>Chat with our AI assistant (Rate limited: 100 tokens/60s)</p>
|
||||
</div>
|
||||
|
||||
<div class="chat-container">
|
||||
<div class="chat-messages" id="chat-messages">
|
||||
<div class="system-message">
|
||||
Welcome! Ask me anything about videogames. I'm powered by the
|
||||
videogame-expert model.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-input-container">
|
||||
<textarea
|
||||
id="chat-input"
|
||||
placeholder="Type your message here..."
|
||||
rows="3"
|
||||
></textarea>
|
||||
<button id="send-btn" class="btn btn-primary" onclick="sendMessage()">
|
||||
Send Message
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="chat-info">
|
||||
<small>
|
||||
Model: <strong>videogame-expert</strong> | Status:
|
||||
<span id="status">Ready</span> | Rate Limit:
|
||||
<strong>100 tokens/60s</strong>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked@11.1.1/marked.min.js"></script>
|
||||
<script>
|
||||
const API_BASE = "/api";
|
||||
let isProcessing = false;
|
||||
|
||||
function addMessage(content, isUser = false) {
|
||||
const messagesDiv = document.getElementById("chat-messages");
|
||||
const messageDiv = document.createElement("div");
|
||||
messageDiv.className = isUser ? "user-message" : "assistant-message";
|
||||
|
||||
if (isUser) {
|
||||
messageDiv.textContent = content;
|
||||
} else {
|
||||
// Parse markdown for assistant messages
|
||||
messageDiv.innerHTML = marked.parse(content);
|
||||
}
|
||||
|
||||
messagesDiv.appendChild(messageDiv);
|
||||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||||
}
|
||||
|
||||
function setStatus(text, isError = false) {
|
||||
const statusSpan = document.getElementById("status");
|
||||
statusSpan.textContent = text;
|
||||
statusSpan.style.color = isError ? "#f44336" : "#4CAF50";
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
if (isProcessing) return;
|
||||
|
||||
const input = document.getElementById("chat-input");
|
||||
const prompt = input.value.trim();
|
||||
|
||||
if (!prompt) {
|
||||
alert("Please enter a message");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add user message
|
||||
addMessage(prompt, true);
|
||||
input.value = "";
|
||||
|
||||
// Set processing state
|
||||
isProcessing = true;
|
||||
document.getElementById("send-btn").disabled = true;
|
||||
setStatus("Processing...");
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/llm/chat`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompt: prompt,
|
||||
max_tokens: 150,
|
||||
temperature: 0.7,
|
||||
model: "videogame-expert",
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.detail || "API request failed");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Add assistant response
|
||||
addMessage(data.response, false);
|
||||
|
||||
// Show tokens used
|
||||
const tokensInfo = `Tokens used: ${data.tokens_used}`;
|
||||
const infoDiv = document.createElement("div");
|
||||
infoDiv.className = "system-message";
|
||||
infoDiv.textContent = tokensInfo;
|
||||
document.getElementById("chat-messages").appendChild(infoDiv);
|
||||
|
||||
setStatus("Ready");
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
addMessage(`Error: ${error.message}`, false);
|
||||
setStatus("Error", true);
|
||||
} finally {
|
||||
isProcessing = false;
|
||||
document.getElementById("send-btn").disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow Enter to send (Shift+Enter for newline)
|
||||
document
|
||||
.getElementById("chat-input")
|
||||
.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user