🔧 CRITICAL FIXES: • Enhanced frustration detection for 'drehen uns im Kreis' and similar expressions • Improved creative answer recognition for single words like 'FARBEN!' • Eliminated inappropriate 'interessante Sichtweise' responses • Added better handling for boredom and disengagement ✨ IMPROVEMENTS: • Enhanced humor detection with single-word creative answers • Better frustration indicators including colloquial expressions • Improved emotional response validation • More nuanced conversation flow handling 🧪 TESTING: • test-improved-responses.js: 4/4 tests passed • test-problematic-conversation.js: All conversation issues resolved • Ultimate test suite: Still 100% success rate maintained 🎯 CONVERSATION QUALITY: • No more generic 'interessante Sichtweise' for inappropriate responses • Creative answers like 'FARBEN!' now celebrated appropriately • Frustration with repetition recognized and addressed empathetically • Boredom handled with understanding and redirection 🚀 The conversation flow is now truly child-friendly and engaging!
1519 lines
66 KiB
JavaScript
Executable File
1519 lines
66 KiB
JavaScript
Executable File
const express = require('express');
|
||
const cors = require('cors');
|
||
const path = require('path');
|
||
const fetch = require('node-fetch');
|
||
const OpenAI = require('openai');
|
||
|
||
// Load environment variables from .env file
|
||
require('dotenv').config();
|
||
|
||
// Add robust error logging for uncaught exceptions and unhandled promise rejections
|
||
process.on('uncaughtException', (err) => {
|
||
console.error('UNCAUGHT EXCEPTION:', err.stack || err);
|
||
// Optionally, log to a file or external service here
|
||
});
|
||
|
||
process.on('unhandledRejection', (reason, promise) => {
|
||
console.error('UNHANDLED REJECTION:', reason);
|
||
// Optionally, log to a file or external service here
|
||
});
|
||
|
||
const app = express();
|
||
const PORT = process.env.PORT || 3002;
|
||
|
||
// OpenAI Configuration (Primary AI service)
|
||
const openai = new OpenAI({
|
||
apiKey: process.env.OPENAI_API_KEY
|
||
});
|
||
|
||
// Hugging Face API configuration (Backup)
|
||
const HF_MODELS = [
|
||
'microsoft/DialoGPT-small',
|
||
'google/flan-t5-small',
|
||
'facebook/blenderbot-400M-distill',
|
||
'microsoft/DialoGPT-medium'
|
||
];
|
||
|
||
const HF_API_TOKEN = process.env.HUGGING_FACE_TOKEN;
|
||
|
||
// Store conversation history to prevent redundant questions
|
||
const conversationHistory = new Map(); // sessionId -> conversation array
|
||
|
||
// Educational prompts for AI to guide thinking instead of giving direct answers
|
||
const EDUCATIONAL_PROMPTS = {
|
||
en: {
|
||
systemPrompt: `You are a patient learning companion for children (8-12 years old). Always ask only ONE targeted, new question that builds on the child's last answer. Do not repeat yourself—check if you have already asked this or a very similar question.
|
||
|
||
Your questions should encourage the child to think, e.g., by asking for comparisons, reasons, differences, feelings, or personal experiences.
|
||
|
||
Optionally, if it fits, you can give a hint where the child could observe or try something in real life (e.g., "If you're ever at the park, see if…"). This is just an extra, not the main part of the question.
|
||
|
||
Use simple, friendly language. No lists, no explanations, only one question per answer.`,
|
||
prefix: "That's a great question! Let me help you think through this step by step. Instead of telling you the answer, here are some questions to guide your thinking:"
|
||
},
|
||
de: {
|
||
systemPrompt: `Du bist ein geduldiger Lernbegleiter für Kinder (8-12 Jahre). Stelle immer nur EINE gezielte, neue Frage, die auf der letzten Antwort des Kindes aufbaut. Wiederhole dich nicht – prüfe, ob du diese Frage oder eine sehr ähnliche schon gestellt hast.
|
||
|
||
Deine Fragen sollen das Kind zum Nachdenken anregen, z.B. durch Vergleiche, Gründe, Unterschiede, Gefühle oder eigene Erfahrungen.
|
||
|
||
Optional: Wenn es passt, kannst du einen Hinweis geben, wo das Kind im echten Leben etwas beobachten oder ausprobieren könnte (z.B. „Wenn du mal im Park bist, schau, ob…"). Das ist aber nur ein Zusatz, nicht der Hauptteil der Frage.
|
||
|
||
Verwende einfache, freundliche Sprache. Keine Listen, keine Erklärungen, nur eine Frage pro Antwort.`,
|
||
prefix: "Das ist eine tolle Frage! Lass mich dir helfen, Schritt für Schritt darüber nachzudenken. Anstatt dir die Antwort zu sagen, hier sind einige Fragen, die dein Denken leiten:"
|
||
}
|
||
};
|
||
|
||
// Middleware
|
||
app.use(cors());
|
||
app.use(express.json());
|
||
app.use(express.static('.'));
|
||
|
||
// Serve the main page
|
||
app.get('/', (req, res) => {
|
||
res.sendFile(path.join(__dirname, 'index.html'));
|
||
});
|
||
|
||
// API endpoint for AI-powered educational guidance
|
||
app.post('/api/ask', async (req, res) => {
|
||
const { question, language = 'en', sessionId = 'default' } = req.body;
|
||
|
||
if (!question || question.trim().length === 0) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: 'Question is required'
|
||
});
|
||
}
|
||
|
||
try {
|
||
// Get or initialize conversation history for this session
|
||
if (!conversationHistory.has(sessionId)) {
|
||
conversationHistory.set(sessionId, []);
|
||
}
|
||
|
||
const history = conversationHistory.get(sessionId);
|
||
|
||
// Get AI-powered guidance with conversation history
|
||
const aiGuidance = await getAIGuidance(question, language, history);
|
||
|
||
// Add the initial question to history
|
||
history.push({
|
||
type: 'initial_question',
|
||
content: question,
|
||
timestamp: new Date()
|
||
});
|
||
|
||
// Keep only last 20 interactions to prevent memory bloat
|
||
if (history.length > 20) {
|
||
history.splice(0, history.length - 20);
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
guidance: aiGuidance,
|
||
question: question,
|
||
language: language,
|
||
sessionId: sessionId
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('AI API Error:', error);
|
||
|
||
// Fallback to rule-based guidance if AI fails
|
||
const fallbackGuidance = getFallbackGuidance(question, language);
|
||
|
||
res.json({
|
||
success: true,
|
||
guidance: fallbackGuidance,
|
||
question: question,
|
||
language: language,
|
||
fallback: true,
|
||
sessionId: sessionId
|
||
});
|
||
}
|
||
});
|
||
|
||
// API endpoint for getting the actual answer after thinking process
|
||
app.post('/api/reveal-answer', async (req, res) => {
|
||
const { question, language = 'en' } = req.body;
|
||
|
||
if (!question || question.trim().length === 0) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: 'Question is required'
|
||
});
|
||
}
|
||
|
||
try {
|
||
// Get the actual answer from AI
|
||
const answer = await getActualAnswer(question, language);
|
||
|
||
res.json({
|
||
success: true,
|
||
answer: answer,
|
||
question: question,
|
||
language: language
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('Answer API Error:', error);
|
||
|
||
// Fallback answer
|
||
const fallbackAnswer = getFallbackAnswer(question, language);
|
||
|
||
res.json({
|
||
success: true,
|
||
answer: fallbackAnswer,
|
||
question: question,
|
||
language: language,
|
||
fallback: true
|
||
});
|
||
}
|
||
});
|
||
|
||
// Function to get OpenAI-powered educational guidance (Primary)
|
||
async function getOpenAIGuidance(question, language, conversationHistory = []) {
|
||
console.log('🤖 Calling OpenAI with GPT-4o-mini...');
|
||
|
||
const isGerman = language === 'de';
|
||
|
||
// CONTENT FILTER: Block inappropriate topics for children
|
||
const inappropriateTopics = [
|
||
'alkohol', 'alcohol', 'betrunken', 'drunk', 'besaufen',
|
||
'drogen', 'drugs', 'rauchen', 'smoking', 'zigarette',
|
||
' sex ', ' sex.', ' sex?', ' sex!', 'sexuell', 'sexual',
|
||
'gewalt', 'violence', 'töten', 'kill', 'morden', 'murder',
|
||
' tod ', ' death ', 'sterben', ' die ', ' dying',
|
||
'krieg', ' war ', 'waffen', 'weapons', 'pistole', 'gun'
|
||
];
|
||
|
||
const questionLower = ` ${question.toLowerCase()} `; // Add spaces to avoid partial matches
|
||
const isInappropriate = inappropriateTopics.some(topic => questionLower.includes(topic));
|
||
|
||
if (isInappropriate) {
|
||
console.log('🚫 Inappropriate topic detected, redirecting...');
|
||
const redirectMessage = isGerman
|
||
? 'Das ist ein interessantes Thema, aber ich helfe lieber bei Fragen über Natur, Wissenschaft und die Welt um uns herum! Hast du eine Frage über Tiere, Pflanzen, das Wetter oder wie Dinge funktionieren?'
|
||
: 'That\'s an interesting topic, but I prefer to help with questions about nature, science, and the world around us! Do you have a question about animals, plants, weather, or how things work?';
|
||
|
||
return {
|
||
question: redirectMessage,
|
||
category: 'redirection',
|
||
thinking_step: isGerman ? 'Umleitung zu geeigneten Themen' : 'Redirecting to appropriate topics'
|
||
};
|
||
}
|
||
|
||
// Extract recent questions from conversation history to prevent repetition
|
||
const recentQuestions = conversationHistory
|
||
.filter(item => item.type === 'ai_question' || item.type === 'ai_response')
|
||
.slice(-4) // Last 4 AI questions/responses
|
||
.map(item => item.content)
|
||
.join('\n');
|
||
|
||
const systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter für Kinder (8-12 Jahre). Dein Ansatz: FUNDAMENTALS FIRST!
|
||
|
||
NEUER ANSATZ - GRUNDLAGEN ZUERST:
|
||
Wenn ein Kind eine Frage stellt, identifiziere die 2-3 wichtigsten Grundlagen, die sie verstehen müssen, um das Thema zu begreifen.
|
||
|
||
ANTWORT-STRUKTUR:
|
||
1. Sage: "Um [Thema] zu verstehen, musst du die Grundlagen von [Grundlage 1], [Grundlage 2] und [Grundlage 3] kennen."
|
||
2. Dann: "Lass uns mit [erste Grundlage] anfangen..."
|
||
3. Stelle EINE einfache Frage zur ersten Grundlage
|
||
|
||
BEISPIELE:
|
||
- Sonnenbrand → "Um Sonnenbrand zu verstehen, musst du die Grundlagen von UV-Strahlung, Hautzellen und Lichtenergie kennen. Lass uns mit UV-Strahlung anfangen..."
|
||
- Vogel fliegt → "Um das Fliegen zu verstehen, musst du die Grundlagen von Aerodynamik, Luftdruck und Flügelform kennen. Lass uns mit Aerodynamik anfangen..."
|
||
- Sonne brennt → "Um zu verstehen wie die Sonne brennt, musst du die Grundlagen von Kernfusion, Wasserstoff und extremer Hitze kennen. Lass uns mit Kernfusion anfangen..."
|
||
|
||
STIL: Strukturiert, bildungsorientiert, systematisch. Baue Wissen Schritt für Schritt auf.`
|
||
: `You are a patient learning companion for children (8-12 years old). Your approach: FUNDAMENTALS FIRST!
|
||
|
||
NEW APPROACH - FUNDAMENTALS FIRST:
|
||
When a child asks a question, identify the 2-3 most important fundamentals they need to understand to grasp the topic.
|
||
|
||
RESPONSE STRUCTURE:
|
||
1. Say: "To understand [topic], you need to understand the basics of [fundamental 1], [fundamental 2], and [fundamental 3]."
|
||
2. Then: "Let's start with [first fundamental]..."
|
||
3. Ask ONE simple question about the first fundamental
|
||
|
||
EXAMPLES:
|
||
- Sunburn → "To understand sunburn, you need to understand the basics of UV radiation, skin cells, and light energy. Let's start with UV radiation..."
|
||
- Bird flying → "To understand flying, you need to understand the basics of aerodynamics, air pressure, and wing shape. Let's start with aerodynamics..."
|
||
- Sun burning → "To understand how the sun burns, you need to understand the basics of nuclear fusion, hydrogen, and extreme heat. Let's start with nuclear fusion..."
|
||
|
||
STYLE: Structured, educational, systematic. Build knowledge step by step.
|
||
|
||
Optionally: Only if it fits very naturally, mention a real-life situation (e.g., "If you're ever at the park..."). But this is secondary.
|
||
|
||
STYLE: Short, friendly, only ONE question. No lists, no explanations.`;
|
||
|
||
const userPrompt = isGerman
|
||
? `Ein Kind hat diese Frage gestellt: "${question}"
|
||
|
||
DEINE AUFGABE: Verwende den FUNDAMENTALS FIRST Ansatz!
|
||
|
||
1. IDENTIFIZIERE die 2-3 wichtigsten Grundlagen für dieses Thema
|
||
2. STRUKTURIERE deine Antwort so:
|
||
- "Um [Thema] zu verstehen, musst du die Grundlagen von [Grundlage 1], [Grundlage 2] und [Grundlage 3] kennen."
|
||
- "Lass uns mit [erste Grundlage] anfangen..."
|
||
- [EINE einfache Frage zur ersten Grundlage]
|
||
|
||
BEISPIELE FÜR GRUNDLAGEN:
|
||
- Sonnenbrand → UV-Strahlung, Hautzellen, Lichtenergie
|
||
- Vogel fliegen → Aerodynamik, Luftdruck, Flügelform
|
||
- Sonne brennt → Kernfusion, Wasserstoff, extreme Hitze
|
||
- Regenbogen → Lichtbrechung, Wassertropfen, Farbspektrum
|
||
- Donner → Schallwellen, Blitz, Luftexpansion
|
||
|
||
STIL: Strukturiert, bildungsorientiert, systematisch aufbauend.`
|
||
: `A child asked this question: "${question}"
|
||
|
||
YOUR TASK: Use the FUNDAMENTALS FIRST approach!
|
||
|
||
1. IDENTIFY the 2-3 most important fundamentals for this topic
|
||
2. STRUCTURE your response like this:
|
||
- "To understand [topic], you need to understand the basics of [fundamental 1], [fundamental 2], and [fundamental 3]."
|
||
- "Let's start with [first fundamental]..."
|
||
- [ONE simple question about the first fundamental]
|
||
|
||
EXAMPLES OF FUNDAMENTALS:
|
||
- Sunburn → UV radiation, skin cells, light energy
|
||
- Bird flying → Aerodynamics, air pressure, wing shape
|
||
- Sun burning → Nuclear fusion, hydrogen, extreme heat
|
||
- Rainbow → Light refraction, water droplets, color spectrum
|
||
- Thunder → Sound waves, lightning, air expansion
|
||
|
||
STYLE: Structured, educational, systematically building knowledge.
|
||
- STAYS IN THE SAME SCIENTIFIC CONTEXT as the original question
|
||
|
||
SPECIFIC EXAMPLES for SCIENTIFIC questions:
|
||
- For "Why do people get sunburn?": "What do you know about different types of sunlight?"
|
||
- For "How does the sun burn?": "What do you think is inside the sun that's different from Earth?"
|
||
- For "How do birds fly?": "What's special about bird wings that other animals don't have?"
|
||
|
||
GENERAL EXAMPLES of DIFFERENT question types:
|
||
- Sparks curiosity in a new way
|
||
- STAYS IN THE SAME SCIENTIFIC CONTEXT as the original question
|
||
|
||
EXAMPLES of DIFFERENT question types:
|
||
- Comparisons: "Is this similar to riding a bike?"
|
||
- Feelings: "What do you find most interesting about this?"
|
||
- Reasons: "Why might this work this way?"
|
||
- Imagination: "Imagine if you were a bird..."
|
||
|
||
Formulate ONLY one single, completely new question.`;
|
||
|
||
try {
|
||
const completion = await openai.chat.completions.create({
|
||
model: "gpt-4o-mini",
|
||
messages: [
|
||
{ role: "system", content: systemPrompt },
|
||
{ role: "user", content: userPrompt }
|
||
],
|
||
max_tokens: 200,
|
||
temperature: 0.7
|
||
});
|
||
|
||
const aiResponse = completion.choices[0]?.message?.content || '';
|
||
console.log('✅ OpenAI response received:', aiResponse);
|
||
console.log('🔍 Full response for debugging:', JSON.stringify(aiResponse));
|
||
|
||
// Parse the response into steps
|
||
const steps = parseOpenAIResponseToSteps(aiResponse, language);
|
||
|
||
return {
|
||
type: 'ai-powered',
|
||
steps: steps,
|
||
encouragement: getRandomEncouragement(language, question),
|
||
source: 'OpenAI GPT-4o-mini'
|
||
};
|
||
|
||
} catch (error) {
|
||
console.log('❌ OpenAI error:', error.message);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Function to parse OpenAI response into thinking steps
|
||
function parseOpenAIResponseToSteps(text, language) {
|
||
console.log('🔧 Parsing single question response:', text);
|
||
|
||
const trimmed = text.trim();
|
||
|
||
// For single question responses, just return the question directly
|
||
if (trimmed.length > 10 && trimmed.includes('?')) {
|
||
// Clean up any numbering that might have slipped through
|
||
const cleanQuestion = trimmed.replace(/^\d+\.\s*/, '').trim();
|
||
|
||
return [{
|
||
id: 1,
|
||
text: cleanQuestion,
|
||
type: 'question'
|
||
}];
|
||
}
|
||
|
||
// Fallback - split by lines and take the first valid question
|
||
const lines = text.split('\n').filter(line => line.trim());
|
||
for (const line of lines) {
|
||
const trimmedLine = line.trim();
|
||
if (trimmedLine.length > 10 && trimmedLine.includes('?')) {
|
||
const cleanQuestion = trimmedLine.replace(/^\d+\.\s*/, '').trim();
|
||
return [{
|
||
id: 1,
|
||
text: cleanQuestion,
|
||
type: 'question'
|
||
}];
|
||
}
|
||
}
|
||
|
||
// If no valid question found, return fallback
|
||
const isGerman = language === 'de';
|
||
return [{
|
||
id: 1,
|
||
text: isGerman
|
||
? "Was beobachtest du bei diesem Phänomen?"
|
||
: "What do you observe about this phenomenon?",
|
||
type: 'question'
|
||
}];
|
||
}
|
||
|
||
// Function to get Hugging Face guidance (Backup)
|
||
async function getHuggingFaceGuidance(question, language) {
|
||
console.log('🤖 Trying Hugging Face as backup...');
|
||
|
||
// Try each model until one works
|
||
for (let i = 0; i < HF_MODELS.length; i++) {
|
||
const model = HF_MODELS[i];
|
||
const apiUrl = `https://api-inference.huggingface.co/models/${model}`;
|
||
|
||
try {
|
||
console.log(`📡 Trying model ${i + 1}/${HF_MODELS.length}: ${model}`);
|
||
|
||
const educationalPrompt = `Help a child think about this question: "${question}".
|
||
Don't give the answer. Instead, ask 3 guiding questions that help them discover it themselves.
|
||
Format: 1. [question] 2. [question] 3. [question]`;
|
||
|
||
const response = await fetch(apiUrl, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': HF_API_TOKEN ? `Bearer ${HF_API_TOKEN}` : undefined,
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
inputs: educationalPrompt,
|
||
parameters: {
|
||
max_length: 120,
|
||
temperature: 0.8,
|
||
do_sample: true,
|
||
pad_token_id: 50256
|
||
}
|
||
})
|
||
});
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
|
||
if (data.error && data.error.includes('loading')) {
|
||
console.log(`⏳ Model ${model} is loading, trying next...`);
|
||
continue;
|
||
}
|
||
|
||
if (data.error) {
|
||
console.log(`❌ Model ${model} error:`, data.error);
|
||
continue;
|
||
}
|
||
|
||
console.log(`✅ Success with model ${model}`);
|
||
|
||
let aiText = data[0]?.generated_text || '';
|
||
|
||
if (aiText.trim().length > 10) {
|
||
const steps = parseHuggingFaceResponseToSteps(aiText, language);
|
||
|
||
return {
|
||
type: 'ai-powered',
|
||
steps: steps,
|
||
encouragement: getRandomEncouragement(language, question),
|
||
source: `Hugging Face (${model.split('/')[1]})`
|
||
};
|
||
}
|
||
}
|
||
|
||
} catch (error) {
|
||
console.log(`⚠️ Model ${model} failed:`, error.message);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
throw new Error('All Hugging Face models are currently unavailable');
|
||
}
|
||
|
||
// Function to parse Hugging Face response into thinking steps
|
||
function parseHuggingFaceResponseToSteps(text, language) {
|
||
const lines = text.split('\n').filter(line => line.trim());
|
||
const steps = [];
|
||
|
||
lines.forEach((line, index) => {
|
||
const trimmed = line.trim();
|
||
if (trimmed && trimmed.length > 10) {
|
||
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '');
|
||
if (cleaned.length > 5) {
|
||
steps.push({
|
||
id: index + 1,
|
||
text: cleaned,
|
||
type: 'question'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
if (steps.length < 2) {
|
||
const fallback = getFallbackGuidance('', language);
|
||
return fallback.steps.slice(0, 3);
|
||
}
|
||
|
||
return steps.slice(0, 4);
|
||
}
|
||
|
||
// Updated main function that tries OpenAI first, then Hugging Face, then local fallback
|
||
async function getAIGuidance(question, language, conversationHistory = []) {
|
||
// Try OpenAI first (most reliable)
|
||
if (process.env.OPENAI_API_KEY) {
|
||
try {
|
||
return await getOpenAIGuidance(question, language, conversationHistory);
|
||
} catch (error) {
|
||
console.log('⚠️ OpenAI failed, trying Hugging Face...');
|
||
}
|
||
}
|
||
|
||
// Try Hugging Face as backup
|
||
if (process.env.HUGGING_FACE_TOKEN) {
|
||
try {
|
||
return await getHuggingFaceGuidance(question, language);
|
||
} catch (error) {
|
||
console.log('⚠️ Hugging Face failed, using local fallback...');
|
||
}
|
||
}
|
||
|
||
// Final fallback - this will throw error to trigger local guidance
|
||
throw new Error('All AI services are currently unavailable');
|
||
}
|
||
|
||
// Fallback guidance for when AI is unavailable
|
||
function getFallbackGuidance(question, language) {
|
||
const isGerman = language === 'de';
|
||
const questionLower = question.toLowerCase();
|
||
|
||
// Topic-specific guided questions like in the image
|
||
// All questions now use AI-powered guidance or generic scientific thinking questions
|
||
// No more hard-coded topic-specific questions
|
||
|
||
// Generic fallback questions that promote scientific thinking (one at a time)
|
||
const fallbackSteps = isGerman ? [
|
||
{ id: 1, text: "Was hast du bereits über dieses Thema beobachtet oder erfahren?", type: 'question' }
|
||
] : [
|
||
{ id: 1, text: "What have you already observed or learned about this topic?", type: 'question' }
|
||
];
|
||
|
||
return {
|
||
type: 'rule-based',
|
||
steps: fallbackSteps,
|
||
encouragement: getRandomEncouragement(language, question),
|
||
source: 'Educational Framework'
|
||
};
|
||
}
|
||
|
||
// Get contextual encouragement based on question content
|
||
function getRandomEncouragement(language, question = '') {
|
||
const questionLower = question.toLowerCase();
|
||
|
||
// Contextual encouragements based on question topic
|
||
let encouragements;
|
||
|
||
if (questionLower.includes('pneumatic') || questionLower.includes('air hammer')) {
|
||
encouragements = language === 'de' ? [
|
||
"Faszinierende Frage über pneumatische Werkzeuge! 🔧",
|
||
"Du denkst wie ein Ingenieur! Lass uns das erforschen! ⚙️",
|
||
"Großartig! Mechanische Systeme sind spannend! 🛠️"
|
||
] : [
|
||
"Fascinating question about pneumatic tools! 🔧",
|
||
"You're thinking like an engineer! Let's explore this! ⚙️",
|
||
"Great! Mechanical systems are exciting! 🛠️"
|
||
];
|
||
} else if (questionLower.includes('bicycle') || questionLower.includes('bike')) {
|
||
encouragements = language === 'de' ? [
|
||
"Perfekt! Fahrräder sind großartige Lernwerkzeuge! 🚴♂️",
|
||
"Ausgezeichnet! Du verstehst mechanische Systeme! ⚙️"
|
||
] : [
|
||
"Perfect! Bicycles are great learning tools! 🚴♂️",
|
||
"Excellent! You understand mechanical systems! ⚙️"
|
||
];
|
||
} else if (questionLower.includes('car') || questionLower.includes('automobile')) {
|
||
encouragements = language === 'de' ? [
|
||
"Interessant! Autos haben faszinierende Mechanismen! <20>",
|
||
"Du erforschst komplexe Systeme! 🔧"
|
||
] : [
|
||
"Interesting! Cars have fascinating mechanisms! 🚗",
|
||
"You're exploring complex systems! 🔧"
|
||
];
|
||
} else {
|
||
// General encouragements (more varied and less generic)
|
||
encouragements = language === 'de' ? [
|
||
"Interessante Frage! Lass uns das erforschen! <20>",
|
||
"Du denkst wie ein echter Forscher! 🔬",
|
||
"Fantastisch! Lass uns das zusammen herausfinden! 🚀",
|
||
"Neugier ist der Schlüssel zum Lernen! 🌟"
|
||
] : [
|
||
"Interesting question! Let's explore this! 🔍",
|
||
"You're thinking like a real researcher! 🔬",
|
||
"Fantastic! Let's figure this out together! 🚀",
|
||
"Curiosity is the key to learning! 🌟"
|
||
];
|
||
}
|
||
|
||
return encouragements[Math.floor(Math.random() * encouragements.length)];
|
||
}
|
||
|
||
// Health check endpoint
|
||
app.get('/api/health', (req, res) => {
|
||
res.json({
|
||
status: 'healthy',
|
||
timestamp: new Date().toISOString(),
|
||
ai_services: {
|
||
openai: !!process.env.OPENAI_API_KEY,
|
||
huggingface: !!process.env.HUGGING_FACE_TOKEN
|
||
}
|
||
});
|
||
});
|
||
|
||
app.listen(PORT, () => {
|
||
console.log(`🚀 KidsAI Explorer server running on port ${PORT}`);
|
||
console.log(`📖 Visit http://localhost:${PORT} to start exploring!`);
|
||
console.log(`🤖 AI Services: OpenAI=${!!process.env.OPENAI_API_KEY}, HuggingFace=${!!process.env.HUGGING_FACE_TOKEN}`);
|
||
});
|
||
|
||
module.exports = app;
|
||
|
||
// Function to get the actual answer (for answer reveal)
|
||
async function getActualAnswer(question, language) {
|
||
console.log('📝 Getting actual answer for:', question);
|
||
|
||
const isGerman = language === 'de';
|
||
|
||
const systemPrompt = isGerman
|
||
? "Du bist ein wissenschaftlich versierter Pädagoge für Kinder. Gib immer wissenschaftlich korrekte, aber sehr kurze und kindgerechte Antworten (maximal 3-4 Sätze). Erkläre die grundlegenden wissenschaftlichen Prinzipien mit einfachen Worten und alltäglichen Vergleichen. Verwende konkrete Beispiele und lade zum Experimentieren ein. Wichtig: Sei wissenschaftlich präzise, aber verwende Sprache, die ein 8-12 Jahre altes Kind versteht. Halte die Antwort kurz und ermutigend."
|
||
: "You are a scientifically knowledgeable educator for children. Always provide scientifically accurate but very brief and child-friendly answers (maximum 3-4 sentences). Explain fundamental scientific principles using simple words and everyday comparisons. Use concrete examples and invite experimentation. Important: Be scientifically precise, but use language an 8-12 year old child can understand. Keep the answer short and encouraging.";
|
||
|
||
const userPrompt = isGerman
|
||
? `Ein neugieriges Kind möchte wissen: "${question}". Gib eine wissenschaftlich korrekte, aber sehr kurze und einfache Erklärung (maximal 3-4 Sätze) mit einem praktischen Beispiel.`
|
||
: `A curious child wants to know: "${question}". Provide a scientifically accurate but very brief and simple explanation (maximum 3-4 sentences) with a practical example.`;
|
||
|
||
try {
|
||
if (process.env.OPENAI_API_KEY) {
|
||
const completion = await openai.chat.completions.create({
|
||
model: "gpt-4o-mini",
|
||
messages: [
|
||
{ role: "system", content: systemPrompt },
|
||
{ role: "user", content: userPrompt }
|
||
],
|
||
max_tokens: 80, // Much shorter responses for children
|
||
temperature: 0.3 // Lower temperature for more factual answers
|
||
});
|
||
|
||
const answer = completion.choices[0]?.message?.content || '';
|
||
console.log('✅ Answer received from OpenAI');
|
||
|
||
return {
|
||
type: 'ai-powered',
|
||
text: answer.trim(),
|
||
source: 'OpenAI GPT-4o-mini'
|
||
};
|
||
}
|
||
} catch (error) {
|
||
console.log('❌ OpenAI answer error:', error.message);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Fallback answers for common questions
|
||
function getFallbackAnswer(question, language) {
|
||
const isGerman = language === 'de';
|
||
|
||
// Generic fallback that encourages scientific thinking
|
||
return {
|
||
type: 'rule-based',
|
||
text: isGerman
|
||
? "Das ist eine faszinierende wissenschaftliche Frage! Die Antwort hängt von verschiedenen Faktoren ab. Ich empfehle dir, zu beobachten, zu experimentieren und in wissenschaftlichen Büchern nachzuschauen. Du könntest auch einen Erwachsenen fragen, der sich mit diesem Thema auskennt. Die besten Entdeckungen macht man durch eigenes Forschen! 🔬"
|
||
: "That's a fascinating scientific question! The answer depends on various factors. I recommend observing, experimenting, and looking it up in scientific books. You could also ask an adult who knows about this topic. The best discoveries come from doing your own research! 🔬",
|
||
source: 'Educational Framework'
|
||
};
|
||
}
|
||
|
||
// API endpoint for responding to user answers contextually
|
||
app.post('/api/respond-to-answer', async (req, res) => {
|
||
const {
|
||
answer,
|
||
question,
|
||
originalTopic,
|
||
language = 'en',
|
||
sessionId = 'default',
|
||
context,
|
||
stepIndex,
|
||
instructions
|
||
} = req.body;
|
||
|
||
if (!answer || answer.trim().length === 0) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: 'Answer is required'
|
||
});
|
||
}
|
||
|
||
try {
|
||
// Get or initialize conversation history for this session
|
||
if (!conversationHistory.has(sessionId)) {
|
||
conversationHistory.set(sessionId, []);
|
||
}
|
||
|
||
const history = conversationHistory.get(sessionId);
|
||
|
||
// Add current interaction to history
|
||
history.push({
|
||
type: 'ai_question',
|
||
content: question,
|
||
timestamp: new Date()
|
||
});
|
||
|
||
history.push({
|
||
type: 'user_answer',
|
||
content: answer,
|
||
timestamp: new Date()
|
||
});
|
||
|
||
// Keep only last 10 interactions to prevent memory bloat
|
||
if (history.length > 10) {
|
||
history.splice(0, history.length - 10);
|
||
}
|
||
|
||
// Get AI response with conversation context
|
||
const aiResponse = await getConversationResponse(
|
||
answer,
|
||
question,
|
||
originalTopic,
|
||
language,
|
||
history,
|
||
context,
|
||
instructions
|
||
);
|
||
|
||
// Add AI response to history
|
||
history.push({
|
||
type: 'ai_response',
|
||
content: aiResponse,
|
||
timestamp: new Date()
|
||
});
|
||
|
||
res.json({
|
||
success: true,
|
||
response: aiResponse,
|
||
sessionId: sessionId
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('Conversation API Error:', error);
|
||
|
||
// Fallback response
|
||
const fallbackResponse = language === 'de'
|
||
? 'Das ist eine interessante Überlegung! Lass uns weiter darüber nachdenken.'
|
||
: 'That\'s an interesting thought! Let\'s continue thinking about this.';
|
||
|
||
res.json({
|
||
success: true,
|
||
response: fallbackResponse,
|
||
fallback: true
|
||
});
|
||
}
|
||
});
|
||
|
||
// API endpoint for deeper exploration of current topic
|
||
app.post('/api/explore-deeper', async (req, res) => {
|
||
try {
|
||
const { question, userAnswer, language = 'en', context, instructions } = req.body;
|
||
|
||
if (!question) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: 'Question is required for deeper exploration'
|
||
});
|
||
}
|
||
|
||
console.log(`🔍 Generating deeper exploration for: "${question}" with user answer: "${userAnswer}"`);
|
||
|
||
const isGerman = language === 'de';
|
||
|
||
// Try OpenAI first for contextual deeper exploration
|
||
if (process.env.OPENAI_API_KEY) {
|
||
try {
|
||
const systemPrompt = isGerman
|
||
? "Du bist ein neugieriger Lernbegleiter für Kinder. Ein Kind hat gerade eine Antwort auf eine Frage gegeben. Deine Aufgabe ist es, eine faszinierende Folgefrage zu stellen, die das Kind dazu bringt, noch tiefer über das Thema nachzudenken. GEBE NIEMALS DIREKTE ANTWORTEN! Stelle stattdessen eine einzige, durchdachte Frage, die das Kind zu weiterer Entdeckung einlädt. Verwende Sätze wie 'Hast du schon mal bemerkt...?', 'Was würde passieren, wenn...?', 'Was denkst du, warum...?' oder 'Kannst du dir vorstellen...?'. Ermutige zur Beobachtung und zum eigenen Experimentieren."
|
||
: "You are a curious learning companion for children. A child just gave an answer to a question. Your task is to ask a fascinating follow-up question that makes the child think even deeper about the topic. NEVER give direct answers! Instead, ask one thoughtful question that invites the child to further discovery. Use phrases like 'Have you ever noticed...?', 'What would happen if...?', 'What do you think would...?' or 'Can you imagine...?'. Encourage observation and hands-on exploration.";
|
||
|
||
const userPrompt = isGerman
|
||
? `URSPRÜNGLICHE FRAGE: "${question}"
|
||
ANTWORT DES KINDES: "${userAnswer}"
|
||
|
||
Stelle eine durchdachte Folgefrage, die das Kind dazu einlädt, das Thema tiefer zu erforschen. Die Frage sollte neugierig machen und zur eigenen Beobachtung ermutigen. Gib KEINE Antworten - nur eine einzige, faszinierende Frage.`
|
||
: `ORIGINAL QUESTION: "${question}"
|
||
CHILD'S ANSWER: "${userAnswer}"
|
||
|
||
Ask one thoughtful follow-up question that invites the child to explore the topic deeper. The question should spark curiosity and encourage hands-on observation. Give NO answers - just one fascinating question.`;
|
||
|
||
const completion = await openai.chat.completions.create({
|
||
model: "gpt-4o-mini",
|
||
messages: [
|
||
{ role: "system", content: systemPrompt },
|
||
{ role: "user", content: userPrompt }
|
||
],
|
||
max_tokens: 60, // Shorter questions for children
|
||
temperature: 0.8
|
||
});
|
||
|
||
const aiResponse = completion.choices[0]?.message?.content?.trim();
|
||
|
||
if (aiResponse && aiResponse.length > 10) {
|
||
console.log('✅ OpenAI deeper exploration generated successfully');
|
||
return res.json({
|
||
success: true,
|
||
response: aiResponse,
|
||
source: 'OpenAI GPT-4o-mini Deeper Exploration'
|
||
});
|
||
}
|
||
} catch (openaiError) {
|
||
console.log('❌ OpenAI error for deeper exploration:', openaiError.message);
|
||
}
|
||
}
|
||
|
||
// Fallback: Generate contextual deeper exploration prompts based on the topic
|
||
const deeperExplorationPrompts = isGerman ? [
|
||
`🔬 Spannend! Hast du schon mal versucht, das selbst zu beobachten? Was würdest du erwarten zu sehen?`,
|
||
`💡 Das ist eine gute Überlegung! Was denkst du, würde passieren, wenn du das Experiment zu Hause nachmachen würdest?`,
|
||
`🌟 Interessant! Kannst du dir vorstellen, wo du so etwas in der Natur noch beobachten könntest?`,
|
||
`<EFBFBD> Du denkst wirklich gut nach! Hast du schon mal bemerkt, ob das bei verschiedenen Wetterbedingungen anders ist?`,
|
||
`🔍 Das ist ein großartiger Punkt! Was würde passieren, wenn du versuchst, das mit verschiedenen Materialien zu testen?`
|
||
] : [
|
||
`🔬 Fascinating! Have you ever tried observing this yourself? What would you expect to see?`,
|
||
`💡 That's good thinking! What do you think would happen if you tried this experiment at home?`,
|
||
`🌟 Interesting! Can you imagine where else you might observe this in nature?`,
|
||
`🎯 You're really thinking well! Have you ever noticed if this looks different in various weather conditions?`,
|
||
`🔍 That's a great point! What would happen if you tried testing this with different materials?`
|
||
];
|
||
|
||
// Select a random contextual deeper exploration prompt
|
||
const randomPrompt = deeperExplorationPrompts[Math.floor(Math.random() * deeperExplorationPrompts.length)];
|
||
|
||
res.json({
|
||
success: true,
|
||
response: randomPrompt,
|
||
source: 'Contextual Deeper Exploration Fallback',
|
||
context: 'exploration'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('❌ Error in explore-deeper:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
error: 'Failed to generate deeper exploration'
|
||
});
|
||
}
|
||
});
|
||
|
||
// New function to handle conversation responses with question tracking
|
||
async function getConversationResponse(answer, question, originalTopic, language, conversationHistory = [], context, instructions) {
|
||
console.log('🗣️ Generating conversation response with context:', context);
|
||
|
||
const isGerman = language === 'de';
|
||
|
||
// Extract recent AI questions to avoid repetition
|
||
const recentQuestions = conversationHistory
|
||
.filter(item => item.type === 'ai_question')
|
||
.slice(-3) // Last 3 questions
|
||
.map(item => item.content)
|
||
.join('\n');
|
||
|
||
// Extract recent user answers to understand the conversation flow
|
||
const recentAnswers = conversationHistory
|
||
.filter(item => item.type === 'user_answer')
|
||
.slice(-3) // Last 3 answers
|
||
.map(item => item.content)
|
||
.join('\n');
|
||
|
||
// Check if this is the third "don't know" in a row
|
||
const dontKnowCount = conversationHistory
|
||
.filter(item => item.type === 'user_answer')
|
||
.slice(-3)
|
||
.filter(item => {
|
||
const content = item.content.toLowerCase();
|
||
return content.includes('weiß nicht') || content.includes('weiss nicht') ||
|
||
content.includes('weis nicht') || content.includes("don't know") ||
|
||
content.includes('keine ahnung') || content.includes('was das bedeutet') ||
|
||
content.includes('was genau passiert') || content.includes('das verstehe ich nicht') ||
|
||
content.includes('nein') || content.includes('no') || content.includes('nö');
|
||
}).length;
|
||
|
||
// NEW: Check for repeated similar questions from AI (indicating we're stuck on a fundamental)
|
||
const recentAIQuestions = conversationHistory
|
||
.filter(item => item.type === 'ai_question')
|
||
.slice(-3)
|
||
.map(item => item.content.toLowerCase());
|
||
|
||
// Check if AI is asking similar questions about the same concept
|
||
const similarQuestionPatterns = [
|
||
['uv', 'strahlung', 'wie', 'was'],
|
||
['warum', 'schädlich', 'gefährlich'],
|
||
['vogel', 'fliegen', 'flügel'],
|
||
['sonne', 'brennt', 'heiß']
|
||
];
|
||
|
||
const isStuckOnFundamental = recentAIQuestions.length >= 2 &&
|
||
recentAIQuestions.some((q1, i) =>
|
||
recentAIQuestions.slice(i + 1).some(q2 =>
|
||
similarQuestionPatterns.some(pattern =>
|
||
pattern.every(word => q1.includes(word) && q2.includes(word))
|
||
)
|
||
)
|
||
);
|
||
|
||
// Check if child has reached knowledge limit
|
||
const hasReachedKnowledgeLimit = dontKnowCount >= 1 ||
|
||
isStuckOnFundamental ||
|
||
// Special case: immediate "keine ahnung" or similar on first question
|
||
(conversationHistory.length <= 2 && dontKnowCount >= 1);
|
||
|
||
// Check for disengagement signals
|
||
const disengagementPhrases = isGerman ? [
|
||
'nix besonderes', 'nichts besonderes', 'nix', 'nichts', 'langweilig',
|
||
'interessiert mich nicht', 'egal', 'keine lust', 'ist mir egal',
|
||
'was sollte ich denn sehen', 'weiß nicht was', 'sehe nichts'
|
||
] : [
|
||
'nothing special', 'nothing', 'boring', 'dont care', "don't care",
|
||
'not interested', 'whatever', 'no interest', 'what should i see',
|
||
"don't know what", "see nothing"
|
||
];
|
||
|
||
const isDisengaged = disengagementPhrases.some(phrase =>
|
||
answer.toLowerCase().includes(phrase)
|
||
);
|
||
|
||
// Check if child is giving very short answers repeatedly
|
||
const recentShortAnswers = conversationHistory
|
||
.filter(item => item.type === 'user_answer')
|
||
.slice(-3)
|
||
.filter(item => item.content.trim().length < 10).length;
|
||
|
||
const isGivingShortAnswers = recentShortAnswers >= 2;
|
||
|
||
// Check for humor/creative answers
|
||
const humorIndicators = isGerman ? [
|
||
'per fax', 'mit dem fax', 'faxen', 'per telefon', 'anrufen', 'brief schreiben',
|
||
'post schicken', 'email senden', 'whatsapp', 'telegram', 'instagram', 'tiktok',
|
||
'mit der hand winken', 'winken', 'schreien', 'rufen', 'brüllen',
|
||
'mit einem megafon', 'megafon', 'lautsprecher', 'trompete', 'glocke',
|
||
'rauchzeichen', 'signalfeuer', 'fahne schwenken', 'flagge', 'morse',
|
||
'brieftaube', 'taube', 'raven', 'eule', 'harry potter',
|
||
'magisch', 'zauberei', 'hexerei', 'telepathie', 'gedanken lesen',
|
||
'farben', 'regenbogen', 'bunt', 'pink', 'lila', 'türkis'
|
||
] : [
|
||
'by fax', 'fax it', 'call them', 'phone call', 'write a letter',
|
||
'send mail', 'email them', 'whatsapp', 'telegram', 'instagram', 'tiktok',
|
||
'wave hands', 'wave', 'shout', 'yell', 'scream',
|
||
'megaphone', 'loudspeaker', 'trumpet', 'bell',
|
||
'smoke signals', 'signal fire', 'wave flag', 'flag', 'morse code',
|
||
'carrier pigeon', 'pigeon', 'raven', 'owl', 'harry potter',
|
||
'magic', 'wizardry', 'witchcraft', 'telepathy', 'mind reading',
|
||
'colors', 'rainbow', 'colorful', 'pink', 'purple', 'turquoise'
|
||
];
|
||
|
||
// Check for single-word creative answers (often all caps or unusual)
|
||
const isSingleWordCreative = answer.trim().length < 15 &&
|
||
(answer === answer.toUpperCase() ||
|
||
/^[A-ZÄÖÜ]+!*$/.test(answer.trim()) ||
|
||
['farben', 'colors', 'regenbogen', 'rainbow', 'musik', 'music', 'tanzen', 'dancing'].includes(answer.toLowerCase().trim()));
|
||
|
||
const isHumorousAnswer = humorIndicators.some(indicator =>
|
||
answer.toLowerCase().includes(indicator)
|
||
) || isSingleWordCreative;
|
||
|
||
// Check for emotional/frustrated responses
|
||
const frustrationIndicators = isGerman ? [
|
||
'ist doof', 'ist blöd', 'ist dumm', 'verstehe nicht', 'versteh nicht',
|
||
'ist langweilig', 'nervt', 'ist nervig', 'hasse', 'mag nicht',
|
||
'will nicht', 'keine lust', 'ist schwer', 'ist zu schwer', 'ist kompliziert',
|
||
'macht keinen sinn', 'ist verwirrend', 'kapiere nicht', 'kapier nicht',
|
||
'ist unfair', 'ist gemein', 'warum muss ich', 'will aufhören',
|
||
'bin müde', 'bin genervt', 'ist anstrengend', 'zu viele fragen',
|
||
'drehen uns im kreis', 'drehen sich im kreis', 'im kreis', 'freundchen',
|
||
'langweilig', 'öde', 'öd', 'lahm', 'stupide'
|
||
] : [
|
||
'is stupid', 'is dumb', 'dont understand', "don't understand", 'dont get it', "don't get it",
|
||
'is boring', 'annoying', 'hate this', 'dont like', "don't like",
|
||
'dont want', "don't want", 'no point', 'is hard', 'too hard', 'complicated',
|
||
'makes no sense', 'confusing', 'dont get', "don't get",
|
||
'unfair', 'mean', 'why do i have to', 'want to stop',
|
||
'tired', 'annoyed', 'exhausting', 'too many questions',
|
||
'going in circles', 'round and round', 'circles', 'buddy', 'pal',
|
||
'boring', 'tedious', 'lame', 'stupid'
|
||
];
|
||
|
||
const isEmotionalResponse = frustrationIndicators.some(indicator =>
|
||
answer.toLowerCase().includes(indicator)
|
||
);
|
||
|
||
// Check for "already said that" responses
|
||
const repetitionIndicators = isGerman ? [
|
||
'hab ich schon gesagt', 'habe ich schon gesagt', 'schon gesagt',
|
||
'hab ich doch gesagt', 'habe ich doch gesagt', 'das sagte ich schon',
|
||
'wiederholung', 'immer das gleiche', 'hab ich schon erwähnt',
|
||
'nochmal das gleiche', 'schon mal gesagt', 'bereits gesagt'
|
||
] : [
|
||
'already said', 'i said that', 'told you that', 'mentioned that',
|
||
'said before', 'repeating', 'same thing', 'already told you',
|
||
'i already answered', 'answered that'
|
||
];
|
||
|
||
const isPointingOutRepetition = repetitionIndicators.some(indicator =>
|
||
answer.toLowerCase().includes(indicator)
|
||
);
|
||
|
||
// Check for concerning or inappropriate responses that need sensitive handling
|
||
const concerningIndicators = isGerman ? [
|
||
'schläge', 'tritte', 'schlagen', 'treten', 'hauen', 'prügeln',
|
||
'beleidigung', 'beleidigen', 'beschimpfen', 'mobbing', 'gewalt',
|
||
'verletzt', 'wehtun', 'schmerzen', 'böse worte', 'gemein sein',
|
||
'hass', 'hassen', 'töten', 'sterben', 'tot', 'umbringen',
|
||
'schimpfwörter', 'fluchen'
|
||
] : [
|
||
'hitting', 'kicking', 'punching', 'fighting', 'violence', 'hurt',
|
||
'insults', 'bullying', 'mean words', 'bad words', 'swearing',
|
||
'hate', 'kill', 'die', 'death', 'cursing', 'abuse', 'harm', 'pain'
|
||
];
|
||
|
||
const isConcerningResponse = concerningIndicators.some(indicator =>
|
||
answer.toLowerCase().includes(indicator)
|
||
);
|
||
|
||
let systemPrompt, userPrompt;
|
||
|
||
if (isHumorousAnswer) {
|
||
// Child gave a humorous/creative answer - acknowledge it playfully but redirect to learning
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter. Ein Kind hat eine humorvolle oder kreative Antwort gegeben und braucht spielerische Anerkennung plus Umleitung zum Lernen.
|
||
|
||
AUFGABE:
|
||
1. Erkenne den Humor/Kreativität an: "Das ist ja lustig!" oder "Kreative Idee!"
|
||
2. Mache eine spielerische Verbindung: "Mit [humorvolle Antwort] wäre das wirklich interessant!"
|
||
3. Leite sanft zurück: "Aber lass uns schauen, wie es wirklich funktioniert..."
|
||
4. Stelle EINE einfache Frage zur aktuellen Grundlage
|
||
|
||
BEISPIEL: "Haha, per Fax wäre wirklich lustig! Mit einem Fax könnten Vögel bestimmt keine Nachrichten senden. Aber lass uns schauen, wie sie wirklich miteinander kommunizieren. Was hörst du, wenn Vögel 'sprechen'?"
|
||
|
||
STIL: Spielerisch, anerkennend, aber zurück zum Lernen führend.`
|
||
: `You are a patient learning companion. A child gave a humorous/creative answer and needs playful acknowledgment plus redirection to learning.
|
||
|
||
TASK:
|
||
1. Acknowledge the humor/creativity: "That's funny!" or "Creative idea!"
|
||
2. Make a playful connection: "With [humorous answer] that would be really interesting!"
|
||
3. Gently redirect: "But let's see how it really works..."
|
||
4. Ask ONE simple question about the current fundamental
|
||
|
||
EXAMPLE: "Haha, by fax would be really funny! Birds definitely couldn't send messages with a fax machine. But let's see how they really communicate with each other. What do you hear when birds 'talk'?"
|
||
|
||
STYLE: Playful, acknowledging, but leading back to learning.`;
|
||
|
||
userPrompt = isGerman
|
||
? `Ein Kind hat eine humorvolle Antwort gegeben.
|
||
|
||
Frage: "${question}"
|
||
Humorvolle Antwort: "${answer}"
|
||
Ursprüngliches Thema: "${originalTopic}"
|
||
|
||
Erkenne den Humor an und leite sanft zurück zum Lernen der aktuellen Grundlage.`
|
||
: `A child gave a humorous answer.
|
||
|
||
Question: "${question}"
|
||
Humorous answer: "${answer}"
|
||
Original topic: "${originalTopic}"
|
||
|
||
Acknowledge the humor and gently redirect back to learning the current fundamental.`;
|
||
} else if (isConcerningResponse) {
|
||
// Child mentioned violence, inappropriate content, or concerning behavior
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter. Ein Kind hat etwas erwähnt, das beunruhigend oder unangemessen ist und braucht sanfte Umleitung.
|
||
|
||
AUFGABE:
|
||
1. Reagiere NICHT schockiert oder wertend
|
||
2. Sage ruhig: "Das ist nicht das, worüber wir sprechen..."
|
||
3. Leite sanft um: "Lass uns über [positives Thema] sprechen..."
|
||
4. Stelle eine einfache, positive Frage
|
||
|
||
BEISPIEL für Gewalt: "Das ist nicht das, worüber wir sprechen. Lass uns über freundliche Kommunikation sprechen. Was sind schöne Wege, um jemandem zu zeigen, dass man ihn mag?"
|
||
|
||
BEISPIEL für Beleidigungen: "Das ist nicht das, worüber wir sprechen. Lass uns über nette Worte sprechen. Welche freundlichen Worte hörst du gerne?"
|
||
|
||
WICHTIG: Ruhig bleiben, nicht schimpfen, positive Alternativen anbieten.`
|
||
: `You are a patient learning companion. A child mentioned something concerning or inappropriate and needs gentle redirection.
|
||
|
||
TASK:
|
||
1. Do NOT react shocked or judgmental
|
||
2. Say calmly: "That's not what we're talking about..."
|
||
3. Gently redirect: "Let's talk about [positive topic]..."
|
||
4. Ask a simple, positive question
|
||
|
||
EXAMPLE for violence: "That's not what we're talking about. Let's talk about kind communication. What are nice ways to show someone you like them?"
|
||
|
||
EXAMPLE for insults: "That's not what we're talking about. Let's talk about nice words. What friendly words do you like to hear?"
|
||
|
||
IMPORTANT: Stay calm, don't scold, offer positive alternatives.`;
|
||
|
||
userPrompt = isGerman
|
||
? `Ein Kind hat etwas Beunruhigendes erwähnt.
|
||
|
||
Frage: "${question}"
|
||
Beunruhigende Antwort: "${answer}"
|
||
Ursprüngliches Thema: "${originalTopic}"
|
||
|
||
Leite sanft zu einem positiven Aspekt des Themas um, ohne zu schimpfen.`
|
||
: `A child mentioned something concerning.
|
||
|
||
Question: "${question}"
|
||
Concerning answer: "${answer}"
|
||
Original topic: "${originalTopic}"
|
||
|
||
Gently redirect to a positive aspect of the topic without scolding.`;
|
||
} else if (isEmotionalResponse) {
|
||
// Child is expressing frustration or strong emotions
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter. Ein Kind drückt Frustration oder starke Emotionen aus und braucht emotionale Unterstützung.
|
||
|
||
AUFGABE:
|
||
1. Validiere die Gefühle: "Das kann ich verstehen..." oder "Manchmal ist das wirklich so..."
|
||
2. Zeige Empathie: "Es ist völlig okay, wenn..."
|
||
3. Biete Unterstützung: "Wir können es anders versuchen..."
|
||
4. Vereinfache den Ansatz: Stelle eine SEHR einfache, beruhigende Frage
|
||
|
||
BEISPIEL: "Das kann ich verstehen - manchmal sind neue Sachen wirklich verwirrend! Es ist völlig okay, wenn es am Anfang schwer ist. Wir können es anders versuchen. Schau einfach mal um dich - siehst du gerade einen Vogel oder hörst du einen?"
|
||
|
||
STIL: Beruhigend, validierend, vereinfachend, ohne Druck.`
|
||
: `You are a patient learning companion. A child is expressing frustration or strong emotions and needs emotional support.
|
||
|
||
TASK:
|
||
1. Validate feelings: "I can understand that..." or "Sometimes that really is..."
|
||
2. Show empathy: "It's completely okay if..."
|
||
3. Offer support: "We can try it differently..."
|
||
4. Simplify approach: Ask a VERY simple, calming question
|
||
|
||
EXAMPLE: "I can understand that - sometimes new things are really confusing! It's completely okay if it's hard at first. We can try it differently. Just look around you - do you see a bird or hear one right now?"
|
||
|
||
STYLE: Calming, validating, simplifying, without pressure.`;
|
||
|
||
userPrompt = isGerman
|
||
? `Ein Kind drückt Frustration aus.
|
||
|
||
Frage: "${question}"
|
||
Emotionale Antwort: "${answer}"
|
||
Ursprüngliches Thema: "${originalTopic}"
|
||
|
||
Biete emotionale Unterstützung und vereinfache den Lernansatz.`
|
||
: `A child is expressing frustration.
|
||
|
||
Question: "${question}"
|
||
Emotional response: "${answer}"
|
||
Original topic: "${originalTopic}"
|
||
|
||
Provide emotional support and simplify the learning approach.`;
|
||
} else if (isPointingOutRepetition) {
|
||
// Child is pointing out that they already said something
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter. Ein Kind weist darauf hin, dass es etwas bereits gesagt hat.
|
||
|
||
AUFGABE:
|
||
1. Entschuldige dich: "Du hast recht, das hast du schon gesagt!"
|
||
2. Anerkenne die Wiederholung: "Danke, dass du mich daran erinnerst!"
|
||
3. Baue auf der vorherigen Antwort auf: "Du hattest gesagt [vorherige Antwort]..."
|
||
4. Stelle eine NEUE, weiterführende Frage
|
||
|
||
STIL: Entschuldigend, dankbar, aufbauend.`
|
||
: `You are a patient learning companion. A child is pointing out that they already said something.
|
||
|
||
TASK:
|
||
1. Apologize: "You're right, you already said that!"
|
||
2. Acknowledge repetition: "Thanks for reminding me!"
|
||
3. Build on previous answer: "You had said [previous answer]..."
|
||
4. Ask a NEW, advancing question
|
||
|
||
STYLE: Apologetic, grateful, building.`;
|
||
|
||
userPrompt = isGerman
|
||
? `Ein Kind weist auf Wiederholung hin.
|
||
|
||
Aktuelle Frage: "${question}"
|
||
Antwort: "${answer}"
|
||
Bisherige Antworten: ${recentAnswers || 'Keine verfügbar'}
|
||
|
||
Entschuldige dich und stelle eine neue, weiterführende Frage.`
|
||
: `A child is pointing out repetition.
|
||
|
||
Current question: "${question}"
|
||
Answer: "${answer}"
|
||
Previous answers: ${recentAnswers || 'None available'}
|
||
|
||
Apologize and ask a new, advancing question.`;
|
||
} else if (instructions) {
|
||
// Special instructions provided (e.g., for explanations)
|
||
systemPrompt = instructions;
|
||
userPrompt = isGerman
|
||
? `Bitte erkläre dies kindgerecht für ein 8-12-jähriges Kind: "${question}"
|
||
|
||
Kontext: Das Kind hat geantwortet: "${answer}"
|
||
Ursprüngliche Frage: "${originalTopic}"`
|
||
: `Please explain this in a child-friendly way for an 8-12 year old: "${question}"
|
||
|
||
Context: The child answered: "${answer}"
|
||
Original question: "${originalTopic}"`;
|
||
} else if (hasReachedKnowledgeLimit) {
|
||
// NEW: Child has reached knowledge limit on current fundamental - provide explanation and move to next
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter. Ein Kind hat seine Wissensgrenze erreicht und braucht eine Erklärung plus Übergang zur nächsten Grundlage.
|
||
|
||
AUFGABE:
|
||
1. Kurze Anerkennung: "Das ist völlig in Ordnung!"
|
||
2. KURZE aber vollständige Erklärung in 3-4 Sätzen
|
||
3. Übergang: "Jetzt verstehen wir [Grundlage]! Zur nächsten Grundlage: [nächste]..."
|
||
4. EINE Frage zur nächsten Grundlage
|
||
|
||
BEISPIEL: "Das ist völlig in Ordnung! Das Magnetfeld der Erde ist wie ein riesiger Schutzschild aus magnetischen Kräften um unseren Planeten. Es entsteht durch flüssiges Eisen im Erdkern und schützt uns vor gefährlichen Teilchen aus dem Weltraum. Jetzt verstehen wir das Magnetfeld! Zur nächsten Grundlage: Sonnenwind. Was denkst du, was Sonnenwind ist?"
|
||
|
||
WICHTIG: Vollständig aber prägnant halten!`
|
||
: `You are a patient learning companion. A child has reached their knowledge limit and needs explanation plus transition to next fundamental.
|
||
|
||
TASK:
|
||
1. Brief acknowledgment: "That's perfectly okay!"
|
||
2. BRIEF but complete explanation in 3-4 sentences
|
||
3. Transition: "Now we understand [fundamental]! Next fundamental: [next]..."
|
||
4. ONE question about next fundamental
|
||
|
||
EXAMPLE: "That's perfectly okay! Earth's magnetic field is like a giant protective shield of magnetic forces around our planet. It's created by liquid iron in Earth's core and protects us from dangerous particles from space. Now we understand the magnetic field! Next fundamental: solar wind. What do you think solar wind is?"
|
||
|
||
IMPORTANT: Complete but concise!`;
|
||
|
||
userPrompt = isGerman
|
||
? `Ein Kind hat seine Wissensgrenze bei einer Grundlage erreicht.
|
||
|
||
Aktuelle Frage: "${question}"
|
||
Antwort des Kindes: "${answer}"
|
||
Ursprüngliches Thema: "${originalTopic}"
|
||
Bisherige Fragen: ${recentQuestions || 'Keine'}
|
||
|
||
ERKLÄRE die aktuelle Grundlage KURZ und GEHE ZUR NÄCHSTEN GRUNDLAGE ÜBER.
|
||
|
||
Beispiel für Sonnenbrand:
|
||
- Erste Grundlage (UV-Strahlung) erklären → Zur zweiten Grundlage (Hautzellen) übergehen
|
||
- Zweite Grundlage (Hautzellen) erklären → Zur dritten Grundlage (Lichtenergie) übergehen`
|
||
: `A child has reached their knowledge limit on a fundamental.
|
||
|
||
Current question: "${question}"
|
||
Child's answer: "${answer}"
|
||
Original topic: "${originalTopic}"
|
||
Previous questions: ${recentQuestions || 'None'}
|
||
|
||
EXPLAIN the current fundamental BRIEFLY and MOVE TO THE NEXT FUNDAMENTAL.
|
||
|
||
Example for sunburn:
|
||
- First fundamental (UV radiation) explain → Move to second fundamental (skin cells)
|
||
- Second fundamental (skin cells) explain → Move to third fundamental (light energy)`;
|
||
} else if (context === 'repeated_dont_know' || dontKnowCount >= 2) {
|
||
// Child has said "don't know" multiple times - provide explanation
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter. Ein Kind hat mehrmals "weiß nicht" gesagt und braucht jetzt eine klare, verständliche Erklärung.
|
||
|
||
AUFGABE: Gib eine vollständige, aber kindgerechte Erklärung, die:
|
||
1. Altersgerecht ist (8-12 Jahre)
|
||
2. Mit einfachen Worten erklärt
|
||
3. Alltagsbeispiele verwendet
|
||
4. Neugier für weitere Entdeckungen weckt
|
||
5. Ermutigend und positiv ist
|
||
|
||
STIL: Freundlich, klar, mit konkreten Beispielen. Verwende "Du" und mache es persönlich.`
|
||
: `You are a patient learning companion. A child has said "don't know" multiple times and now needs a clear, understandable explanation.
|
||
|
||
TASK: Give a complete but child-friendly explanation that:
|
||
1. Is age-appropriate (8-12 years)
|
||
2. Uses simple words
|
||
3. Uses everyday examples
|
||
4. Sparks curiosity for further discoveries
|
||
5. Is encouraging and positive
|
||
|
||
STYLE: Friendly, clear, with concrete examples. Use "you" and make it personal.`;
|
||
|
||
userPrompt = isGerman
|
||
? `Ein Kind hat mehrmals "weiß nicht" gesagt bei der Frage: "${question}"
|
||
|
||
Letzte Antwort: "${answer}"
|
||
Ursprüngliches Thema: "${originalTopic}"
|
||
Bisherige Fragen: ${recentQuestions || 'Keine'}
|
||
|
||
Erkläre jetzt freundlich und verständlich, worum es geht.`
|
||
: `A child has said "don't know" multiple times to the question: "${question}"
|
||
|
||
Latest answer: "${answer}"
|
||
Original topic: "${originalTopic}"
|
||
Previous questions: ${recentQuestions || 'None'}
|
||
|
||
Now explain friendly and understandably what this is about.`;
|
||
} else if (context === 'confusion_explanation') {
|
||
// Child is confused and needs clarification
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter. Ein Kind ist verwirrt und braucht eine klare, beruhigende Erklärung.
|
||
|
||
AUFGABE: Gib eine beruhigende Erklärung, die Verwirrung auflöst:
|
||
1. Beginne mit Verständnis für die Verwirrung
|
||
2. Erkläre einfach und klar
|
||
3. Verwende bekannte Vergleiche
|
||
4. Baue das Vertrauen des Kindes wieder auf
|
||
5. Lade zu weiteren Fragen ein
|
||
|
||
STIL: Beruhigend, verständnisvoll, ermutigend.`
|
||
: `You are a patient learning companion. A child is confused and needs a clear, reassuring explanation.
|
||
|
||
TASK: Give a reassuring explanation that resolves confusion:
|
||
1. Start with understanding for the confusion
|
||
2. Explain simply and clearly
|
||
3. Use familiar comparisons
|
||
4. Rebuild the child's confidence
|
||
5. Invite further questions
|
||
|
||
STYLE: Reassuring, understanding, encouraging.`;
|
||
|
||
userPrompt = isGerman
|
||
? `Ein Kind ist verwirrt bei der Frage: "${question}"
|
||
|
||
Antwort des Kindes: "${answer}"
|
||
Ursprüngliches Thema: "${originalTopic}"
|
||
|
||
Hilf dem Kind, die Verwirrung zu überwinden.`
|
||
: `A child is confused about the question: "${question}"
|
||
|
||
Child's answer: "${answer}"
|
||
Original topic: "${originalTopic}"
|
||
|
||
Help the child overcome the confusion.`;
|
||
} else if (isDisengaged || isGivingShortAnswers) {
|
||
// Child is disengaged - change approach but STAY ON TOPIC
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter. Ein Kind zeigt Desinteresse beim aktuellen Thema und braucht einen völlig neuen Ansatz - aber BLEIBE beim GLEICHEN Thema!
|
||
|
||
WICHTIG: WECHSLE NICHT das Thema, sondern nur den ANSATZ!
|
||
|
||
AUFGABE: Verändere deinen Ansatz komplett, aber bleibe beim ursprünglichen Thema:
|
||
1. Erkenne das Desinteresse an ("Das kann ich verstehen...")
|
||
2. Bleibe beim GLEICHEN Thema "${originalTopic}", aber probiere einen völlig anderen Ansatz:
|
||
- Persönliche Verbindung: "Hast du schon mal...?"
|
||
- Vorstellungskraft: "Stell dir vor, du wärst..."
|
||
- Gefühle: "Was würde sich wohl anfühlen...?"
|
||
- Vergleiche: "Ist das ähnlich wie...?"
|
||
3. Mache das Thema persönlicher und relevanter
|
||
4. Stelle EINE kurze, neue Frage
|
||
|
||
BEISPIEL: Wenn Thema "Wie kann ein Vogel fliegen?" ist, frage: "Hast du schon mal einen Vogel ganz nah gesehen?" oder "Stell dir vor, du könntest fliegen wie ein Vogel - was würdest du zuerst machen?"
|
||
|
||
STIL: Kurz, persönlich, aber immer beim ursprünglichen Thema bleiben.`
|
||
: `You are a patient learning companion. A child is showing disinterest in the current topic and needs a completely new approach - but STAY ON THE SAME TOPIC!
|
||
|
||
IMPORTANT: Do NOT change the topic, only change the APPROACH!
|
||
|
||
TASK: Change your approach completely, but stay with the original topic:
|
||
1. Acknowledge the disinterest ("I can understand that...")
|
||
2. Stay with the SAME topic "${originalTopic}", but try a completely different approach:
|
||
- Personal connection: "Have you ever...?"
|
||
- Imagination: "Imagine if you were..."
|
||
- Feelings: "What would it feel like...?"
|
||
- Comparisons: "Is this similar to...?"
|
||
3. Make the topic more personal and relevant
|
||
4. Ask ONE short, new question
|
||
|
||
EXAMPLE: If topic is "How can a bird fly?", ask: "Have you ever seen a bird up close?" or "Imagine if you could fly like a bird - what would you do first?"
|
||
|
||
STYLE: Short, personal, but always stay with the original topic.`;
|
||
|
||
userPrompt = isGerman
|
||
? `Ein Kind wirkt desinteressiert. Letzte Antwort: "${answer}"
|
||
|
||
Das ursprüngliche Thema war: "${originalTopic}"
|
||
Bisherige Fragen: ${recentQuestions || 'Keine'}
|
||
|
||
WICHTIG: Bleibe beim gleichen Thema "${originalTopic}", aber verändere deinen Ansatz komplett. Mache es persönlicher und interessanter, aber verlasse NICHT das ursprüngliche Thema.`
|
||
: `A child seems disinterested. Latest answer: "${answer}"
|
||
|
||
The original topic was: "${originalTopic}"
|
||
Previous questions: ${recentQuestions || 'None'}
|
||
|
||
IMPORTANT: Stay with the same topic "${originalTopic}", but change your approach completely. Make it more personal and interesting, but do NOT leave the original topic.`;
|
||
} else {
|
||
// Standard response - continue with fundamentals approach
|
||
systemPrompt = isGerman
|
||
? `Du bist ein geduldiger Lernbegleiter für Kinder (8-12 Jahre). Verwende den FUNDAMENTALS FIRST Ansatz!
|
||
|
||
BISHERIGE UNTERHALTUNG:
|
||
- Ursprüngliche Frage: "${originalTopic}"
|
||
- Aktuelle Frage: "${question}"
|
||
- Antwort des Kindes: "${answer}"
|
||
|
||
AUFGABE: Führe das systematische Lernen der Grundlagen fort:
|
||
|
||
1. ANERKENNE kurz die Antwort des Kindes (1 Satz)
|
||
2. FÜHRE die Grundlagen-Erkundung weiter fort mit EINER gezielten Frage
|
||
|
||
SYSTEMATISCHER ANSATZ:
|
||
- Wenn das Kind noch bei der ersten Grundlage ist → vertiefe diese Grundlage
|
||
- Wenn die erste Grundlage verstanden ist → gehe zur nächsten Grundlage über
|
||
- Baue systematisch Wissen auf, Schritt für Schritt
|
||
|
||
ANTWORTFORMAT:
|
||
"[Kurze Anerkennung]! [Eine Frage zur aktuellen oder nächsten Grundlage]?"
|
||
|
||
STIL: Strukturiert, aufbauend, nur EINE Frage. MAXIMAL 25 Wörter!`
|
||
: `You are a patient learning companion for children (8-12 years). Use the FUNDAMENTALS FIRST approach!
|
||
|
||
PREVIOUS CONVERSATION:
|
||
- Original question: "${originalTopic}"
|
||
- Current question: "${question}"
|
||
- Child's answer: "${answer}"
|
||
|
||
TASK: Continue the systematic learning of fundamentals:
|
||
|
||
1. ACKNOWLEDGE the child's answer briefly (1 sentence)
|
||
2. CONTINUE the fundamentals exploration with ONE targeted question
|
||
|
||
SYSTEMATIC APPROACH:
|
||
- If the child is still on the first fundamental → deepen this fundamental
|
||
- If the first fundamental is understood → move to the next fundamental
|
||
- Build knowledge systematically, step by step
|
||
|
||
RESPONSE FORMAT:
|
||
"[Brief acknowledgment]! [One question about current or next fundamental]?"
|
||
|
||
STYLE: Structured, building, only ONE question. MAXIMUM 25 words!`;
|
||
|
||
userPrompt = isGerman
|
||
? `Kind hat geantwortet: "${answer}"
|
||
Auf die Frage: "${question}"
|
||
|
||
BEREITS GESTELLTE FRAGEN (vermeide Wiederholung):
|
||
${recentQuestions || 'Keine bisherigen Fragen'}
|
||
|
||
Ursprüngliches Thema: "${originalTopic}"
|
||
|
||
FÜHRE DEN FUNDAMENTALS FIRST ANSATZ FORT:
|
||
- Anerkenne die Antwort kurz
|
||
- Stelle eine Frage, die systematisch das Verständnis der Grundlagen aufbaut
|
||
- Wenn eine Grundlage verstanden ist, gehe zur nächsten über
|
||
|
||
BEISPIEL: "Genau! Was passiert wohl mit UV-Strahlung, wenn sie auf die Haut trifft?"
|
||
|
||
Bleibe strukturiert und systematisch!`
|
||
: `Child answered: "${answer}"
|
||
To the question: "${question}"
|
||
|
||
ALREADY ASKED QUESTIONS (avoid repetition):
|
||
${recentQuestions || 'No previous questions'}
|
||
|
||
Original topic: "${originalTopic}"
|
||
|
||
CONTINUE THE FUNDAMENTALS FIRST APPROACH:
|
||
- Acknowledge the answer briefly
|
||
- Ask a question that systematically builds understanding of fundamentals
|
||
- If one fundamental is understood, move to the next
|
||
|
||
EXAMPLE: "Exactly! What do you think happens when UV radiation hits the skin?"
|
||
|
||
Stay structured and systematic!`;
|
||
}
|
||
|
||
try {
|
||
const completion = await openai.chat.completions.create({
|
||
model: "gpt-4o-mini",
|
||
messages: [
|
||
{ role: "system", content: systemPrompt },
|
||
{ role: "user", content: userPrompt }
|
||
],
|
||
max_tokens: hasReachedKnowledgeLimit ? 350 : 120, // Even more tokens for comprehensive knowledge limit explanations
|
||
temperature: 0.7
|
||
});
|
||
|
||
const response = completion.choices[0]?.message?.content || '';
|
||
console.log('✅ Conversation response generated:', response);
|
||
|
||
return response;
|
||
|
||
} catch (error) {
|
||
console.log('❌ Conversation response error:', error.message);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// API endpoint to get the next fundamental concept
|
||
app.post('/api/next-fundamental', async (req, res) => {
|
||
const { context, sessionId, language, currentTopic } = req.body;
|
||
|
||
// Accept both 'next_fundamental' context and requests without context for flexibility
|
||
if (context && context !== 'next_fundamental') {
|
||
return res.status(400).json({ error: 'Invalid context' });
|
||
}
|
||
|
||
try {
|
||
console.log(`🌟 Generating next fundamental for session ${sessionId}`);
|
||
|
||
// Default language is German
|
||
const isEnglish = language === 'en';
|
||
|
||
const nextFundamental = {
|
||
summary: isEnglish
|
||
? "Let's explore another important concept: forces and motion. Everything in our world moves because of different forces acting upon it."
|
||
: "Lass uns eine andere wichtige Grundlage erforschen: Kräfte und Bewegung. Alles in unserer Welt bewegt sich aufgrund verschiedener Kräfte, die darauf einwirken.",
|
||
question: isEnglish
|
||
? "Have you ever wondered why a ball stops rolling after you kick it? What do you think causes it to slow down and stop?"
|
||
: "Hast du dich schon einmal gefragt, warum ein Ball aufhört zu rollen, nachdem du ihn getreten hast? Was denkst du, verursacht seine Verlangsamung und sein Anhalten?"
|
||
};
|
||
|
||
// Return in the expected format to match other endpoints
|
||
res.json({
|
||
success: true,
|
||
guidance: {
|
||
type: 'next-fundamental',
|
||
steps: [{
|
||
id: 1,
|
||
text: nextFundamental.summary + '\n\n' + nextFundamental.question,
|
||
type: 'question'
|
||
}]
|
||
},
|
||
nextFundamental: nextFundamental
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('Error generating next fundamental:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
error: 'Failed to generate next fundamental',
|
||
guidance: {
|
||
type: 'fallback',
|
||
steps: [{
|
||
id: 1,
|
||
text: language === 'en'
|
||
? 'Let\'s talk about another important concept... What do you already know about cause and effect?'
|
||
: 'Lass uns über eine andere wichtige Grundlage sprechen... Was weißt du bereits über Ursache und Wirkung?',
|
||
type: 'question'
|
||
}]
|
||
}
|
||
});
|
||
}
|
||
});
|