Compare commits

...

10 Commits

Author SHA1 Message Date
a7dfbe2ebd add markdown 2024-10-31 16:33:05 +01:00
4d2e5332d8 fix 2024-10-31 16:08:07 +01:00
192070333c fix 2024-10-31 15:52:28 +01:00
cd00e28fd7 add complete chat history 2024-10-31 15:02:07 +01:00
08b5b62442 simple messages works 2024-10-31 14:45:19 +01:00
78c854439a add model streaming 2024-10-30 19:21:33 +01:00
0b8028635a delete config - create example 2024-10-30 19:02:52 +01:00
9679e5efaf change Title 2024-10-30 17:35:32 +01:00
226e9133ed fix 2024-10-30 17:33:28 +01:00
dc80804241 checkpoint chat 2024-10-30 17:17:02 +01:00
6 changed files with 211 additions and 61 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/.env.server /.env.server
/nginx.conf

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat Interface</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div class="main-container">
<!-- Linke Seitenleiste für Chat-Sessions -->
<div class="sidebar" id="chatSidebar">
<h3>Gespeicherte Chats</h3>
<button id="newChatButton">Neuer Chat</button>
<div id="chatList"></div>
</div>
<!-- Chatbereich -->
<div class="chat-container">
<div class="chat-box" id="chatBox">
<!-- Nachrichten werden hier angezeigt -->
</div>
<div class="chat-input-container">
<textarea id="chatInput" class="chat-input" placeholder="Schreibe eine Nachricht..."></textarea>
<button id="sendButton" class="chat-send-button">Senden</button>
</div>
</div>
</div>
<script src="js/chat.js"></script>
</body>
</html>

View File

@ -4,7 +4,7 @@ body {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100vh; /* height: 100vh;*/
margin: 0; margin: 0;
background-color: #e9ecef; background-color: #e9ecef;
} }
@ -89,6 +89,7 @@ button:hover {
/* Header Layout */ /* Header Layout */
.header { .header {
z-index: 10;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@ -149,6 +150,7 @@ main {
/* Benutzerliste Styling */ /* Benutzerliste Styling */
.user-list { .user-list {
margin-top: 6rem;
padding: 20px; padding: 20px;
background-color: #f8f9fa; background-color: #f8f9fa;
border-radius: 8px; border-radius: 8px;
@ -169,10 +171,9 @@ main {
color: red; color: red;
} }
/* Chat Interface Styles */ /* Chat Interface Styles
.chat-container { .chat-container {
width: 100%; width: 100%;
max-width: 600px;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -181,7 +182,7 @@ main {
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
} }*/
.chat-box { .chat-box {
flex: 1; flex: 1;
@ -250,8 +251,10 @@ main {
/* Haupt-Container für Layout mit Seitenleiste */ /* Haupt-Container für Layout mit Seitenleiste */
.main-container { .main-container {
height: 90vh;
width: 100%;
display: flex; display: flex;
height: 100vh; transform: translate(0px, 6rem);
} }
/* Linke Seitenleiste */ /* Linke Seitenleiste */
@ -288,6 +291,7 @@ main {
/* Chatbereich Styles (wie oben beschrieben) */ /* Chatbereich Styles (wie oben beschrieben) */
.chat-container { .chat-container {
width: 100%;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -307,6 +311,7 @@ main {
#chatBox { #chatBox {
display: flex; display: flex;
flex-direction: column-reverse; flex-direction: column-reverse;
overflow-y: auto;
} }
#sendButton { #sendButton {
@ -366,3 +371,4 @@ main {
background-color: #27ae60; background-color: #27ae60;
} }

View File

@ -1,5 +1,6 @@
let chatCount = 0; // Zähler für Chat-Sessions let chatCount = 0; // Zähler für Chat-Sessions
let currentChatId = null; // Aktuell angezeigte Chat-ID let currentChatId = null; // Aktuell angezeigte Chat-ID
let useMarkdown = true;
document.getElementById('newChatButton').addEventListener('click', () => { document.getElementById('newChatButton').addEventListener('click', () => {
createNewChat(); createNewChat();
@ -13,7 +14,14 @@ document.getElementById('sendButton').addEventListener('click', () => {
if (messageText !== '' && currentChatId !== null) { // Stelle sicher, dass eine Chat-ID vorhanden ist if (messageText !== '' && currentChatId !== null) { // Stelle sicher, dass eine Chat-ID vorhanden ist
const messageElement = document.createElement('div'); const messageElement = document.createElement('div');
messageElement.classList.add('chat-message', 'user'); messageElement.classList.add('chat-message', 'user');
messageElement.innerText = messageText; //messageElement.innerText = messageText;
if (useMarkdown) {
messageElement.innerHTML = renderMarkdown(messageText);
} else {
messageElement.innerText = messageText;
}
chatBox.prepend(messageElement); chatBox.prepend(messageElement);
// Nachricht speichern // Nachricht speichern
@ -37,21 +45,31 @@ function createNewChat() {
loadChatHistory(currentChatId); // Lade den neuen Chat loadChatHistory(currentChatId); // Lade den neuen Chat
} }
function saveMessage(chatId, message) { async function saveMessage(chatId, message) {
const chatHistory = JSON.parse(localStorage.getItem(`chatHistory-${chatId}`)) || []; const chatHistory = JSON.parse(localStorage.getItem(`chatHistory-${chatId}`)) || [];
chatHistory.push({ user: "user", text: message }); chatHistory.push({ role: "user", content: message });
localStorage.setItem(`chatHistory-${chatId}`, JSON.stringify(chatHistory)); localStorage.setItem(`chatHistory-${chatId}`, JSON.stringify(chatHistory));
// Aktualisiere den Button-Text in der Sidebar // Aktualisiere den Button-Text in der Sidebar
updateChatSessionButton(chatId, message); updateChatSessionButton(chatId);
// Sende die Nachricht an den Chatbot
const userMessage = getLastUserMessage();
if (userMessage) {
let a = await stream_api_open_ai(userMessage);
console.log("Komplette Nachricht: " + a)
const chatHistory = JSON.parse(localStorage.getItem(`chatHistory-${chatId}`)) || [];
chatHistory.push({ role: "assistant", content: a });
localStorage.setItem(`chatHistory-${chatId}`, JSON.stringify(chatHistory));
}
} }
function updateChatSessionButton(chatId, message) { function updateChatSessionButton(chatId) {
const button = document.getElementById(`chatSession-${chatId}`); const button = document.getElementById(`chatSession-${chatId}`);
if (button) { if (button) {
const chatHistory = JSON.parse(localStorage.getItem(`chatHistory-${chatId}`)) || []; const chatHistory = JSON.parse(localStorage.getItem(`chatHistory-${chatId}`)) || [];
// Hole die erste Nachricht oder lasse es leer, wenn keine vorhanden ist // Hole die erste Nachricht oder lasse es leer, wenn keine vorhanden ist
const chatName = chatHistory.length > 0 ? chatHistory[0].text.slice(0, 10) : ''; // Nimm die ersten 10 Zeichen der ersten Nachricht const chatName = chatHistory.length > 0 ? chatHistory[0].content.slice(0, 10) : ''; // Nimm die ersten 10 Zeichen der ersten Nachricht
button.innerText = chatName + "..." || `Chat ${chatId + 1}`; // Falls leer, setze auf "Chat x" button.innerText = chatName + "..." || `Chat ${chatId + 1}`; // Falls leer, setze auf "Chat x"
} }
} }
@ -105,8 +123,8 @@ function loadChatHistory(chatId) {
const chatHistory = JSON.parse(localStorage.getItem(`chatHistory-${chatId}`)) || []; const chatHistory = JSON.parse(localStorage.getItem(`chatHistory-${chatId}`)) || [];
chatHistory.forEach(entry => { chatHistory.forEach(entry => {
const messageElement = document.createElement('div'); const messageElement = document.createElement('div');
messageElement.classList.add('chat-message', entry.user); messageElement.classList.add('chat-message', entry.role);
messageElement.innerText = entry.text; messageElement.innerText = entry.content;
chatBox.prepend(messageElement); chatBox.prepend(messageElement);
}); });
} }
@ -127,7 +145,11 @@ function loadAllChatSessions() {
chatCount = Math.max(chatCount, chatId + 1); // Chat-Zähler aktualisieren chatCount = Math.max(chatCount, chatId + 1); // Chat-Zähler aktualisieren
} }
}); });
console.log(chatCount)
for (let i = 0; i < chatCount; i++) {// Aktualisiere die Namen an der Seite
updateChatSessionButton(i);
}
// Lade den neuesten Chat-Verlauf // Lade den neuesten Chat-Verlauf
if (chatCount > 0) { if (chatCount > 0) {
loadChatHistory(chatCount - 1); loadChatHistory(chatCount - 1);
@ -146,6 +168,10 @@ const chatInput = document.getElementById('chatInput');
// Event-Listener für das Senden der Nachricht und das Erstellen eines Zeilenumbruchs // Event-Listener für das Senden der Nachricht und das Erstellen eines Zeilenumbruchs
chatInput.addEventListener('keydown', (event) => { chatInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') { if (event.key === 'Enter') {
if (currentChatId == null){
createNewChat();
}
// Prüfen, ob die Shift-Taste nicht gedrückt wird // Prüfen, ob die Shift-Taste nicht gedrückt wird
if (!event.shiftKey) { if (!event.shiftKey) {
event.preventDefault(); // Verhindert das Standardverhalten (Absenden des Formulars) event.preventDefault(); // Verhindert das Standardverhalten (Absenden des Formulars)
@ -172,3 +198,118 @@ chatInput.addEventListener('keydown', (event) => {
} }
} }
}); });
/* OpenAI zeug */
async function getModels() {
try {
const response = await fetch(`http://localhost:8015/v1/models`, {
method: 'GET',
headers: {
'Authorization': `Bearer YOUR_API_KEY`,
'Content-Type': 'application/json',
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const models = await response.json();
return models.data[0].id
} catch (error) {
console.error('Error fetching models:', error);
}
}
function getAllCurrentChatMessages() {
const cookieData = JSON.parse(localStorage.getItem(`chatHistory-${currentChatId}`)) || [];
return cookieData
}
function getLastUserMessage() {
// Lade die Cookie-Daten (ersetze dies durch den tatsächlichen Weg zur Cookie-Datenstruktur)
const cookieData = JSON.parse(localStorage.getItem(`chatHistory-${currentChatId}`)) || [];
// Filter nur die User-Nachrichten heraus
const userMessages = cookieData.filter(message => message.role === "user");
// Falls es User-Nachrichten gibt, die letzte davon zurückgeben
if (userMessages.length > 0) {
return userMessages[userMessages.length - 1].content;
}
return ''; // Rückgabe eines leeren Strings, falls keine Nachricht gefunden wird
}
function renderMarkdown(content) {
return marked.parse(content);
}
async function stream_api_open_ai(userMessage) {
// Neues div-Element für die Antwort des Chatbots erstellen
const chatBox = document.getElementById('chatBox');
const botMessageDiv = document.createElement('div');
botMessageDiv.className = 'chat-message bot';
chatBox.prepend(botMessageDiv);
const response = await fetch('http://localhost:8015/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer YOUR_API_KEY`,
},
body: JSON.stringify({
model: await getModels(),
messages: [
{ role: 'system', content: 'You are a knowledgeable assistant.' },
...getAllCurrentChatMessages()
],
stream: true,
temperature: 0.3
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let result = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
for (const line of lines) {
if (line && line.includes('data: ')) {
const jsonStr = line.replace('data: ', '').trim();
if (jsonStr === '[DONE]'){
break;
}
try {
const json = JSON.parse(jsonStr);
if (json.choices[0].delta.content) {
const token = json.choices[0].delta.content;
result += token;
if (useMarkdown) {
botMessageDiv.innerHTML = renderMarkdown(result);
} else {
botMessageDiv.textContent = result;
}
}
} catch (error) {
console.error('Error parsing JSON:', error, 'Received:', jsonStr);
}
}
}
}
return result;
}

View File

@ -9,23 +9,42 @@
<body> <body>
<!-- Header für Benutzer-Info und Buttons --> <!-- Header für Benutzer-Info und Buttons -->
<header class="header"> <header class="header">
<h1 class="header-welcome">Willkommen auf Ihrer persönlichen Seite!</h1> <h1 class="header-welcome">Ein simples Chat-Interface mit Fokus auf die Privatsphäre.</h1>
<div class="header-user-info"> <div class="header-user-info">
<p><strong>Benutzername:</strong> <span id="username">Gast</span></p> <p><strong>Benutzername:</strong> <span id="username">Gast</span></p>
<p><strong>Admin:</strong> <span id="isAdmin">Nein</span></p> <p><strong>Admin:</strong> <span id="isAdmin">Nein</span></p>
</div> </div>
<div class="header-buttons">
<button id="adminPermissionsBtn" class="header-btn" style="display: none;">Admin-Einstellungen</button>
<button id="logoutBtn" class="header-btn">Abmelden</button>
</div>
</header>
<div class="header-buttons">
<button id="adminPermissionsBtn" class="header-btn" style="display: none;">Admin-Einstellungen</button>
<button id="logoutBtn" class="header-btn">Abmelden</button>
</div>
</header>
<!-- Freier Platz für zukünftige Features --> <!-- Freier Platz für zukünftige Features -->
<main></main> <div class="main-container">
<!-- Linke Seitenleiste für Chat-Sessions -->
<div class="sidebar" id="chatSidebar">
<h3>Gespeicherte Chats</h3>
<button id="newChatButton">Neuer Chat</button>
<div id="chatList"></div>
</div>
<!-- Chatbereich -->
<div class="chat-container">
<div class="chat-box" id="chatBox">
<!-- Nachrichten werden hier angezeigt -->
</div>
<div class="chat-input-container">
<textarea id="chatInput" class="chat-input" placeholder="Schreibe eine Nachricht..."></textarea>
<button id="sendButton" class="chat-send-button">Senden</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="js/chat.js"></script>
<script src="js/main.js"></script> <script src="js/main.js"></script>
</body> </body>
</html> </html>

View File

@ -14,6 +14,24 @@ server {
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
# Weiterleitung für v1/models
location /v1/models {
proxy_pass http://ip:port/v1/models;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Weiterleitung für v1/chat/completions
location /v1/chat/completions {
proxy_pass http://ip:port/v1/chat/completions;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Anfragen für andere statische Dateien (HTML, CSS, JS, Bilder) im Root-Verzeichnis suchen # Anfragen für andere statische Dateien (HTML, CSS, JS, Bilder) im Root-Verzeichnis suchen
location / { location / {
try_files $uri $uri/ /index.html; # Bei nicht gefundenen Dateien auf index.html zurückgreifen try_files $uri $uri/ /index.html; # Bei nicht gefundenen Dateien auf index.html zurückgreifen