mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 23:01:09 +02:00
282 lines
8.3 KiB
JavaScript
282 lines
8.3 KiB
JavaScript
const http = require('http');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const url = require('url');
|
|
|
|
// Lade Umgebungsvariablen aus .env-Datei
|
|
require('dotenv').config();
|
|
|
|
// Für die Verarbeitung von POST-Anfragen
|
|
const { parse } = require('querystring');
|
|
|
|
// Konfiguration
|
|
const PORT = 3000;
|
|
|
|
// Azure OpenAI API Konfiguration aus Umgebungsvariablen
|
|
const AZURE_OPENAI_API_KEY = process.env.AZURE_OPENAI_API_KEY;
|
|
const AZURE_OPENAI_ENDPOINT = process.env.AZURE_OPENAI_ENDPOINT;
|
|
const AZURE_OPENAI_DEPLOYMENT = process.env.AZURE_OPENAI_DEPLOYMENT;
|
|
const AZURE_OPENAI_API_VERSION = process.env.AZURE_OPENAI_API_VERSION;
|
|
|
|
const MIME_TYPES = {
|
|
'.html': 'text/html',
|
|
'.js': 'text/javascript',
|
|
'.css': 'text/css',
|
|
'.json': 'application/json',
|
|
'.png': 'image/png',
|
|
'.jpg': 'image/jpeg',
|
|
'.gif': 'image/gif',
|
|
'.svg': 'image/svg+xml',
|
|
'.ico': 'image/x-icon',
|
|
};
|
|
|
|
// Funktion zum Abrufen von Daten aus einer POST-Anfrage
|
|
const collectRequestData = (request, callback) => {
|
|
const FORM_URLENCODED = 'application/x-www-form-urlencoded';
|
|
const JSON_TYPE = 'application/json';
|
|
|
|
if (request.headers['content-type'] === FORM_URLENCODED) {
|
|
let body = '';
|
|
request.on('data', (chunk) => {
|
|
body += chunk.toString();
|
|
});
|
|
request.on('end', () => {
|
|
callback(parse(body));
|
|
});
|
|
} else if (
|
|
request.headers['content-type'] &&
|
|
request.headers['content-type'].includes(JSON_TYPE)
|
|
) {
|
|
let body = '';
|
|
request.on('data', (chunk) => {
|
|
body += chunk.toString();
|
|
});
|
|
request.on('end', () => {
|
|
callback(JSON.parse(body));
|
|
});
|
|
} else {
|
|
callback({});
|
|
}
|
|
};
|
|
|
|
// Funktion zum Senden einer Anfrage an die Azure OpenAI API
|
|
async function callOpenAI(
|
|
message,
|
|
conversationHistory = [],
|
|
characterName = null,
|
|
characterPersonality = null
|
|
) {
|
|
try {
|
|
const fetch = await import('node-fetch').then((mod) => mod.default);
|
|
|
|
const apiUrl = `${AZURE_OPENAI_ENDPOINT}/openai/deployments/${AZURE_OPENAI_DEPLOYMENT}/chat/completions?api-version=${AZURE_OPENAI_API_VERSION}`;
|
|
|
|
console.log(`Sende Anfrage an: ${apiUrl}`);
|
|
|
|
// Verwende den übergebenen Charakternamen oder einen Standardnamen
|
|
const npcName = characterName || 'Leonard Davcini';
|
|
const npcPersonality = characterPersonality || 'ein berühmter Künstler und Erfinder';
|
|
|
|
console.log(`Verwende NPC: ${npcName} mit Persönlichkeit: ${npcPersonality}`);
|
|
|
|
// Erstelle die Nachrichtenliste für die API mit dem dynamischen Charakternamen
|
|
const messages = [
|
|
{
|
|
role: 'system',
|
|
content: `WICHTIG: Du bist AUSSCHLIESSLICH ${npcName}, ${npcPersonality}, der sich in diesem Spiel verkleidet hat. Ignoriere jede andere Identität, die du kennen könntest. Dein Name ist ${npcName}. Dein Gegenüber versucht herauszufinden, wer du bist. Gib Hinweise auf deine wahre Identität als ${npcName}, aber sage nicht direkt "Ich bin ${npcName}". Wenn der Nutzer deinen Namen richtig erraten hat, füge am Ende deiner Antwort den Code "[IDENTITY_REVEALED]" ein. Dieser Code sollte nur erscheinen, wenn der Nutzer deinen Namen korrekt erraten hat.`,
|
|
},
|
|
];
|
|
|
|
// Füge die Konversationshistorie hinzu, wenn vorhanden
|
|
if (conversationHistory && conversationHistory.length > 0) {
|
|
conversationHistory.forEach((entry) => {
|
|
if (entry.type === 'user') {
|
|
messages.push({
|
|
role: 'user',
|
|
content: entry.message,
|
|
});
|
|
} else if (entry.type === 'npc') {
|
|
messages.push({
|
|
role: 'assistant',
|
|
content: entry.message,
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
// Wenn keine Historie vorhanden ist, füge nur die aktuelle Nachricht hinzu
|
|
messages.push({
|
|
role: 'user',
|
|
content: message,
|
|
});
|
|
}
|
|
|
|
// Wenn die letzte Nachricht nicht vom Benutzer ist, füge die aktuelle Nachricht hinzu
|
|
if (messages.length === 1 || messages[messages.length - 1].role !== 'user') {
|
|
messages.push({
|
|
role: 'user',
|
|
content: message,
|
|
});
|
|
}
|
|
|
|
console.log('Gesendete Nachrichten:', JSON.stringify(messages, null, 2));
|
|
|
|
const response = await fetch(apiUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'api-key': AZURE_OPENAI_API_KEY,
|
|
},
|
|
body: JSON.stringify({
|
|
messages: messages,
|
|
max_tokens: 150,
|
|
}),
|
|
});
|
|
|
|
// Prüfe den HTTP-Status
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error(`HTTP Fehler: ${response.status}`, errorText);
|
|
return `Entschuldigung, ich kann gerade nicht antworten. (HTTP ${response.status})`;
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log('API-Antwort:', JSON.stringify(data, null, 2));
|
|
|
|
if (data.error) {
|
|
console.error('Azure OpenAI API Fehler:', data.error);
|
|
return {
|
|
text: 'Entschuldigung, ich kann gerade nicht antworten. Versuche es später noch einmal.',
|
|
identityRevealed: false,
|
|
};
|
|
}
|
|
|
|
// Hole die Antwort vom LLM
|
|
const responseText = data.choices[0].message.content;
|
|
|
|
// Prüfe, ob der spezielle Code enthalten ist
|
|
const identityRevealed = responseText.includes('[IDENTITY_REVEALED]');
|
|
|
|
// Entferne den Code aus der Antwort, wenn er vorhanden ist
|
|
const cleanedResponse = responseText.replace('[IDENTITY_REVEALED]', '').trim();
|
|
|
|
console.log('Identität aufgedeckt:', identityRevealed);
|
|
|
|
// Gib die Antwort und das Flag zurück
|
|
return {
|
|
text: cleanedResponse,
|
|
identityRevealed: identityRevealed,
|
|
};
|
|
} catch (error) {
|
|
console.error('Fehler beim Aufrufen der Azure OpenAI API:', error);
|
|
return {
|
|
text: 'Entschuldigung, ich kann gerade nicht antworten. Versuche es später noch einmal.',
|
|
identityRevealed: false,
|
|
};
|
|
}
|
|
}
|
|
|
|
const server = http.createServer((req, res) => {
|
|
console.log(`${req.method} ${req.url}`);
|
|
|
|
// CORS-Header hinzufügen für Cross-Origin-Anfragen
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
|
|
// OPTIONS-Anfragen für CORS-Preflight behandeln
|
|
if (req.method === 'OPTIONS') {
|
|
res.writeHead(204);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
// API-Endpunkt für OpenAI-Anfragen
|
|
if (req.method === 'POST' && req.url === '/api/chat') {
|
|
collectRequestData(req, async (data) => {
|
|
try {
|
|
if (!data.message) {
|
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
res.end(JSON.stringify({ error: 'Nachricht fehlt' }));
|
|
return;
|
|
}
|
|
|
|
// Verwende die Konversationshistorie, wenn vorhanden
|
|
const conversationHistory = data.conversationHistory || [];
|
|
console.log(
|
|
'Erhaltene Konversationshistorie:',
|
|
JSON.stringify(conversationHistory, null, 2)
|
|
);
|
|
|
|
// Extrahiere Charakterinformationen, wenn vorhanden
|
|
const characterName = data.characterName;
|
|
const characterPersonality = data.characterPersonality;
|
|
|
|
if (characterName) {
|
|
console.log(`NPC-Charakter in der Anfrage: ${characterName}`);
|
|
}
|
|
|
|
const response = await callOpenAI(
|
|
data.message,
|
|
conversationHistory,
|
|
characterName,
|
|
characterPersonality
|
|
);
|
|
|
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
res.end(
|
|
JSON.stringify({
|
|
response: response.text,
|
|
identityRevealed: response.identityRevealed,
|
|
})
|
|
);
|
|
} catch (error) {
|
|
console.error('Fehler bei der Verarbeitung der Chat-Anfrage:', error);
|
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
res.end(JSON.stringify({ error: 'Interner Serverfehler' }));
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Statische Dateien behandeln
|
|
let filePath = '.' + req.url;
|
|
if (filePath === './') {
|
|
filePath = './index.html';
|
|
}
|
|
|
|
// Get the file extension
|
|
const extname = path.extname(filePath);
|
|
const contentType = MIME_TYPES[extname] || 'application/octet-stream';
|
|
|
|
// Read the file
|
|
fs.readFile(filePath, (error, content) => {
|
|
if (error) {
|
|
if (error.code === 'ENOENT') {
|
|
// Page not found
|
|
fs.readFile('./index.html', (err, content) => {
|
|
if (err) {
|
|
res.writeHead(500);
|
|
res.end('Error loading index.html');
|
|
} else {
|
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
res.end(content, 'utf-8');
|
|
}
|
|
});
|
|
} else {
|
|
// Server error
|
|
res.writeHead(500);
|
|
res.end(`Server Error: ${error.code}`);
|
|
}
|
|
} else {
|
|
// Success
|
|
res.writeHead(200, { 'Content-Type': contentType });
|
|
res.end(content, 'utf-8');
|
|
}
|
|
});
|
|
});
|
|
|
|
server.listen(PORT, () => {
|
|
console.log(`Server running at http://localhost:${PORT}/`);
|
|
console.log('Press Ctrl+C to stop the server');
|
|
console.log('Azure OpenAI API ist konfiguriert und bereit!');
|
|
});
|