Initial implementation of the chat application with MariaDB and ArangoDB integration, including Docker setup and web interface.

This commit is contained in:
st-server
2026-01-06 15:26:14 +01:00
parent f0e2fd294b
commit b3f6ecb1bc
13 changed files with 296 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
data/arangodb/
data/mariadb/

6
api/Dockerfile Normal file
View File

@@ -0,0 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]

70
api/app.py Normal file
View File

@@ -0,0 +1,70 @@
from flask import Flask, request, jsonify
import mysql.connector
import requests
import os
from arango import ArangoClient
app = Flask(__name__)
# MariaDB Verbindung
def get_db():
return mysql.connector.connect(
host=os.environ['DB_HOST'],
user=os.environ['DB_USER'],
password=os.environ['DB_PASSWORD'],
database=os.environ['DB_NAME']
)
# ArangoDB Verbindung
def get_arango():
client = ArangoClient(hosts=os.environ['ARANGO_URL'])
sys_db = client.db('_system', username=os.environ['ARANGO_USER'], password=os.environ['ARANGO_PASSWORD'])
if not sys_db.has_database('llm_facts'):
sys_db.create_database('llm_facts')
db = client.db('llm_facts', username=os.environ['ARANGO_USER'], password=os.environ['ARANGO_PASSWORD'])
if not db.has_collection('facts'):
db.create_collection('facts')
return db.collection('facts')
# Skill-Trigger
def extract_fact(text):
triggers = ["merke dir", "notiere", "speichere"]
for t in triggers:
if text.lower().startswith(t):
fact = text[len(t):].strip()
return fact
return None
@app.route("/chat", methods=["POST"])
def chat():
data = request.json
project = data.get("project")
message = data.get("message")
# Lernbefehl
fact = extract_fact(message)
if fact:
arango = get_arango()
arango.insert({
"_raw": message,
"_source": project,
"content": fact,
"tags": ["skill:merke"],
"relations": []
})
return jsonify({"reply": f"Fakt gespeichert: {fact}"})
# Chat speichern in MariaDB
db = get_db()
cursor = db.cursor()
cursor.execute("INSERT INTO chats (project, user_input) VALUES (%s,%s)", (project, message))
db.commit()
cursor.close()
db.close()
# Anfrage an LLM
resp = requests.post(f"{os.environ['LM_API_URL']}/generate", json={"prompt": message})
return jsonify({"reply": resp.json()})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)

4
api/requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
flask
mysql-connector-python
requests
python-arango

96
docker-compose.yml Normal file
View File

@@ -0,0 +1,96 @@
services:
myki-mariadb:
image: mariadb:11
container_name: myki-mariadb
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: llm_projects
MYSQL_USER: llmuser
MYSQL_PASSWORD: llmpassword
ports:
- "3306:3306"
volumes:
- ./data/mariadb:/var/lib/mysql
- ./initdb:/docker-entrypoint-initdb.d # SQL-Dateien werden hier automatisch beim ersten Start ausgeführt
myki-phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: myki-phpmyadmin
restart: always
environment:
PMA_HOST: mariadb
PMA_USER: llmuser
PMA_PASSWORD: llmpassword
ports:
- "8081:80"
depends_on:
- myki-mariadb
myki-arangodb:
image: arangodb:3.12
container_name: myki-arangodb
restart: always
environment:
ARANGO_ROOT_PASSWORD: rootpassword
ports:
- "8529:8529"
volumes:
- ./data/arangodb:/var/lib/arangodb3
- ./initdb-arango:/docker-entrypoint-initdb.d # Initialisierung
# myki-lmstudio:
# image: ghcr.io/nomic-ai/ministral-3-14b-instruct-2512
# container_name: myki-lmstudio
# environment:
# LMSTUDIO_API: "1"
# ports:
# - "8080:8080"
# volumes:
# - ./lmstudio/models:/models
myki-api:
build: ./api
container_name: myki-api
depends_on:
- myki-mariadb
- myki-arangodb
#- myki-lmstudio
environment:
DB_HOST: mariadb
DB_USER: llmuser
DB_PASSWORD: llmpassword
DB_NAME: llm_projects
#LM_API_URL: http://lmstudio:8080
LM_API_URL: http://host.docker.internal:1234
ARANGO_URL: http://arangodb:8529
ARANGO_USER: root
ARANGO_PASSWORD: rootpassword
ports:
- "5000:5000"
myki-worker:
build: ./worker
container_name: myki-worker
depends_on:
- myki-api
- myki-mariadb
- myki-arangodb
#- lmstudio
environment:
DB_HOST: mariadb
DB_USER: llmuser
DB_PASSWORD: llmpassword
DB_NAME: llm_projects
LM_API_URL: http://lmstudio:8080
ARANGO_URL: http://arangodb:8529
ARANGO_USER: root
ARANGO_PASSWORD: rootpassword
web:
build: ./web
container_name: llm_web
depends_on:
- myki-api
ports:
- "3001:3000"

15
initdb/init.sql Normal file
View File

@@ -0,0 +1,15 @@
CREATE TABLE chats (
id INT AUTO_INCREMENT PRIMARY KEY,
project VARCHAR(255),
user_input TEXT,
llm_output TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE feedback (
id INT AUTO_INCREMENT PRIMARY KEY,
chat_id INT,
rating ENUM('positive','negative'),
comment TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

2
web/Dockerfile Normal file
View File

@@ -0,0 +1,2 @@
FROM nginx:alpine
COPY . /usr/share/nginx/html

17
web/index.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Projekt-LLM Chat</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="chat-container">
<input id="project" placeholder="Projektname">
<div id="messages"></div>
<input id="message" placeholder="Schreibe eine Nachricht">
<button onclick="sendMessage()">Senden</button>
</div>
<script src="script.js"></script>
</body>
</html>

12
web/scripts.js Normal file
View File

@@ -0,0 +1,12 @@
async function sendMessage() {
const project = document.getElementById('project').value;
const message = document.getElementById('message').value;
const res = await fetch('http://localhost:5000/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({project, message})
});
const data = await res.json();
const messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML += `<p><b>Du:</b> ${message}</p><p><b>LLM:</b> ${data.reply}</p>`;
}

4
web/style.css Normal file
View File

@@ -0,0 +1,4 @@
#chat-container { width: 500px; margin: auto; }
#messages { border: 1px solid #ccc; height: 400px; overflow-y: scroll; padding: 5px; margin-bottom: 5px; }
input { width: 80%; margin-bottom: 5px; }
button { width: 18%; }

6
worker/Dockerfile Normal file
View File

@@ -0,0 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY worker.py .
CMD ["python", "worker.py"]

4
worker/requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
mysql-connector-python
requests
python-arango
schedule

58
worker/worker.py Normal file
View File

@@ -0,0 +1,58 @@
import os
import mysql.connector
import requests
from arango import ArangoClient
import schedule
import time
# MariaDB
def get_db():
return mysql.connector.connect(
host=os.environ['DB_HOST'],
user=os.environ['DB_USER'],
password=os.environ['DB_PASSWORD'],
database=os.environ['DB_NAME']
)
# ArangoDB
def get_arango():
client = ArangoClient(hosts=os.environ['ARANGO_URL'])
db = client.db('llm_facts', username=os.environ['ARANGO_USER'], password=os.environ['ARANGO_PASSWORD'])
return db.collection('facts')
# Analysiere neue Chats und überführe sie in ArangoDB
def analyze_chats():
db = get_db()
cursor = db.cursor(dictionary=True)
cursor.execute("SELECT * FROM chats WHERE llm_output IS NULL")
chats = cursor.fetchall()
arango = get_arango()
for chat in chats:
# Anfrage an LLM zur Analyse / Fakt-Extraktion
resp = requests.post(f"{os.environ['LM_API_URL']}/generate", json={"prompt": f"Extrahiere Fakten und Kontext aus diesem Text:\n{chat['user_input']}"})
fact = resp.json()
# Speichern in ArangoDB
arango.insert({
"_raw": chat['user_input'],
"_source": chat['project'],
"content": fact,
"tags": ["auto"],
"relations": []
})
# Update llm_output in MariaDB
cursor.execute("UPDATE chats SET llm_output=%s WHERE id=%s", (fact, chat['id']))
db.commit()
cursor.close()
db.close()
# Alle 30 Sekunden ausführen
schedule.every(30).seconds.do(analyze_chats)
while True:
schedule.run_pending()
time.sleep(1)