Files
kidsai/html/kidsai/server_backup.js
root 4b4c349c81 feat: Implement intelligent question type analysis and guided learning
- Add question type classification (mathematical, factual_simple, exploratory)
- Implement conditional UI: no text fields for math questions
- Add math-specific step-by-step guidance without revealing answers
- Enhanced AI prompts for subject-specific teaching approaches
- Remove unnecessary action buttons (Research Ideas, Try Experiments, Discuss with Others)
- Improve user experience with focused, educational interactions
2025-06-29 12:33:49 +02:00

418 lines
16 KiB
JavaScript
Executable File
Raw Blame History

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', // Smaller, more reliable model
'google/flan-t5-small', // Good for instructions
'facebook/blenderbot-400M-distill', // Conversational AI
'microsoft/DialoGPT-medium' // Original choice
];
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
});
}
});
// 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 pädagogischer Assistent für Kinder. Anstatt direkte Antworten zu geben, stellst du 3-4 durchdachte Leitfragen, die Kindern helfen, selbst über das Problem nachzudenken. Verwende einfache Sprache, sei ermutigend und konzentriere dich auf den Denkprozess. Formatiere als nummerierte Liste."
: "You are an educational assistant for children. Instead of giving direct answers, provide 3-4 thoughtful guiding questions that help children think through the problem themselves. Use simple language, be encouraging, and focus on the thinking process. Format as a numbered list.";
const userPrompt = isGerman
? `Ein Kind hat gefragt: "${question}". Hilf ihm dabei, selbst über die Antwort nachzudenken, indem du Leitfragen stellst.`
: `A child asked: "${question}". Help them think through the answer themselves by providing guiding questions.`;
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),
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 AI-powered educational guidance with model fallbacks (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}`);
// Create educational prompt
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();
// Check if the model is still loading
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}`);
// Extract and format the AI response
let aiText = data[0]?.generated_text || '';
if (aiText.trim().length > 10) {
const steps = parseAIResponseToSteps(aiText, language);
return {
type: 'ai-powered',
steps: steps,
encouragement: getRandomEncouragement(language),
source: `Hugging Face (${model.split('/')[1]})`
};
}
}
} catch (error) {
console.log(`⚠️ Model ${model} failed:`, error.message);
continue;
}
}
// If all models fail, throw error to trigger fallback
throw new Error('All Hugging Face models are currently unavailable');
}
// 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');
}
// 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}`);
// Create educational prompt
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
}
})
});
console.log(`<EFBFBD> Model ${model} response status:`, response.status);
if (response.ok) {
const data = await response.json();
// Check if the model is still loading
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}:`, JSON.stringify(data, null, 2));
// Extract and format the AI response
let aiText = data[0]?.generated_text || '';
if (aiText.trim().length > 10) {
const steps = parseAIResponseToSteps(aiText, language);
return {
type: 'ai-powered',
steps: steps,
encouragement: getRandomEncouragement(language),
source: `Hugging Face AI (${model.split('/')[1]})`
};
}
} else {
const errorText = await response.text();
console.log(`❌ Model ${model} HTTP error ${response.status}:`, errorText);
}
} catch (error) {
console.log(`⚠️ Model ${model} failed:`, error.message);
continue;
}
}
// If all models fail, throw error to trigger fallback
throw new Error('All AI models are currently unavailable');
}
// Function to parse AI response into thinking steps
function parseAIResponseToSteps(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) {
// Remove numbering if present
const cleaned = trimmed.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '');
if (cleaned.length > 5) {
steps.push({
id: index + 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
}
// Fallback guidance for when AI is unavailable
function getFallbackGuidance(question, language) {
const isGerman = language === 'de';
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),
source: 'Educational Framework'
};
}
// Get random encouragement
function getRandomEncouragement(language) {
const encouragements = language === 'de' ? [
"Großartige Frage! Du denkst wie ein echter Forscher! 🔬",
"Super! Lass uns das zusammen herausfinden! 🚀",
"Wow, das ist eine kluge Frage! 🤔",
"Du bist auf dem richtigen Weg! 🌟"
] : [
"Great question! You're thinking like a real scientist! 🔬",
"Awesome! Let's figure this out together! 🚀",
"Wow, that's a smart question! 🤔",
"You're on the right track! 🌟"
];
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() });
});
app.listen(PORT, () => {
console.log(`🚀 KidsAI Explorer server running on port ${PORT}`);
console.log(`📖 Visit http://localhost:${PORT} to start exploring!`);
});
module.exports = app;