Dashboards met AI: Maak een eenvoudige prompt-UI voor LlamaIndex
geschreven door Vincent de Vries op 14 april 2025 15:06:14 CEST
Inleiding
In een eerder artikel "Dashboards met AI: Hoe je een on-prem LLM integreert met Grafana Mimir", hebben we gezien hoe je Grafana Metrics kunt integreren met een LLM met behulp van LlamaIndex. Dit stelde ons in staat om direct vragen te stellen aan onze LLM over onze metrics, maar wie wil er nou voor elke prompt een Python script uitvoeren?
In dit artikel gaan we een stap verder door een minimale web based UI toe te voegen om de interactie met de LLM en LlamaIndex wat makkelijker te maken. De UI elimineert de noodzaak om Python scripts voor elke query uit te voeren, met het resultaat dat het minder omslachtig en gebruiksvriendelijker wordt. Het is wel belangrijk om te benadrukken dat deze implementatie puur bedoeld is voor experimenteel gebruik en niet geschikt is voor productiegebruik.
Prerequisites
Mocht je nog geen omgeving hebben geïnstalleerd met LlamaIndex dan is het aan te raden eerst LlamaIndex omgeving installeren zoals beschreven in het vorige artikel: Dashboards met AI: Hoe je een on-prem LLM integreert met Grafana Mimir.
De web based UI bouwen
Eerst moet je Flask installeren in je omgeving, zodat we Python scripts kunnen draaien als een webserver.
pip install flask
Vervolgens moeten we een map genaamd "templates" aanmaken. Dit is de map waar Flask zoekt naar de HTML-bestanden die weergegeven moeten worden. Maak deze map aan in je home directory, zodat de Python-scripts eenvoudig de relatieve paden naar je LlamaIndex-indexen kunnen vinden.
/home/username/
├── web_llamaindex.py
├── templates/
│ ├── index.html
Maak in je user home een bestand genaamd web_llamaindex.py en voeg de onderstaande code toe:
from flask import Flask, render_template, request, jsonify
import os
from llama_index.core import load_index_from_storage, StorageContext, Settings
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
app = Flask(__name__)
# Force multi-threading
os.environ["OMP_NUM_THREADS"] = str(os.cpu_count())
os.environ["MKL_NUM_THREADS"] = str(os.cpu_count())
os.environ["OPENBLAS_NUM_THREADS"] = str(os.cpu_count())
os.environ["NUMEXPR_NUM_THREADS"] = str(os.cpu_count())
# Set up LlamaIndex with Ollama
Settings.llm = Ollama(model="mistral", request_timeout=120, num_threads=0) # Use all CPU cores
Settings.embed_model = HuggingFaceEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
# Load storage context before loading the index
storage_context = StorageContext.from_defaults(persist_dir="./mimir_index")
# Load existing index with storage context
index = load_index_from_storage(storage_context=storage_context)
# Create a retriever from the index
retriever = index.as_retriever()
# Create query engine using the retriever
query_engine = RetrieverQueryEngine(retriever=retriever)
@app.route('/')
def home():
return render_template('index.html')
@app.route('/query', methods=['POST'])
def query():
data = request.get_json()
user_query = data.get("query", "")
response = query_engine.query(user_query)
return jsonify({"query": user_query, "response": str(response)})
if __name__ == '__main__':
app.run(host='0.0.0.0', port='5000', debug=True)
De code in web_llamaindex.py hierboven zet een API op voor de indexpagina.
Om indexsidan te maken, maak een nieuw bestand aan met de naam index.html in de map templates. Plak vervolgens de onderstaande code in index.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chatbot</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f8f9fa;
color: #333;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
height: 100vh;
justify-content: center;
}
.chat-container {
width: 40%;
max-width: 600px;
background: white;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
display: flex;
flex-direction: column;
height: 80vh;
overflow: hidden;
}
.chat-box {
flex-grow: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
border-bottom: 1px solid #ddd;
}
.message {
padding: 10px;
border-radius: 8px;
margin-bottom: 10px;
max-width: 80%;
word-wrap: break-word;
}
.user-message {
background: #007bff;
color: white;
align-self: flex-end;
}
.bot-message {
background: #e9ecef;
align-self: flex-start;
}
.input-container {
display: flex;
padding-top: 10px;
}
input {
flex-grow: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
background: white;
color: #333;
outline: none;
}
button {
padding: 10px 20px;
margin-left: 10px;
border: none;
background: #007bff;
color: white;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background: #0056b3;
}
/* Spinner */
.spinner {
display: none;
align-self: center;
width: 24px;
height: 24px;
border: 3px solid #ddd;
border-top: 3px solid #007bff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-top: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="chat-container">
<div class="chat-box" id="chatBox"></div>
<div class="spinner" id="spinner"></div>
<div class="input-container">
<input type="text" id="userInput" placeholder="Type a message..." onkeypress="handleKeyPress(event)">
<button onclick="sendMessage()">Send</button>
</div>
</div>
<script>
function sendMessage() {
let userInput = document.getElementById("userInput");
let message = userInput.value.trim();
if (message === "") return;
let chatBox = document.getElementById("chatBox");
let spinner = document.getElementById("spinner");
let userMsgDiv = document.createElement("div");
userMsgDiv.className = "message user-message";
userMsgDiv.textContent = message;
chatBox.appendChild(userMsgDiv);
userInput.value = "";
chatBox.scrollTop = chatBox.scrollHeight;
spinner.style.display = "block"; // Show spinner
fetch('/query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: message })
})
.then(response => response.json())
.then(data => {
let botMsgDiv = document.createElement("div");
botMsgDiv.className = "message bot-message";
botMsgDiv.textContent = data.response;
chatBox.appendChild(botMsgDiv);
chatBox.scrollTop = chatBox.scrollHeight;
})
.finally(() => {
spinner.style.display = "none"; // Hide spinner
});
}
function handleKeyPress(event) {
if (event.key === "Enter") {
sendMessage();
}
}
</script>
</body>
</html>
Het enige wat je nu nog hoeft te doen, is de server starten:
python3 web_llamaindex.py
Als alles goed is gegaan, zou je een output moeten zien die er ongeveer zo uitziet:
Om het webgebaseerde interface te gebruiken, ga dan in je browser naar het IP-adres of de FQDN op poort 5000. Bijvoorbeeld: http://myhost.local:5000
Je hebt nu een eenvoudige en nette interface die je kunt gebruiken voor je prompts:
Samenvatting
In deze blogpost hebben we een volgende stap gezet in het combineren van Grafana met AI door een eenvoudige, webgebaseerde interface te bouwen om te communiceren met een lokale LLM via LlamaIndex. Met behulp van Flask konden we een gebruiksvriendelijke UI-oplossing creëren waarmee je rechtstreeks vanuit de browser vragen kunt stellen – zonder telkens een Python-script te hoeven uitvoeren.
Wil je met ons over Grafana praten?
Je bent misschien ook geïnteresseerd in
Gerelateerde blogs

Dashboards met AI: Hoe je een on-prem LLM integreert met Grafana Mimir

Nog geen reacties
Vertel ons wat je denkt