- Complete KidsAI Explorer application - Multi-language support (English/German) - AI-powered educational guidance using OpenAI - Interactive chat interface for children - Proper placeholder translation fixes - Mobile-responsive design - Educational framework for critical thinking
651 lines
27 KiB
JavaScript
Executable File
651 lines
27 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();
|
||
|
||
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;
|
||
|
||
// Educational prompts for AI to guide thinking instead of giving direct answers
|
||
const EDUCATIONAL_PROMPTS = {
|
||
en: {
|
||
systemPrompt: "You are an educational assistant for children. Instead of giving direct answers, ask 2-3 guiding questions that help children think through the problem themselves. Be encouraging and use simple language. Focus on the thinking process, not the 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 Lernassistent für Kinder. Anstatt direkte Antworten zu geben, stelle 2-3 Leitfragen, die Kindern helfen, das Problem selbst zu durchdenken. Sei ermutigend und verwende einfache Sprache. Konzentriere dich auf den Denkprozess, nicht auf die 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' } = req.body;
|
||
|
||
if (!question || question.trim().length === 0) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: 'Question is required'
|
||
});
|
||
}
|
||
|
||
try {
|
||
// Get AI-powered guidance
|
||
const aiGuidance = await getAIGuidance(question, language);
|
||
|
||
res.json({
|
||
success: true,
|
||
guidance: aiGuidance,
|
||
question: question,
|
||
language: language
|
||
});
|
||
|
||
} 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
|
||
});
|
||
}
|
||
});
|
||
|
||
// 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) {
|
||
console.log('🤖 Calling OpenAI with GPT-3.5-turbo...');
|
||
|
||
const isGerman = language === 'de';
|
||
|
||
const systemPrompt = isGerman
|
||
? "Du bist ein geduldiger Lehrer für Kinder. Anstatt direkte Antworten zu geben, führe das Kind Schritt für Schritt zum Verständnis. Stelle 3-4 aufbauende Fragen, die das Kind zum Nachdenken anregen und ihm helfen, die Antwort selbst zu entdecken. Jede Frage sollte auf der vorherigen aufbauen und dem Kind helfen, das Konzept zu verstehen. Verwende einfache Sprache und ermutigende Worte. Formatiere als nummerierte Liste."
|
||
: "You are a patient teacher for children. Instead of giving direct answers, guide the child step by step to understanding. Ask 3-4 building questions that encourage the child to think and help them discover the answer themselves. Each question should build on the previous one and help the child understand the concept. Use simple language and encouraging words. Format as a numbered list.";
|
||
|
||
const userPrompt = isGerman
|
||
? `Ein Kind hat gefragt: "${question}". Führe es Schritt für Schritt zum Verständnis, ohne die Antwort direkt zu verraten. Stelle aufbauende Fragen, die dem Kind helfen, selbst zu denken und die Antwort zu entdecken. Jede Frage sollte das Kind näher zur Lösung führen.`
|
||
: `A child asked: "${question}". Guide them step by step to understanding without giving away the answer directly. Ask building questions that help the child think for themselves and discover the answer. Each question should bring the child closer to the solution.`;
|
||
|
||
try {
|
||
const completion = await openai.chat.completions.create({
|
||
model: "gpt-3.5-turbo",
|
||
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.substring(0, 100) + '...');
|
||
|
||
// Parse the response into steps
|
||
const steps = parseOpenAIResponseToSteps(aiResponse, language);
|
||
|
||
return {
|
||
type: 'ai-powered',
|
||
steps: steps,
|
||
encouragement: getRandomEncouragement(language, question),
|
||
source: 'OpenAI GPT-3.5'
|
||
};
|
||
|
||
} catch (error) {
|
||
console.log('❌ OpenAI error:', error.message);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Function to parse OpenAI response into thinking steps
|
||
function parseOpenAIResponseToSteps(text, language) {
|
||
const lines = text.split('\n').filter(line => line.trim());
|
||
const steps = [];
|
||
|
||
lines.forEach((line, index) => {
|
||
const trimmed = line.trim();
|
||
// Look for numbered items or questions
|
||
if (trimmed && (trimmed.match(/^\d+\./) || trimmed.includes('?') || trimmed.length > 10)) {
|
||
// Clean up numbering and formatting
|
||
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '').trim();
|
||
if (cleaned.length > 5) {
|
||
steps.push({
|
||
id: steps.length + 1,
|
||
text: cleaned,
|
||
type: 'question'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
// Ensure we have at least 2 steps
|
||
if (steps.length < 2) {
|
||
const fallback = getFallbackGuidance('', language);
|
||
return fallback.steps.slice(0, 3);
|
||
}
|
||
|
||
return steps.slice(0, 4); // Limit to 4 steps max
|
||
}
|
||
|
||
// 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) {
|
||
// Try OpenAI first (most reliable)
|
||
if (process.env.OPENAI_API_KEY) {
|
||
try {
|
||
return await getOpenAIGuidance(question, language);
|
||
} 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
|
||
if (questionLower.includes('bird') || questionLower.includes('fly') || questionLower.includes('wing')) {
|
||
const birdQuestions = isGerman ? [
|
||
{ id: 1, text: "Was weißt du bereits über Vögel und ihre Flügel?", type: 'question' },
|
||
{ id: 2, text: "Wie denkst du, nutzen Vögel ihre Flügel, um sich durch die Luft zu bewegen?", type: 'question' },
|
||
{ id: 3, text: "Kannst du an andere Tiere denken, die fliegen können, und wie unterscheiden sie sich in ihren Flugfähigkeiten?", type: 'question' }
|
||
] : [
|
||
{ id: 1, text: "What do you already know about this topic?", type: 'question' },
|
||
{ id: 2, text: "How do birds use their wings to generate lift and propel themselves through the air?", type: 'question' },
|
||
{ id: 3, text: "Can you think of any other animals that can fly like birds, and how do they differ in their flying abilities?", type: 'question' }
|
||
];
|
||
|
||
return {
|
||
type: 'topic-specific',
|
||
steps: birdQuestions,
|
||
encouragement: getRandomEncouragement(language, question),
|
||
source: 'Educational Framework'
|
||
};
|
||
}
|
||
|
||
if (questionLower.includes('sky') || questionLower.includes('blue') || questionLower.includes('himmel') || questionLower.includes('blau')) {
|
||
const skyQuestions = isGerman ? [
|
||
{ id: 1, text: "Was weißt du bereits über Licht und Farben?", type: 'question' },
|
||
{ id: 2, text: "Was passiert mit Licht, wenn es durch die Luft geht?", type: 'question' },
|
||
{ id: 3, text: "Warum siehst du manche Farben deutlicher als andere?", type: 'question' }
|
||
] : [
|
||
{ id: 1, text: "What do you already know about light and colors?", type: 'question' },
|
||
{ id: 2, text: "What happens to light when it travels through the air?", type: 'question' },
|
||
{ id: 3, text: "Why do you see some colors more clearly than others?", type: 'question' }
|
||
];
|
||
|
||
return {
|
||
type: 'topic-specific',
|
||
steps: skyQuestions,
|
||
encouragement: getRandomEncouragement(language, question),
|
||
source: 'Educational Framework'
|
||
};
|
||
}
|
||
|
||
// Generic fallback questions
|
||
const fallbackSteps = isGerman ? [
|
||
{ id: 1, text: "Was weißt du bereits über dieses Thema?", type: 'question' },
|
||
{ id: 2, text: "Welche Teile der Frage verstehst du, und welche sind unklar?", type: 'question' },
|
||
{ id: 3, text: "Wo könntest du mehr Informationen finden?", type: 'question' },
|
||
{ id: 4, text: "Kannst du das Problem in kleinere Teile aufteilen?", type: 'question' }
|
||
] : [
|
||
{ id: 1, text: "What do you already know about this topic?", type: 'question' },
|
||
{ id: 2, text: "Which parts of the question do you understand, and which are unclear?", type: 'question' },
|
||
{ id: 3, text: "Where could you find more information about this?", type: 'question' },
|
||
{ id: 4, text: "Can you break this problem down into smaller parts?", 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 hilfreicher Assistent für Kinder. Gib eine klare, einfache und altersgerechte Antwort auf die Frage. Verwende einfache Sprache und erkläre es so, dass ein Kind es verstehen kann. Halte die Antwort kurz aber vollständig."
|
||
: "You are a helpful assistant for children. Provide a clear, simple, and age-appropriate answer to the question. Use simple language and explain it in a way a child can understand. Keep the answer concise but complete.";
|
||
|
||
const userPrompt = isGerman
|
||
? `Ein Kind möchte wissen: "${question}". Gib eine einfache, klare Antwort.`
|
||
: `A child wants to know: "${question}". Provide a simple, clear answer.`;
|
||
|
||
try {
|
||
if (process.env.OPENAI_API_KEY) {
|
||
const completion = await openai.chat.completions.create({
|
||
model: "gpt-3.5-turbo",
|
||
messages: [
|
||
{ role: "system", content: systemPrompt },
|
||
{ role: "user", content: userPrompt }
|
||
],
|
||
max_tokens: 150,
|
||
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-3.5'
|
||
};
|
||
}
|
||
} catch (error) {
|
||
console.log('❌ OpenAI answer error:', error.message);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Fallback answers for common questions
|
||
function getFallbackAnswer(question, language) {
|
||
const isGerman = language === 'de';
|
||
|
||
// Simple pattern matching for common questions
|
||
const questionLower = question.toLowerCase();
|
||
|
||
if (questionLower.includes('sky') || questionLower.includes('himmel') || questionLower.includes('blue') || questionLower.includes('blau')) {
|
||
return {
|
||
type: 'rule-based',
|
||
text: isGerman
|
||
? "Der Himmel ist blau, weil winzig kleine Teilchen in der Luft das blaue Licht mehr streuen als andere Farben. Es ist wie wenn du blaues Licht durch viele kleine Glaskugeln scheinst!"
|
||
: "The sky is blue because tiny particles in the air scatter blue light more than other colors. It's like shining blue light through many tiny glass balls!",
|
||
source: 'Educational Framework'
|
||
};
|
||
}
|
||
|
||
if (questionLower.includes('bird') || questionLower.includes('fly') || questionLower.includes('vogel') || questionLower.includes('fliegen')) {
|
||
return {
|
||
type: 'rule-based',
|
||
text: isGerman
|
||
? "Vögel können fliegen, weil ihre Flügel eine besondere Form haben und sie sehr leichte, hohle Knochen haben. Sie schlagen mit den Flügeln und erzeugen Auftrieb, der sie in die Luft hebt!"
|
||
: "Birds can fly because their wings have a special shape and they have very light, hollow bones. They flap their wings to create lift that pushes them up into the air!",
|
||
source: 'Educational Framework'
|
||
};
|
||
}
|
||
|
||
if (questionLower.includes('1+1') || questionLower.includes('add') || questionLower.includes('plus')) {
|
||
return {
|
||
type: 'rule-based',
|
||
text: isGerman
|
||
? "1 + 1 = 2! Wenn du einen Apfel hast und noch einen dazu bekommst, hast du zwei Äpfel. Das ist Addition - zusammenzählen!"
|
||
: "1 + 1 = 2! When you have one apple and get another one, you have two apples. That's addition - adding together!",
|
||
source: 'Educational Framework'
|
||
};
|
||
}
|
||
|
||
// Generic fallback
|
||
return {
|
||
type: 'rule-based',
|
||
text: isGerman
|
||
? "Das ist eine tolle Frage! Die Antwort hängt von vielen Faktoren ab. Ich empfehle dir, in einem Buch nachzuschauen oder einen Erwachsenen zu fragen, der mehr darüber weiß."
|
||
: "That's a great question! The answer depends on many factors. I recommend looking it up in a book or asking an adult who knows more about this topic.",
|
||
source: 'Educational Framework'
|
||
};
|
||
}
|
||
|
||
// API endpoint for responding to user answers contextually
|
||
app.post('/api/respond-to-answer', async (req, res) => {
|
||
try {
|
||
const { answer, question, language = 'en', stepIndex } = req.body;
|
||
|
||
if (!answer || !question) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: 'Answer and question are required'
|
||
});
|
||
}
|
||
|
||
console.log(`📝 Generating response to answer: "${answer}" for question: "${question}"`);
|
||
|
||
// Create contextual prompt for responding to the user's answer
|
||
const isGerman = language === 'de';
|
||
|
||
const contextualPrompt = isGerman ?
|
||
`Du bist ein pädagogischer KI-Assistent für Kinder. Ein Kind hat gerade auf eine Frage geantwortet.
|
||
|
||
FRAGE: "${question}"
|
||
ANTWORT DES KINDES: "${answer}"
|
||
|
||
Deine Aufgabe ist es, eine ermutigende, pädagogische Antwort zu geben, die:
|
||
1. Das Kind für seine Antwort ermutigt (egal ob richtig oder falsch)
|
||
2. Auf ihrer spezifischen Antwort aufbaut
|
||
3. Ihr Denken würdigt und erweitert
|
||
4. Verwende Emojis sparsam und altersgerecht
|
||
|
||
WICHTIG: Stelle KEINE Folgefragen - antworte nur mit Ermutigung und Bestätigung in 1-2 Sätzen.` :
|
||
`You are an educational AI assistant for children. A child just answered a question.
|
||
|
||
QUESTION: "${question}"
|
||
CHILD'S ANSWER: "${answer}"
|
||
|
||
Your task is to provide an encouraging, educational response that:
|
||
1. Encourages the child for their answer (regardless of right or wrong)
|
||
2. Builds on their specific answer
|
||
3. Acknowledges and expands their thinking
|
||
4. Use emojis sparingly and age-appropriately
|
||
|
||
IMPORTANT: Do NOT ask follow-up questions - only respond with encouragement and validation in 1-2 sentences.`;
|
||
|
||
// Try OpenAI first
|
||
if (process.env.OPENAI_API_KEY) {
|
||
try {
|
||
const completion = await openai.chat.completions.create({
|
||
model: "gpt-3.5-turbo",
|
||
messages: [
|
||
{
|
||
role: "system",
|
||
content: contextualPrompt
|
||
}
|
||
],
|
||
max_tokens: 200,
|
||
temperature: 0.7
|
||
});
|
||
|
||
const aiResponse = completion.choices[0]?.message?.content?.trim();
|
||
|
||
if (aiResponse && aiResponse.length > 10) {
|
||
console.log('✅ OpenAI response generated successfully');
|
||
return res.json({
|
||
success: true,
|
||
response: aiResponse,
|
||
source: 'OpenAI GPT-3.5'
|
||
});
|
||
}
|
||
} catch (openaiError) {
|
||
console.log('❌ OpenAI error:', openaiError.message);
|
||
}
|
||
}
|
||
|
||
// Fallback: Generate a contextual response based on answer content
|
||
const answerLower = answer.toLowerCase();
|
||
let fallbackResponse;
|
||
|
||
if (answerLower.includes("don't know") || answerLower.includes("no idea") || answer.trim().length < 3) {
|
||
fallbackResponse = isGerman ?
|
||
"🌟 Das ist völlig in Ordnung! Nicht zu wissen bedeutet, dass wir zusammen etwas Neues entdecken können." :
|
||
"🌟 That's completely okay! Not knowing means we get to discover something new together.";
|
||
} else if (answer.length < 15) {
|
||
fallbackResponse = isGerman ?
|
||
`💡 Interessante Überlegung zu "${answer}"! Du denkst in die richtige Richtung.` :
|
||
`💡 Interesting thinking about "${answer}"! You're on the right track.`;
|
||
} else {
|
||
fallbackResponse = isGerman ?
|
||
`🎯 Ausgezeichnete Antwort! Du denkst wirklich gut über "${answer}" nach. Das zeigt, dass du das Konzept verstehst.` :
|
||
`🎯 Excellent answer! You're really thinking well about "${answer}". This shows you understand the concept.`;
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
response: fallbackResponse,
|
||
source: 'Contextual Fallback'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('❌ Error in respond-to-answer:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
error: 'Failed to generate response'
|
||
});
|
||
}
|
||
});
|