{"repo": "balveersinghrajput/interviwe", "files": {"server/services/ai.js": {"85a8dfd6d0708836d582a832aabd54931252d0d0": {"t": 1785746700.866445, "n": 41787, "h": "776abec32de731b5", "c": "require('dotenv').config();\n\n/**\n * Universal AI Service\n * Supports OpenAI, OpenRouter, Groq, DeepSeek, Ollama (Local), Claude, or any OpenAI-compatible API.\n */\n\nasync function callAI(prompt) {\n // Always reload dotenv with override: true so changes in .env take effect immediately\n require('dotenv').config({ override: true });\n\n let provider = (process.env.AI_PROVIDER || 'openai').trim().toLowerCase();\n let apiKey = (process.env.AI_API_KEY || process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY || process.env.GEMINI_API_KEY || '').trim();\n\n // Strip inline comments if any\n if (apiKey.includes('#')) {\n apiKey = apiKey.split('#')[0].trim();\n }\n\n if (!apiKey || apiKey.includes('PASTE_') || apiKey.includes('your_api') || apiKey.includes('...')) {\n throw new Error('Please paste your real API key (from Groq, OpenAI, etc.) into server/.env file under AI_API_KEY=');\n }\n\n // Auto-detect provider from API key format\n if (apiKey.startsWith('gsk_')) {\n provider = 'groq';\n } else if (apiKey.startsWith('sk-or-')) {\n provider = 'openrouter';\n }\n\n let model = (process.env.AI_MODEL || '').trim();\n if (!model) {\n if (provider === 'groq') model = 'llama-3.3-70b-versatile';\n else if (provider === 'openrouter') model = 'meta-llama/llama-3.3-70b-instruct';\n else if (provider === 'deepseek') model = 'deepseek-chat';\n else if (provider === 'ollama') model = 'llama3';\n else model = 'gpt-4o-mini';\n }\n\n let baseUrl = (process.env.AI_BASE_URL || '').trim();\n if (!baseUrl) {\n if (provider === 'groq') baseUrl = 'https://api.groq.com/openai/v1';\n else if (provider === 'openrouter') baseUrl = 'https://openrouter.ai/api/v1';\n else if (provider === 'deepseek') baseUrl = 'https://api.deepseek.com/v1';\n else if (provider === 'ollama') baseUrl = 'http://localhost:11434/v1';\n else baseUrl = 'https://api.openai.com/v1';\n }\n\n // Remove trailing slashes\n baseUrl = baseUrl.replace(/\\/+$/, '');\n\n // 1. Gemini Fallback if provider explicitly set to gemini\n if (provider === 'gemini') {\n const { GoogleGenerativeAI } = require('@google/generative-ai');\n const genAI = new GoogleGenerativeAI(apiKey);\n const geminiModel = genAI.getGenerativeModel({ model: model || 'gemini-2.0-flash' });\n const res = await geminiModel.generateContent(prompt);\n return res.response.text();\n }\n\n // 2. OpenAI / OpenRouter / Groq / DeepSeek / Ollama / Custom API (Standard Chat Completion)\n const endpoint = `${baseUrl}/chat/completions`;\n const headers = {\n 'Content-Type': 'application/json'\n };\n\n if (apiKey) {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (provider === 'openrouter') {\n headers['HTTP-Referer'] = 'http://localhost:3000';\n headers['X-Title'] = 'InterviewHub';\n }\n\n const maxTokens = provider === 'groq' ? 2500 : 8192;\n\n const body = {\n model: model,\n messages: [\n {\n role: 'system',\n content: 'You are a Principal Software Engineer and Technical Interviewer. Always respond in strict valid JSON format. All code snippets in string fields must have properly escaped quotes and newline characters.'\n },\n {\n role: 'user',\n content: prompt\n }\n ],\n temperature: 0.2,\n max_tokens: maxTokens,\n response_format: { type: 'json_object' }\n };\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`❌ AI API Error (${provider}):`, response.status, errorText);\n\n // Auto-retry with lighter model / lower max_tokens if HTTP 400, 413 (TPM limit), or 429\n if (response.status === 400 || response.status === 413 || response.status === 429) {\n console.log('🔄 Retrying AI call with fallback parameters and model...');\n delete body.response_format;\n if (provider === 'groq') {\n body.model = 'llama-3.1-8b-instant';\n body.max_tokens = 2500;\n } else {\n body.max_tokens = 2000;\n }\n const retryRes = await fetch(endpoint, { method: 'POST', headers, body: JSON.stringify(body) });\n if (retryRes.ok) {\n const retryData = await retryRes.json();\n return retryData.choices && retryData.choices[0] && retryData.choices[0].message ? retryData.choices[0].message.content : '';\n }\n }\n\n if (response.status === 401) {\n throw new Error(`Invalid API Key for ${provider.toUpperCase()}. Please check your key in server/.env.`);\n }\n if (response.status === 429) {\n throw new Error(`Rate limit reached for ${provider.toUpperCase()}. Please wait a few seconds and try again.`);\n }\n throw new Error(`AI API call failed [${response.status}]: ${errorText}`);\n }\n\n const data = await response.json();\n const content = data.choices && data.choices[0] && data.choices[0].message ? data.choices[0].message.content : '';\n return content;\n}\n\nfunction cleanJSONResponse(rawText) {\n if (!rawText) throw new Error(\"AI returned an empty response. Please try again.\");\n let cleaned = rawText.trim();\n\n // Strip ```json markdown blocks if present\n if (cleaned.startsWith('```')) {\n cleaned = cleaned.replace(/^```[a-z]*\\n?/i, '').replace(/\\n?```$/i, '').trim();\n }\n\n // Extract JSON object substring\n const match = cleaned.match(/\\{[\\s\\S]*\\}/);\n if (match) {\n cleaned = match[0];\n }\n\n try {\n return JSON.parse(cleaned);\n } catch (err) {\n // 1. Try sanitizing control characters\n try {\n const sanitized = cleaned\n .replace(/[\\u0000-\\u001F]+/g, (m) => {\n if (m === '\\n') return '\\\\n';\n if (m === '\\r') return '\\\\r';\n if (m === '\\t') return '\\\\t';\n return '';\n });\n return JSON.parse(sanitized);\n } catch (e) {\n // 2. Salvage partial questions array if JSON was truncated by max tokens\n try {\n const questionsMatch = cleaned.match(/\"questions\"\\s*:\\s*\\[([\\s\\S]*)/);\n if (questionsMatch) {\n let questionsStr = questionsMatch[1];\n const lastObjEnd = questionsStr.lastIndexOf('}');\n if (lastObjEnd !== -1) {\n let validArrayStr = '[' + questionsStr.substring(0, lastObjEnd + 1) + ']';\n validArrayStr = validArrayStr.replace(/[\\u0000-\\u001F]+/g, (m) => (m === '\\n' ? '\\\\n' : m === '\\t' ? '\\\\t' : ''));\n const parsedQuestions = JSON.parse(validArrayStr);\n if (Array.isArray(parsedQuestions) && parsedQuestions.length > 0) {\n return {\n explanation: \"Successfully extracted questions from document\",\n technologies: [\"Java\", \"DSA\"],\n questions: parsedQuestions\n };\n }\n }\n }\n } catch (salvageErr) {\n console.error(\"Salvage error:\", salvageErr);\n }\n console.error(\"JSON parse error on raw text snippet:\", rawText.substring(0, 300));\n throw new Error(\"AI returned invalid JSON format. Please click Analyze again.\");\n }\n }\n}\n\nasync function analyzeFile(fileContent, fileName, fileType) {\n const prompt = `Analyze the following technical file/document/code content thoroughly and extract structured interview questions & answers across ALL sections.\n\nCRITICAL INSTRUCTIONS FOR ACCURATE EXTRACTION:\n1. DO NOT summarize, abbreviate, or shorten any code snippets or output comments! Preserve the exact, full code (including import statements, class headers, and main methods) in \"codeExample\".\n2. Include the exact output comment (e.g. \"// output :- ...\") and step-by-step logic in the \"answer\" field verbatim.\n3. Extract EVERY distinct topic, pattern, or code block present in the text (such as Variables, Conditional Statements, Switch Statement, Loops, Methods, Star Patterns, Arrays, Basic Sorting, 2D Arrays, String Manipulation, Bit Manipulation, OOP Concepts, Recursion, ArrayList, LinkedList, Doubly LinkedList, Cycle Detection, etc.).\n4. Assign clear, descriptive question titles based on the topic comments or logic (e.g. \"Variables in Java - Declaration & Print\", \"Check Even or Odd Number using Modulo\", \"Tax Calculator Logic\", \"Print Largest of 3 Numbers\", \"Butterfly Star Pattern\", \"Reverse a Linked List\", \"Detect Cycle in Linked List using Slow & Fast Pointers\").\n5. Auto-detect language precisely: Use \"DSA (Java)\" for Java algorithms & Data Structures, \"Java\" for core Java, \"Python\", \"JavaScript\", \"C++\", \"SQL\", etc.\n6. Map category accurately:\n - \"Basics\" for Variables, Conditionals, Loops, Switch Statements, Methods\n - \"OOP\" for Classes, Inheritance, Constructors, Abstraction, Polymorphism\n - \"Data Structures\" for Arrays, 2D Arrays, Strings, ArrayList, LinkedList, Doubly LinkedList\n - \"Algorithms\" for Sorting (Bubble, Selection, Insertion), Binary Search, Linear Search, Recursion, Bit Manipulation\n - \"Design Patterns\", \"Database\", \"System Design\"\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Brief overview of the entire document and topics covered\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|Java|Python|JavaScript|...\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"Descriptive Question Title\",\n \"answer\": \"Detailed answer with exact output explanation verbatim\",\n \"codeExample\": \"Full intact code snippet with all imports, class, and main method\"\n }\n ]\n}\n\nFILE NAME: ${fileName}\nFILE TYPE: ${fileType}\nCONTENT:\n${fileContent}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nasync function analyzeImage(imageBase64, mimeType, fileName) {\n require('dotenv').config({ override: true });\n let apiKey = (process.env.AI_API_KEY || process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY || process.env.GEMINI_API_KEY || '').trim();\n if (apiKey.includes('#')) apiKey = apiKey.split('#')[0].trim();\n\n // 1. Try Gemini Vision if Gemini API key or gemini provider\n if (apiKey.startsWith('AIza') || (process.env.AI_PROVIDER || '').toLowerCase() === 'gemini') {\n try {\n const { GoogleGenerativeAI } = require('@google/generative-ai');\n const genAI = new GoogleGenerativeAI(apiKey);\n const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });\n const prompt = `Analyze this image (which may be a screenshot of code, diagram, question paper, resume, or technical notes).\nExtract all technical details and generate 8-15 technical interview questions with full detailed answers and code examples.\n\nReturn ONLY valid JSON:\n{\n \"explanation\": \"...\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"Java|JavaScript|Python|React|DSA (Java)|System Design\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n const imagePart = {\n inlineData: {\n data: imageBase64,\n mimeType: mimeType || 'image/jpeg'\n }\n };\n const res = await model.generateContent([prompt, imagePart]);\n return cleanJSONResponse(res.response.text());\n } catch (err) {\n console.warn('Gemini vision failed, falling back to OpenAI/Groq vision or prompt:', err.message);\n }\n }\n\n // 2. Try OpenAI / Groq Vision or Vision chat completion endpoint\n const endpoint = `https://api.groq.com/openai/v1/chat/completions`;\n const body = {\n model: 'llama-3.2-11b-vision-preview',\n messages: [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `Analyze this image containing technical content, code, questions, or notes.\nGenerate 8-15 technical interview questions with comprehensive answers and code examples.\nReturn ONLY valid JSON matching this exact structure:\n{\n \"explanation\": \"...\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|DSA (Python)|Java|JavaScript|Python|React|Database\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`\n },\n {\n type: 'image_url',\n image_url: {\n url: `data:${mimeType};base64,${imageBase64}`\n }\n }\n ]\n }\n ]\n };\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify(body)\n });\n if (response.ok) {\n const data = await response.json();\n const content = data.choices[0].message.content;\n return cleanJSONResponse(content);\n }\n } catch (err) {\n console.warn('Groq vision failed:', err.message);\n }\n\n // 3. Fallback: Use text model to generate technical interview questions for the uploaded image/file topic\n try {\n console.log(`â„šī¸ Vision model unavailable/restricted, using standard AI model for image: ${fileName}`);\n const fallbackPrompt = `The user uploaded/pasted an image screenshot/file named \"${fileName}\".\nGenerate 8-12 comprehensive, high-frequency technical interview questions with answers and code examples covering relevant computer science concepts (Data Structures, Algorithms, OOP, Web/System Architecture).\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Analyzed technical content from file '${fileName}'. Extracted concepts and generated technical Q&As.\",\n \"technologies\": [\"Software Engineering\", \"Algorithms\", \"Data Structures\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)\",\n \"category\": \"Algorithms\",\n \"difficulty\": \"Medium\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n const responseText = await callAI(fallbackPrompt);\n return cleanJSONResponse(responseText);\n } catch (err) {\n throw new Error(`Image analysis could not be completed. Please paste the copied text directly into the text tab!`);\n }\n}\n\nasync function analyzeUrlContent(urlContent, url) {\n const prompt = `Analyze the web page / document content extracted from the URL: \"${url}\".\n\nCRITICAL INSTRUCTION: EXTRACT EVERY SINGLE INTERVIEW QUESTION & ANSWER PRESENT IN THIS DOCUMENT OR CHAT CONVERSATION! Generate up to 30+ questions covering ALL topics mentioned. DO NOT limit or cap the question count to 8!\n\n1. Summarize the content in 3-5 sentences.\n2. List detected technologies in array format.\n3. For EVERY question, topic, or code problem found:\n - Provide a clear, descriptive question title.\n - Put the exact answer and output explanation verbatim in \"answer\".\n - Put the full, intact code snippet (including imports, classes, and main methods) in \"codeExample\".\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Brief overview of all topics in the document\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|Java|Python|JavaScript|React|Node.js|SQL|...\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"Descriptive Question Title\",\n \"answer\": \"Detailed answer with exact output explanation\",\n \"codeExample\": \"Full intact code snippet\"\n }\n ]\n}\n\nURL: ${url}\nCONTENT:\n${urlContent.substring(0, 60000)}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nasync function generateQuestionsByAI(language, category = 'All', count = 10, difficulty = 'Mixed') {\n const prompt = `Generate exactly ${count} realistic, high-frequency technical interview questions for ${language}${category && category !== 'All' ? ` focused on ${category}` : ''}.\n\nIMPORTANT: Format and arrange questions in a logical STEP-BY-STEP ROADMAP PATTERN (starting from Step 1: Core Basics, moving to Step 2: OOP, Step 3: Data Structures, Step 4: Algorithms, up to Advanced System Design).\n\nMix of difficulty: ${difficulty}.\nCategories allowed: \"Basics\", \"OOP\", \"Data Structures\", \"Algorithms\", \"Design Patterns\", \"Framework-Specific\", \"Database\", \"System Design\".\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"questions\": [\n {\n \"language\": \"${language}\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n\n const responseText = await callAI(prompt);\n const parsed = cleanJSONResponse(responseText);\n return parsed.questions || [];\n}\n\nasync function answerAndCategorizeQuestion(userQuestion, suggestedLanguage = '') {\n const prompt = `You are a Senior Technical Interview Expert.\nA user asked the following interview question:\n\"${userQuestion}\"\n\nYour job:\n1. Auto-detect the programming language / technology. Use \"DSA (Java)\" for Java algorithms/Data Structures, \"DSA (Python)\" for Python algorithms, \"DSA (C++)\" for C++ algorithms, or \"Java\", \"JavaScript\", \"Python\", \"React\", \"Node.js\", \"SQL\", \"MongoDB\", \"Project: RealityEngine\", etc.\n2. Auto-categorize into ONE of: \"Basics\", \"OOP\", \"Data Structures\", \"Algorithms\", \"Design Patterns\", \"Framework-Specific\", \"Database\", \"System Design\".\n3. Auto-assign difficulty: \"Easy\", \"Medium\", or \"Hard\".\n4. Write a comprehensive, expert interview answer (4-8 sentences).\n5. Write a clean, commented code example demonstrating the answer.\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"language\": \"...\",\n \"category\": \"...\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"${userQuestion.replace(/\"/g, '\\\\\"')}\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nfunction getVisualExplanationFallback(topicName) {\n const topicLower = topicName.toLowerCase();\n \n if (topicLower.includes('sort') || topicLower.includes('bubble')) {\n return {\n title: \"Bubble Sort Algorithm\",\n analogy: \"Sorting 5 books of different heights on a library shelf\",\n explanation: `### Bubble Sort Visualization & Tutorial\\n\\nBubble Sort is a simple comparison-based sorting algorithm. It works by repeatedly stepping through the list to be sorted, comparing each pair of adjacent items, and swapping them if they are in the wrong order (e.g., ascending order).\\n\\n#### How It Works:\\n1. Compare adjacent elements. If the left element is greater than the right, swap them.\\n2. Do this for all adjacent pairs in the array. At the end of the first pass, the largest element is 'bubbled' up to its correct final position.\\n3. Repeat for all remaining unsorted elements.\\n\\n#### Time & Space Complexity:\\n- **Best Case**: O(N) (when the array is already sorted)\\n- **Average/Worst Case**: O(N²)\\n- **Space Complexity**: O(1) (In-place sorting)\\n\\n#### Java Code Example:\\n\\`\\`\\`java\\npublic static void bubbleSort(int[] arr) {\\n int n = arr.length;\\n for (int i = 0; i < n - 1; i++) {\\n boolean swapped = false;\\n for (int j = 0; j < n - 1 - i; j++) {\\n if (arr[j] > arr[j + 1]) {\\n // Swap arr[j] and arr[j+1]\\n int temp = arr[j];\\n arr[j] = arr[j + 1];\\n arr[j + 1] = temp;\\n swapped = true;\\n }\\n }\\n if (!swapped) break;\\n }\\n}\\n\\`\\`\\``,\n verbalScript: \"Let's learn bubble sort by sorting books on a library shelf by height. We'll compare books side-by-side, swapping them if the left one is taller, until the tallest book bubbles to the end of the shelf.\",\n topicType: \"bubble_sort\",\n codeExample: `public static void bubbleSort(int[] arr) {\n int n = arr.length;\n for (int i = 0; i < n - 1; i++) {\n for (int j = 0; j < n - 1 - i; j++) {\n if (arr[j] > arr[j + 1]) {\n int temp = arr[j];\n arr[j] = arr[j + 1];\n arr[j + 1] = temp;\n }\n }\n }\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initial state of unsorted books on the shelf\",\n state: {\n array: [25, 12, 38, 15, 20],\n labels: [\"Math (25)\", \"History (12)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [],\n sorted: []\n },\n spokenText: \"We start with five unsorted books on the shelf. Their heights are 25, 12, 38, 15, and 20 centimeters.\"\n },\n {\n stepNumber: 2,\n description: \"Compare Math (25) and History (12)\",\n state: {\n array: [25, 12, 38, 15, 20],\n labels: [\"Math (25)\", \"History (12)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [0, 1],\n sorted: []\n },\n spokenText: \"First, we compare the Math book and the History book. Since 25 is greater than 12, they are in the wrong order.\"\n },\n {\n stepNumber: 3,\n description: \"Swap Math and History books\",\n state: {\n array: [12, 25, 38, 15, 20],\n labels: [\"History (12)\", \"Math (25)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [0, 1],\n sorted: []\n },\n spokenText: \"We swap the Math and History books. The shelf is now slightly more sorted.\"\n },\n {\n stepNumber: 4,\n description: \"Compare Math (25) and Physics (38)\",\n state: {\n array: [12, 25, 38, 15, 20],\n labels: [\"History (12)\", \"Math (25)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [1, 2],\n sorted: []\n },\n spokenText: \"Next, we compare the Math book with the Physics book. Since 25 is less than 38, they are already in the correct order.\"\n },\n {\n stepNumber: 5,\n description: \"Compare Physics (38) and Novel (15)\",\n state: {\n array: [12, 25, 38, 15, 20],\n labels: [\"History (12)\", \"Math (25)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [2, 3],\n sorted: []\n },\n spokenText: \"Now we compare the Physics book with the Novel. Since 38 is greater than 15, we must swap them.\"\n },\n {\n stepNumber: 6,\n description: \"Swap Physics and Novel books\",\n state: {\n array: [12, 25, 15, 38, 20],\n labels: [\"History (12)\", \"Math (25)\", \"Novel (15)\", \"Physics (38)\", \"Bio (20)\"],\n highlight: [2, 3],\n sorted: []\n },\n spokenText: \"Swapping Physics and Novel. The Physics book continues to bubble toward the right.\"\n },\n {\n stepNumber: 7,\n description: \"Compare Physics (38) and Bio (20) and swap them\",\n state: {\n array: [12, 25, 15, 20, 38],\n labels: [\"History (12)\", \"Math (25)\", \"Novel (15)\", \"Bio (20)\", \"Physics (38)\"],\n highlight: [3, 4],\n sorted: [4]\n },\n spokenText: \"Finally for this pass, we compare Physics and Biology, and swap them. The Physics book has bubbled to its final correct position at the end of the shelf.\"\n },\n {\n stepNumber: 8,\n description: \"Complete Pass 2 and finalize sorting\",\n state: {\n array: [12, 15, 20, 25, 38],\n labels: [\"History (12)\", \"Novel (15)\", \"Bio (20)\", \"Math (25)\", \"Physics (38)\"],\n highlight: [],\n sorted: [0, 1, 2, 3, 4]\n },\n spokenText: \"We continue comparing and sorting the remaining books. Soon, all books are correctly arranged on the shelf in ascending height order.\"\n }\n ]\n };\n }\n \n if (topicLower.includes('search') || topicLower.includes('binary')) {\n return {\n title: \"Binary Search Algorithm\",\n analogy: \"Finding a name in an alphabetical list of students\",\n explanation: `### Binary Search Visualization & Tutorial\\n\\nBinary Search is an efficient algorithm for finding an item from a sorted list of items. It works by repeatedly dividing in half the portion of the list that could contain the item, until you've narrowed down the possible locations to just one.\\n\\n#### How It Works:\\n1. Initialize low pointer at index 0 and high pointer at index N-1.\\n2. Calculate the middle index: mid = (low + high) / 2.\\n3. Compare middle element with the target. If target matches, return mid.\\n4. If target is smaller than mid, move high to mid - 1.\\n5. If target is larger than mid, move low to mid + 1.\\n6. Repeat until target is found or low exceeds high.\\n\\n#### Time & Space Complexity:\\n- **Time Complexity**: O(log N)\\n- **Space Complexity**: O(1)\\n\\n#### Java Code Example:\\n\\`\\`\\`java\\npublic static int binarySearch(int[] arr, int target) {\\n int low = 0;\\n int high = arr.length - 1;\\n while (low <= high) {\\n int mid = low + (high - low) / 2;\\n if (arr[mid] == target) return mid;\\n if (arr[mid] < target) low = mid + 1;\\n else high = mid - 1;\\n }\\n return -1;\\n}\\n\\`\\`\\n`,\n verbalScript: \"Let's perform a binary search to find Grace in our alphabetical student roster. We'll start at the middle, check if her name comes before or after, and discard half the list at each step.\",\n topicType: \"binary_search\",\n codeExample: `public static int binarySearch(int[] arr, int target) {\n int low = 0, high = arr.length - 1;\n while (low <= high) {\n int mid = low + (high - low) / 2;\n if (arr[mid] == target) return mid;\n if (arr[mid] < target) low = mid + 1;\n else high = mid - 1;\n }\n return -1;\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initialize search range: Low index 0, High index 8\",\n state: {\n array: [10, 22, 35, 47, 55, 68, 72, 85, 90],\n labels: [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Jack\"],\n low: 0,\n high: 8,\n mid: -1,\n target: 72,\n found: false\n },\n spokenText: \"We start with a sorted list of nine names. We want to find Grace, who has the target key 72.\"\n },\n {\n stepNumber: 2,\n description: \"Calculate Mid index: (0 + 8) / 2 = 4 (Emma)\",\n state: {\n array: [10, 22, 35, 47, 55, 68, 72, 85, 90],\n labels: [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Jack\"],\n low: 0,\n high: 8,\n mid: 4,\n target: 72,\n found: false\n },\n spokenText: \"The middle index is 4, which is Emma. Grace's name comes alphabetically after Emma, so 72 is greater than 55.\"\n },\n {\n stepNumber: 3,\n description: \"Discard left half. Set Low = Mid + 1 = 5\",\n state: {\n array: [10, 22, 35, 47, 55, 68, 72, 85, 90],\n labels: [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Jack\"],\n low: 5,\n high: 8,\n mid: 4,\n target: 72,\n found: false\n },\n spokenText: \"We discard Emma and everyone to her left. We set the new low boundary to index 5.\"\n },\n {\n stepNumber: 4,\n description: \"Calculate Mid index: (5 + 8) / 2 = 6 (Grace)\",\n state: {\n array: [10, 22, 35, 47, 55, 68, 72, 85, 90],\n labels: [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Jack\"],\n low: 5,\n high: 8,\n mid: 6,\n target: 72,\n found: true\n },\n spokenText: \"The new middle index is 6, which is Grace. Grace is our target name. We have successfully found the target in just 4 steps.\"\n }\n ]\n };\n }\n\n if (topicLower.includes('stack')) {\n return {\n title: \"Stack Data Structure\",\n analogy: \"A stack of plates in a buffet cupboard\",\n explanation: `### Stack Visualization & Tutorial\\n\\nA Stack is a linear data structure that follows the LIFO (Last In First Out) principle. The element inserted last is the first one to be removed.\\n\\n#### Operations:\\n- **Push**: Add an item to the top.\\n- **Pop**: Remove the top item.\\n- **Peek**: Get the value of the top item without removing it.\\n\\n#### Complexity:\\n- Push/Pop/Peek: O(1) time.`,\n verbalScript: \"Let's explore the Stack operations using a stack of plates. Plates are placed on top and removed from the top.\",\n topicType: \"stack\",\n codeExample: `public class Stack {\n private List list = new ArrayList<>();\n public void push(String item) { list.add(item); }\n public String pop() { return list.remove(list.size() - 1); }\n public String peek() { return list.get(list.size() - 1); }\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initial empty stack\",\n state: { elements: [], action: \"idle\", activeVal: \"\" },\n spokenText: \"We start with an empty stack cupboard.\"\n },\n {\n stepNumber: 2,\n description: \"Push Plate A onto the stack\",\n state: { elements: [\"Plate A\"], action: \"push\", activeVal: \"Plate A\" },\n spokenText: \"We push Plate A onto the stack. It sits at the bottom.\"\n },\n {\n stepNumber: 3,\n description: \"Push Plate B onto the stack\",\n state: { elements: [\"Plate A\", \"Plate B\"], action: \"push\", activeVal: \"Plate B\" },\n spokenText: \"We push Plate B onto the stack. It sits on top of Plate A.\"\n },\n {\n stepNumber: 4,\n description: \"Peek the top plate\",\n state: { elements: [\"Plate A\", \"Plate B\"], action: \"peek\", activeVal: \"Plate B\" },\n spokenText: \"Peeking at the stack tells us Plate B is currently at the top.\"\n },\n {\n stepNumber: 5,\n description: \"Pop the top plate (Plate B)\",\n state: { elements: [\"Plate A\"], action: \"pop\", activeVal: \"Plate B\" },\n spokenText: \"We pop the top plate. Plate B is removed first, leaving Plate A on the stack.\"\n }\n ]\n };\n }\n\n if (topicLower.includes('queue')) {\n return {\n title: \"Queue Data Structure\",\n analogy: \"A line of customers waiting at a movie ticket counter\",\n explanation: `### Queue Visualization & Tutorial\\n\\nA Queue is a linear data structure that follows the FIFO (First In First Out) principle. The first item added is the first one to be removed.\\n\\n#### Operations:\\n- **Enqueue**: Add an item to the back.\\n- **Dequeue**: Remove an item from the front.\\n\\n#### Complexity:\\n- Enqueue/Dequeue: O(1) time.`,\n verbalScript: \"Let's explore the Queue operations using a customer line. Customers join at the back and are served at the front.\",\n topicType: \"queue\",\n codeExample: `public class Queue {\n private List list = new ArrayList<>();\n public void enqueue(String item) { list.add(item); }\n public String dequeue() { return list.remove(0); }\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initial empty queue\",\n state: { elements: [], action: \"idle\", activeVal: \"\" },\n spokenText: \"The ticket line is empty.\"\n },\n {\n stepNumber: 2,\n description: \"Enqueue Alice to the line\",\n state: { elements: [\"Alice\"], action: \"enqueue\", activeVal: \"Alice\" },\n spokenText: \"Alice joins the line. She is at the front.\"\n },\n {\n stepNumber: 3,\n description: \"Enqueue Bob to the line\",\n state: { elements: [\"Alice\", \"Bob\"], action: \"enqueue\", activeVal: \"Bob\" },\n spokenText: \"Bob joins behind Alice.\"\n },\n {\n stepNumber: 4,\n description: \"Dequeue served customer (Alice)\",\n state: { elements: [\"Bob\"], action: \"dequeue\", activeVal: \"Alice\" },\n spokenText: \"Alice is served and leaves the front of the line. Bob is now next in line.\"\n }\n ]\n };\n }\n\n if (topicLower.includes('list') || topicLower.includes('link')) {\n return {\n title: \"Linked List Reversal\",\n analogy: \"Reversing a chain of linked train carriages\",\n explanation: `### Linked List Reversal\\n\\nReversing a singly linked list involves modifying the next pointers of every node so they point to the preceding node instead of the succeeding one.\\n\\n#### How It Works:\\n1. Maintain three pointers: prev (null), curr (head), and next (null).\\n2. Iterate through the list.\\n3. Save the next node: next = curr.next.\\n4. Reverse the pointer: curr.next = prev.\\n5. Move prev and curr forward: prev = curr, curr = next.`,\n verbalScript: \"Let's reverse a linked list of train carriages. We will detach and point each carriage's coupler backward step-by-step.\",\n topicType: \"linked_list\",\n codeExample: `public void reverse() {\n Node prev = null;\n Node curr = head;\n Node next = null;\n while (curr != null) {\n next = curr.next;\n curr.next = prev;\n prev = curr;\n curr = next;\n }\n head = prev;\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Original linked list: Node 1 -> Node 2 -> Node 3\",\n state: {\n nodes: [\n { id: 1, val: 10, label: \"Carriage 1\", nextId: 2 },\n { id: 2, val: 20, label: \"Carriage 2\", nextId: 3 },\n { id: 3, val: 30, label: \"Carriage 3\", nextId: null }\n ],\n action: \"idle\",\n activeNodeId: 1\n },\n spokenText: \"Here is our initial list of carriages pointing from left to right.\"\n },\n {\n stepNumber: 2,\n description: \"Reverse Carriage 1 pointer to point to Null\",\n state: {\n nodes: [\n { id: 1, val: 10, label: \"Carriage 1\", nextId: null },\n { id: 2, val: 20, label: \"Carriage 2\", nextId: 3 },\n { id: 3, val: 30, label: \"Carriage 3\", nextId: null }\n ],\n action: \"reverse\",\n activeNodeId: 1\n },\n spokenText: \"We make Carriage 1 point to null, since it will become the new tail carriage.\"\n },\n {\n stepNumber: 3,\n description: \"Reverse Carriage 2 pointer to point to Carriage 1\",\n state: {\n nodes: [\n { id: 1, val: 10, label: \"Carriage 1\", nextId: null },\n { id: 2, val: 20, label: \"Carriage 2\", nextId: 1 },\n { id: 3, val: 30, label: \"Carriage 3\", nextId: null }\n ],\n action: \"reverse\",\n activeNodeId: 2\n },\n spokenText: \"Now, Carriage 2's coupler is reversed to point back to Carriage 1.\"\n },\n {\n stepNumber: 4,\n description: \"Reverse Carriage 3 pointer to point to Carriage 2\",\n state: {\n nodes: [\n { id: 1, val: 10, label: \"Carriage 1\", nextId: null },\n { id: 2, val: 20, label: \"Carriage 2\", nextId: 1 },\n { id: 3, val: 30, label: \"Carriage 3\", nextId: 2 }\n ],\n action: \"reverse\",\n activeNodeId: 3\n },\n spokenText: \"Finally, Carriage 3 points back to Carriage 2. Carriage 3 is now the new head of our reversed chain.\"\n }\n ]\n };\n }\n\n // Fallback for everything else: General Flow\n return {\n title: topicName,\n analogy: `Understanding ${topicName} step-by-step`,\n explanation: `### Visual Explanation of ${topicName}\\n\\nHere is a step-by-step breakdown of how **${topicName}** operates in production systems, detailing its core components and architectural layers.\\n\\n#### Highlights:\\n- Clear modular boundaries.\\n- Step-by-step data flows.\\n- High-availability considerations.\\n\\n#### Code Example:\\n\\`\\`\\`javascript\\n// Conceptual illustration\\nfunction executeTask() {\\n console.log(\"Executing ${topicName} workflow...\");\\n}\\n\\`\\`\\``,\n verbalScript: `Let's walk through the steps of ${topicName}. We'll examine how data flows from the initialization phase down to completion.`,\n topicType: \"general_flow\",\n codeExample: `// Conceptual example\\nfunction executeWorkflow() {\\n console.log(\"Executing ${topicName} workflow...\");\\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initialize client-side action\",\n state: {\n nodes: [\n { id: 1, label: \"Client Request\", status: \"active\" },\n { id: 2, label: \"Processing Service\", status: \"idle\" },\n { id: 3, label: \"Data Warehouse\", status: \"idle\" }\n ],\n connections: [\n { from: 1, to: 2 },\n { from: 2, to: 3 }\n ],\n activeNodeId: 1\n },\n spokenText: \"First, the user initiates a client request to trigger the workflow.\"\n },\n {\n stepNumber: 2,\n description: \"Processing request through services\",\n state: {\n nodes: [\n { id: 1, label: \"Client Request\", status: \"done\" },\n { id: 2, label: \"Processing Service\", status: \"active\" },\n { id: 3, label: \"Data Warehouse\", status: \"idle\" }\n ],\n connections: [\n { from: 1, to: 2 },\n { from: 2, to: 3 }\n ],\n activeNodeId: 2\n },\n spokenText: \"Next, our processing service validates and transforms the request data.\"\n },\n {\n stepNumber: 3,\n description: \"Persisting changes to database\",\n state: {\n nodes: [\n { id: 1, label: \"Client Request\", status: \"done\" },\n { id: 2, label: \"Processing Service\", status: \"done\" },\n { id: 3, label: \"Data Warehouse\", status: \"active\" }\n ],\n connections: [\n { from: 1, to: 2 },\n { from: 2, to: 3 }\n ],\n activeNodeId: 3\n },\n spokenText: \"Finally, the changes are committed to the data warehouse, and a success response is returned.\"\n }\n ]\n };\n}\n\nasync function explainTopicWithVisuals(topicName) {\n const prompt = `You are a world-class computer science teacher and algorithm visualizer designer.\nExplain the following topic or problem: \"${topicName}\"\n\nCRITICAL INSTRUCTION:\n1. Every explanation and visualization MUST be built around a concrete, relatable real-world example/analogy (e.g. sorting books on a shelf by size, searching for a name in an alphabetical student roster/phonebook, a stack of dinner plates, a ticket queue of customers at a cinema, reversing a chain of linked train carriages, or tracking recursive family ancestors).\n2. The explanation, code example, visual step descriptions, and speech scripts must consistently refer to this concrete analogy/example and its items.\n3. Provide granular, step-by-step state logs. For algorithms, do not skip steps. Generate between 8 and 15 steps representing the full flow of the analogy/example from start to finish.\n4. Ensure the JSON returned is fully complete and valid.\n\nIf the topic is a standard algorithm/data structure, classify it into one of these types:\n- \"bubble_sort\" (for sorting algorithms like bubble, selection, insertion, quick, merge)\n- \"binary_search\" (for search algorithms and interval division)\n- \"stack\" (for stack operations LIFO)\n- \"queue\" (for queue operations FIFO)\n- \"linked_list\" (for linked lists insertion, deletion, reversal)\n- \"binary_tree\" (for tree traversals BFS, DFS, insertion)\n- \"recursion\" (for recursive operations like Fibonacci or factorial)\n- \"general_flow\" (for any general concepts like JS closures, DB indexing, API calls, OAuth, etc.)\n\nFor \"general_flow\", you will provide a sequence of visual nodes/elements that represent the flow of the explanation.\nFor the specific types, provide the corresponding structural state configuration for each animation step.\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"title\": \"${topicName}\",\n \"analogy\": \"Short 1-2 sentence describing the real-world analogy chosen (e.g., 'Sorting 5 books of different heights on a shelf').\",\n \"explanation\": \"Markdown string containing complete written explanation, detail of the analogy, best practices, and code example\",\n \"verbalScript\": \"A continuous narration script to be read aloud via Text-to-Speech (TTS). It should be engaging, informative, and synchronized step-by-step with the animation steps.\",\n \"topicType\": \"bubble_sort|binary_search|stack|queue|linked_list|binary_tree|recursion|general_flow\",\n \"codeExample\": \"A complete, working code implementation of the concept.\",\n \"visualSteps\": [\n {\n \"stepNumber\": 1,\n \"description\": \"Short label/action for this step referencing the real-world example (e.g. 'Compare height of Book A (15cm) and Book B (12cm)')\",\n \"state\": {\n // state config\n },\n \"spokenText\": \"The specific portion of the narration corresponding to this animation step, explaining the action in terms of the real-world analogy.\"\n }\n ]\n}`;\n\n try {\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n } catch (err) {\n console.warn('âš ī¸ AI explain topic fallback used for topic:', topicName, 'Error:', err.message);\n return getVisualExplanationFallback(topicName);\n }\n}\n\nmodule.exports = { callAI, cleanJSONResponse, analyzeFile, analyzeImage, analyzeUrlContent, generateQuestionsByAI, answerAndCategorizeQuestion, explainTopicWithVisuals };\n\n\n"}, "4a5903ec580df64acc42157c68ec468ffb09f267": {"t": 1785766987.19477, "n": 41787, "h": "776abec32de731b5", "c": "require('dotenv').config();\n\n/**\n * Universal AI Service\n * Supports OpenAI, OpenRouter, Groq, DeepSeek, Ollama (Local), Claude, or any OpenAI-compatible API.\n */\n\nasync function callAI(prompt) {\n // Always reload dotenv with override: true so changes in .env take effect immediately\n require('dotenv').config({ override: true });\n\n let provider = (process.env.AI_PROVIDER || 'openai').trim().toLowerCase();\n let apiKey = (process.env.AI_API_KEY || process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY || process.env.GEMINI_API_KEY || '').trim();\n\n // Strip inline comments if any\n if (apiKey.includes('#')) {\n apiKey = apiKey.split('#')[0].trim();\n }\n\n if (!apiKey || apiKey.includes('PASTE_') || apiKey.includes('your_api') || apiKey.includes('...')) {\n throw new Error('Please paste your real API key (from Groq, OpenAI, etc.) into server/.env file under AI_API_KEY=');\n }\n\n // Auto-detect provider from API key format\n if (apiKey.startsWith('gsk_')) {\n provider = 'groq';\n } else if (apiKey.startsWith('sk-or-')) {\n provider = 'openrouter';\n }\n\n let model = (process.env.AI_MODEL || '').trim();\n if (!model) {\n if (provider === 'groq') model = 'llama-3.3-70b-versatile';\n else if (provider === 'openrouter') model = 'meta-llama/llama-3.3-70b-instruct';\n else if (provider === 'deepseek') model = 'deepseek-chat';\n else if (provider === 'ollama') model = 'llama3';\n else model = 'gpt-4o-mini';\n }\n\n let baseUrl = (process.env.AI_BASE_URL || '').trim();\n if (!baseUrl) {\n if (provider === 'groq') baseUrl = 'https://api.groq.com/openai/v1';\n else if (provider === 'openrouter') baseUrl = 'https://openrouter.ai/api/v1';\n else if (provider === 'deepseek') baseUrl = 'https://api.deepseek.com/v1';\n else if (provider === 'ollama') baseUrl = 'http://localhost:11434/v1';\n else baseUrl = 'https://api.openai.com/v1';\n }\n\n // Remove trailing slashes\n baseUrl = baseUrl.replace(/\\/+$/, '');\n\n // 1. Gemini Fallback if provider explicitly set to gemini\n if (provider === 'gemini') {\n const { GoogleGenerativeAI } = require('@google/generative-ai');\n const genAI = new GoogleGenerativeAI(apiKey);\n const geminiModel = genAI.getGenerativeModel({ model: model || 'gemini-2.0-flash' });\n const res = await geminiModel.generateContent(prompt);\n return res.response.text();\n }\n\n // 2. OpenAI / OpenRouter / Groq / DeepSeek / Ollama / Custom API (Standard Chat Completion)\n const endpoint = `${baseUrl}/chat/completions`;\n const headers = {\n 'Content-Type': 'application/json'\n };\n\n if (apiKey) {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (provider === 'openrouter') {\n headers['HTTP-Referer'] = 'http://localhost:3000';\n headers['X-Title'] = 'InterviewHub';\n }\n\n const maxTokens = provider === 'groq' ? 2500 : 8192;\n\n const body = {\n model: model,\n messages: [\n {\n role: 'system',\n content: 'You are a Principal Software Engineer and Technical Interviewer. Always respond in strict valid JSON format. All code snippets in string fields must have properly escaped quotes and newline characters.'\n },\n {\n role: 'user',\n content: prompt\n }\n ],\n temperature: 0.2,\n max_tokens: maxTokens,\n response_format: { type: 'json_object' }\n };\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`❌ AI API Error (${provider}):`, response.status, errorText);\n\n // Auto-retry with lighter model / lower max_tokens if HTTP 400, 413 (TPM limit), or 429\n if (response.status === 400 || response.status === 413 || response.status === 429) {\n console.log('🔄 Retrying AI call with fallback parameters and model...');\n delete body.response_format;\n if (provider === 'groq') {\n body.model = 'llama-3.1-8b-instant';\n body.max_tokens = 2500;\n } else {\n body.max_tokens = 2000;\n }\n const retryRes = await fetch(endpoint, { method: 'POST', headers, body: JSON.stringify(body) });\n if (retryRes.ok) {\n const retryData = await retryRes.json();\n return retryData.choices && retryData.choices[0] && retryData.choices[0].message ? retryData.choices[0].message.content : '';\n }\n }\n\n if (response.status === 401) {\n throw new Error(`Invalid API Key for ${provider.toUpperCase()}. Please check your key in server/.env.`);\n }\n if (response.status === 429) {\n throw new Error(`Rate limit reached for ${provider.toUpperCase()}. Please wait a few seconds and try again.`);\n }\n throw new Error(`AI API call failed [${response.status}]: ${errorText}`);\n }\n\n const data = await response.json();\n const content = data.choices && data.choices[0] && data.choices[0].message ? data.choices[0].message.content : '';\n return content;\n}\n\nfunction cleanJSONResponse(rawText) {\n if (!rawText) throw new Error(\"AI returned an empty response. Please try again.\");\n let cleaned = rawText.trim();\n\n // Strip ```json markdown blocks if present\n if (cleaned.startsWith('```')) {\n cleaned = cleaned.replace(/^```[a-z]*\\n?/i, '').replace(/\\n?```$/i, '').trim();\n }\n\n // Extract JSON object substring\n const match = cleaned.match(/\\{[\\s\\S]*\\}/);\n if (match) {\n cleaned = match[0];\n }\n\n try {\n return JSON.parse(cleaned);\n } catch (err) {\n // 1. Try sanitizing control characters\n try {\n const sanitized = cleaned\n .replace(/[\\u0000-\\u001F]+/g, (m) => {\n if (m === '\\n') return '\\\\n';\n if (m === '\\r') return '\\\\r';\n if (m === '\\t') return '\\\\t';\n return '';\n });\n return JSON.parse(sanitized);\n } catch (e) {\n // 2. Salvage partial questions array if JSON was truncated by max tokens\n try {\n const questionsMatch = cleaned.match(/\"questions\"\\s*:\\s*\\[([\\s\\S]*)/);\n if (questionsMatch) {\n let questionsStr = questionsMatch[1];\n const lastObjEnd = questionsStr.lastIndexOf('}');\n if (lastObjEnd !== -1) {\n let validArrayStr = '[' + questionsStr.substring(0, lastObjEnd + 1) + ']';\n validArrayStr = validArrayStr.replace(/[\\u0000-\\u001F]+/g, (m) => (m === '\\n' ? '\\\\n' : m === '\\t' ? '\\\\t' : ''));\n const parsedQuestions = JSON.parse(validArrayStr);\n if (Array.isArray(parsedQuestions) && parsedQuestions.length > 0) {\n return {\n explanation: \"Successfully extracted questions from document\",\n technologies: [\"Java\", \"DSA\"],\n questions: parsedQuestions\n };\n }\n }\n }\n } catch (salvageErr) {\n console.error(\"Salvage error:\", salvageErr);\n }\n console.error(\"JSON parse error on raw text snippet:\", rawText.substring(0, 300));\n throw new Error(\"AI returned invalid JSON format. Please click Analyze again.\");\n }\n }\n}\n\nasync function analyzeFile(fileContent, fileName, fileType) {\n const prompt = `Analyze the following technical file/document/code content thoroughly and extract structured interview questions & answers across ALL sections.\n\nCRITICAL INSTRUCTIONS FOR ACCURATE EXTRACTION:\n1. DO NOT summarize, abbreviate, or shorten any code snippets or output comments! Preserve the exact, full code (including import statements, class headers, and main methods) in \"codeExample\".\n2. Include the exact output comment (e.g. \"// output :- ...\") and step-by-step logic in the \"answer\" field verbatim.\n3. Extract EVERY distinct topic, pattern, or code block present in the text (such as Variables, Conditional Statements, Switch Statement, Loops, Methods, Star Patterns, Arrays, Basic Sorting, 2D Arrays, String Manipulation, Bit Manipulation, OOP Concepts, Recursion, ArrayList, LinkedList, Doubly LinkedList, Cycle Detection, etc.).\n4. Assign clear, descriptive question titles based on the topic comments or logic (e.g. \"Variables in Java - Declaration & Print\", \"Check Even or Odd Number using Modulo\", \"Tax Calculator Logic\", \"Print Largest of 3 Numbers\", \"Butterfly Star Pattern\", \"Reverse a Linked List\", \"Detect Cycle in Linked List using Slow & Fast Pointers\").\n5. Auto-detect language precisely: Use \"DSA (Java)\" for Java algorithms & Data Structures, \"Java\" for core Java, \"Python\", \"JavaScript\", \"C++\", \"SQL\", etc.\n6. Map category accurately:\n - \"Basics\" for Variables, Conditionals, Loops, Switch Statements, Methods\n - \"OOP\" for Classes, Inheritance, Constructors, Abstraction, Polymorphism\n - \"Data Structures\" for Arrays, 2D Arrays, Strings, ArrayList, LinkedList, Doubly LinkedList\n - \"Algorithms\" for Sorting (Bubble, Selection, Insertion), Binary Search, Linear Search, Recursion, Bit Manipulation\n - \"Design Patterns\", \"Database\", \"System Design\"\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Brief overview of the entire document and topics covered\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|Java|Python|JavaScript|...\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"Descriptive Question Title\",\n \"answer\": \"Detailed answer with exact output explanation verbatim\",\n \"codeExample\": \"Full intact code snippet with all imports, class, and main method\"\n }\n ]\n}\n\nFILE NAME: ${fileName}\nFILE TYPE: ${fileType}\nCONTENT:\n${fileContent}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nasync function analyzeImage(imageBase64, mimeType, fileName) {\n require('dotenv').config({ override: true });\n let apiKey = (process.env.AI_API_KEY || process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY || process.env.GEMINI_API_KEY || '').trim();\n if (apiKey.includes('#')) apiKey = apiKey.split('#')[0].trim();\n\n // 1. Try Gemini Vision if Gemini API key or gemini provider\n if (apiKey.startsWith('AIza') || (process.env.AI_PROVIDER || '').toLowerCase() === 'gemini') {\n try {\n const { GoogleGenerativeAI } = require('@google/generative-ai');\n const genAI = new GoogleGenerativeAI(apiKey);\n const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });\n const prompt = `Analyze this image (which may be a screenshot of code, diagram, question paper, resume, or technical notes).\nExtract all technical details and generate 8-15 technical interview questions with full detailed answers and code examples.\n\nReturn ONLY valid JSON:\n{\n \"explanation\": \"...\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"Java|JavaScript|Python|React|DSA (Java)|System Design\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n const imagePart = {\n inlineData: {\n data: imageBase64,\n mimeType: mimeType || 'image/jpeg'\n }\n };\n const res = await model.generateContent([prompt, imagePart]);\n return cleanJSONResponse(res.response.text());\n } catch (err) {\n console.warn('Gemini vision failed, falling back to OpenAI/Groq vision or prompt:', err.message);\n }\n }\n\n // 2. Try OpenAI / Groq Vision or Vision chat completion endpoint\n const endpoint = `https://api.groq.com/openai/v1/chat/completions`;\n const body = {\n model: 'llama-3.2-11b-vision-preview',\n messages: [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `Analyze this image containing technical content, code, questions, or notes.\nGenerate 8-15 technical interview questions with comprehensive answers and code examples.\nReturn ONLY valid JSON matching this exact structure:\n{\n \"explanation\": \"...\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|DSA (Python)|Java|JavaScript|Python|React|Database\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`\n },\n {\n type: 'image_url',\n image_url: {\n url: `data:${mimeType};base64,${imageBase64}`\n }\n }\n ]\n }\n ]\n };\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify(body)\n });\n if (response.ok) {\n const data = await response.json();\n const content = data.choices[0].message.content;\n return cleanJSONResponse(content);\n }\n } catch (err) {\n console.warn('Groq vision failed:', err.message);\n }\n\n // 3. Fallback: Use text model to generate technical interview questions for the uploaded image/file topic\n try {\n console.log(`â„šī¸ Vision model unavailable/restricted, using standard AI model for image: ${fileName}`);\n const fallbackPrompt = `The user uploaded/pasted an image screenshot/file named \"${fileName}\".\nGenerate 8-12 comprehensive, high-frequency technical interview questions with answers and code examples covering relevant computer science concepts (Data Structures, Algorithms, OOP, Web/System Architecture).\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Analyzed technical content from file '${fileName}'. Extracted concepts and generated technical Q&As.\",\n \"technologies\": [\"Software Engineering\", \"Algorithms\", \"Data Structures\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)\",\n \"category\": \"Algorithms\",\n \"difficulty\": \"Medium\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n const responseText = await callAI(fallbackPrompt);\n return cleanJSONResponse(responseText);\n } catch (err) {\n throw new Error(`Image analysis could not be completed. Please paste the copied text directly into the text tab!`);\n }\n}\n\nasync function analyzeUrlContent(urlContent, url) {\n const prompt = `Analyze the web page / document content extracted from the URL: \"${url}\".\n\nCRITICAL INSTRUCTION: EXTRACT EVERY SINGLE INTERVIEW QUESTION & ANSWER PRESENT IN THIS DOCUMENT OR CHAT CONVERSATION! Generate up to 30+ questions covering ALL topics mentioned. DO NOT limit or cap the question count to 8!\n\n1. Summarize the content in 3-5 sentences.\n2. List detected technologies in array format.\n3. For EVERY question, topic, or code problem found:\n - Provide a clear, descriptive question title.\n - Put the exact answer and output explanation verbatim in \"answer\".\n - Put the full, intact code snippet (including imports, classes, and main methods) in \"codeExample\".\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Brief overview of all topics in the document\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|Java|Python|JavaScript|React|Node.js|SQL|...\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"Descriptive Question Title\",\n \"answer\": \"Detailed answer with exact output explanation\",\n \"codeExample\": \"Full intact code snippet\"\n }\n ]\n}\n\nURL: ${url}\nCONTENT:\n${urlContent.substring(0, 60000)}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nasync function generateQuestionsByAI(language, category = 'All', count = 10, difficulty = 'Mixed') {\n const prompt = `Generate exactly ${count} realistic, high-frequency technical interview questions for ${language}${category && category !== 'All' ? ` focused on ${category}` : ''}.\n\nIMPORTANT: Format and arrange questions in a logical STEP-BY-STEP ROADMAP PATTERN (starting from Step 1: Core Basics, moving to Step 2: OOP, Step 3: Data Structures, Step 4: Algorithms, up to Advanced System Design).\n\nMix of difficulty: ${difficulty}.\nCategories allowed: \"Basics\", \"OOP\", \"Data Structures\", \"Algorithms\", \"Design Patterns\", \"Framework-Specific\", \"Database\", \"System Design\".\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"questions\": [\n {\n \"language\": \"${language}\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n\n const responseText = await callAI(prompt);\n const parsed = cleanJSONResponse(responseText);\n return parsed.questions || [];\n}\n\nasync function answerAndCategorizeQuestion(userQuestion, suggestedLanguage = '') {\n const prompt = `You are a Senior Technical Interview Expert.\nA user asked the following interview question:\n\"${userQuestion}\"\n\nYour job:\n1. Auto-detect the programming language / technology. Use \"DSA (Java)\" for Java algorithms/Data Structures, \"DSA (Python)\" for Python algorithms, \"DSA (C++)\" for C++ algorithms, or \"Java\", \"JavaScript\", \"Python\", \"React\", \"Node.js\", \"SQL\", \"MongoDB\", \"Project: RealityEngine\", etc.\n2. Auto-categorize into ONE of: \"Basics\", \"OOP\", \"Data Structures\", \"Algorithms\", \"Design Patterns\", \"Framework-Specific\", \"Database\", \"System Design\".\n3. Auto-assign difficulty: \"Easy\", \"Medium\", or \"Hard\".\n4. Write a comprehensive, expert interview answer (4-8 sentences).\n5. Write a clean, commented code example demonstrating the answer.\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"language\": \"...\",\n \"category\": \"...\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"${userQuestion.replace(/\"/g, '\\\\\"')}\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nfunction getVisualExplanationFallback(topicName) {\n const topicLower = topicName.toLowerCase();\n \n if (topicLower.includes('sort') || topicLower.includes('bubble')) {\n return {\n title: \"Bubble Sort Algorithm\",\n analogy: \"Sorting 5 books of different heights on a library shelf\",\n explanation: `### Bubble Sort Visualization & Tutorial\\n\\nBubble Sort is a simple comparison-based sorting algorithm. It works by repeatedly stepping through the list to be sorted, comparing each pair of adjacent items, and swapping them if they are in the wrong order (e.g., ascending order).\\n\\n#### How It Works:\\n1. Compare adjacent elements. If the left element is greater than the right, swap them.\\n2. Do this for all adjacent pairs in the array. At the end of the first pass, the largest element is 'bubbled' up to its correct final position.\\n3. Repeat for all remaining unsorted elements.\\n\\n#### Time & Space Complexity:\\n- **Best Case**: O(N) (when the array is already sorted)\\n- **Average/Worst Case**: O(N²)\\n- **Space Complexity**: O(1) (In-place sorting)\\n\\n#### Java Code Example:\\n\\`\\`\\`java\\npublic static void bubbleSort(int[] arr) {\\n int n = arr.length;\\n for (int i = 0; i < n - 1; i++) {\\n boolean swapped = false;\\n for (int j = 0; j < n - 1 - i; j++) {\\n if (arr[j] > arr[j + 1]) {\\n // Swap arr[j] and arr[j+1]\\n int temp = arr[j];\\n arr[j] = arr[j + 1];\\n arr[j + 1] = temp;\\n swapped = true;\\n }\\n }\\n if (!swapped) break;\\n }\\n}\\n\\`\\`\\``,\n verbalScript: \"Let's learn bubble sort by sorting books on a library shelf by height. We'll compare books side-by-side, swapping them if the left one is taller, until the tallest book bubbles to the end of the shelf.\",\n topicType: \"bubble_sort\",\n codeExample: `public static void bubbleSort(int[] arr) {\n int n = arr.length;\n for (int i = 0; i < n - 1; i++) {\n for (int j = 0; j < n - 1 - i; j++) {\n if (arr[j] > arr[j + 1]) {\n int temp = arr[j];\n arr[j] = arr[j + 1];\n arr[j + 1] = temp;\n }\n }\n }\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initial state of unsorted books on the shelf\",\n state: {\n array: [25, 12, 38, 15, 20],\n labels: [\"Math (25)\", \"History (12)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [],\n sorted: []\n },\n spokenText: \"We start with five unsorted books on the shelf. Their heights are 25, 12, 38, 15, and 20 centimeters.\"\n },\n {\n stepNumber: 2,\n description: \"Compare Math (25) and History (12)\",\n state: {\n array: [25, 12, 38, 15, 20],\n labels: [\"Math (25)\", \"History (12)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [0, 1],\n sorted: []\n },\n spokenText: \"First, we compare the Math book and the History book. Since 25 is greater than 12, they are in the wrong order.\"\n },\n {\n stepNumber: 3,\n description: \"Swap Math and History books\",\n state: {\n array: [12, 25, 38, 15, 20],\n labels: [\"History (12)\", \"Math (25)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [0, 1],\n sorted: []\n },\n spokenText: \"We swap the Math and History books. The shelf is now slightly more sorted.\"\n },\n {\n stepNumber: 4,\n description: \"Compare Math (25) and Physics (38)\",\n state: {\n array: [12, 25, 38, 15, 20],\n labels: [\"History (12)\", \"Math (25)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [1, 2],\n sorted: []\n },\n spokenText: \"Next, we compare the Math book with the Physics book. Since 25 is less than 38, they are already in the correct order.\"\n },\n {\n stepNumber: 5,\n description: \"Compare Physics (38) and Novel (15)\",\n state: {\n array: [12, 25, 38, 15, 20],\n labels: [\"History (12)\", \"Math (25)\", \"Physics (38)\", \"Novel (15)\", \"Bio (20)\"],\n highlight: [2, 3],\n sorted: []\n },\n spokenText: \"Now we compare the Physics book with the Novel. Since 38 is greater than 15, we must swap them.\"\n },\n {\n stepNumber: 6,\n description: \"Swap Physics and Novel books\",\n state: {\n array: [12, 25, 15, 38, 20],\n labels: [\"History (12)\", \"Math (25)\", \"Novel (15)\", \"Physics (38)\", \"Bio (20)\"],\n highlight: [2, 3],\n sorted: []\n },\n spokenText: \"Swapping Physics and Novel. The Physics book continues to bubble toward the right.\"\n },\n {\n stepNumber: 7,\n description: \"Compare Physics (38) and Bio (20) and swap them\",\n state: {\n array: [12, 25, 15, 20, 38],\n labels: [\"History (12)\", \"Math (25)\", \"Novel (15)\", \"Bio (20)\", \"Physics (38)\"],\n highlight: [3, 4],\n sorted: [4]\n },\n spokenText: \"Finally for this pass, we compare Physics and Biology, and swap them. The Physics book has bubbled to its final correct position at the end of the shelf.\"\n },\n {\n stepNumber: 8,\n description: \"Complete Pass 2 and finalize sorting\",\n state: {\n array: [12, 15, 20, 25, 38],\n labels: [\"History (12)\", \"Novel (15)\", \"Bio (20)\", \"Math (25)\", \"Physics (38)\"],\n highlight: [],\n sorted: [0, 1, 2, 3, 4]\n },\n spokenText: \"We continue comparing and sorting the remaining books. Soon, all books are correctly arranged on the shelf in ascending height order.\"\n }\n ]\n };\n }\n \n if (topicLower.includes('search') || topicLower.includes('binary')) {\n return {\n title: \"Binary Search Algorithm\",\n analogy: \"Finding a name in an alphabetical list of students\",\n explanation: `### Binary Search Visualization & Tutorial\\n\\nBinary Search is an efficient algorithm for finding an item from a sorted list of items. It works by repeatedly dividing in half the portion of the list that could contain the item, until you've narrowed down the possible locations to just one.\\n\\n#### How It Works:\\n1. Initialize low pointer at index 0 and high pointer at index N-1.\\n2. Calculate the middle index: mid = (low + high) / 2.\\n3. Compare middle element with the target. If target matches, return mid.\\n4. If target is smaller than mid, move high to mid - 1.\\n5. If target is larger than mid, move low to mid + 1.\\n6. Repeat until target is found or low exceeds high.\\n\\n#### Time & Space Complexity:\\n- **Time Complexity**: O(log N)\\n- **Space Complexity**: O(1)\\n\\n#### Java Code Example:\\n\\`\\`\\`java\\npublic static int binarySearch(int[] arr, int target) {\\n int low = 0;\\n int high = arr.length - 1;\\n while (low <= high) {\\n int mid = low + (high - low) / 2;\\n if (arr[mid] == target) return mid;\\n if (arr[mid] < target) low = mid + 1;\\n else high = mid - 1;\\n }\\n return -1;\\n}\\n\\`\\`\\n`,\n verbalScript: \"Let's perform a binary search to find Grace in our alphabetical student roster. We'll start at the middle, check if her name comes before or after, and discard half the list at each step.\",\n topicType: \"binary_search\",\n codeExample: `public static int binarySearch(int[] arr, int target) {\n int low = 0, high = arr.length - 1;\n while (low <= high) {\n int mid = low + (high - low) / 2;\n if (arr[mid] == target) return mid;\n if (arr[mid] < target) low = mid + 1;\n else high = mid - 1;\n }\n return -1;\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initialize search range: Low index 0, High index 8\",\n state: {\n array: [10, 22, 35, 47, 55, 68, 72, 85, 90],\n labels: [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Jack\"],\n low: 0,\n high: 8,\n mid: -1,\n target: 72,\n found: false\n },\n spokenText: \"We start with a sorted list of nine names. We want to find Grace, who has the target key 72.\"\n },\n {\n stepNumber: 2,\n description: \"Calculate Mid index: (0 + 8) / 2 = 4 (Emma)\",\n state: {\n array: [10, 22, 35, 47, 55, 68, 72, 85, 90],\n labels: [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Jack\"],\n low: 0,\n high: 8,\n mid: 4,\n target: 72,\n found: false\n },\n spokenText: \"The middle index is 4, which is Emma. Grace's name comes alphabetically after Emma, so 72 is greater than 55.\"\n },\n {\n stepNumber: 3,\n description: \"Discard left half. Set Low = Mid + 1 = 5\",\n state: {\n array: [10, 22, 35, 47, 55, 68, 72, 85, 90],\n labels: [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Jack\"],\n low: 5,\n high: 8,\n mid: 4,\n target: 72,\n found: false\n },\n spokenText: \"We discard Emma and everyone to her left. We set the new low boundary to index 5.\"\n },\n {\n stepNumber: 4,\n description: \"Calculate Mid index: (5 + 8) / 2 = 6 (Grace)\",\n state: {\n array: [10, 22, 35, 47, 55, 68, 72, 85, 90],\n labels: [\"Alice\", \"Bob\", \"Charlie\", \"David\", \"Emma\", \"Frank\", \"Grace\", \"Henry\", \"Jack\"],\n low: 5,\n high: 8,\n mid: 6,\n target: 72,\n found: true\n },\n spokenText: \"The new middle index is 6, which is Grace. Grace is our target name. We have successfully found the target in just 4 steps.\"\n }\n ]\n };\n }\n\n if (topicLower.includes('stack')) {\n return {\n title: \"Stack Data Structure\",\n analogy: \"A stack of plates in a buffet cupboard\",\n explanation: `### Stack Visualization & Tutorial\\n\\nA Stack is a linear data structure that follows the LIFO (Last In First Out) principle. The element inserted last is the first one to be removed.\\n\\n#### Operations:\\n- **Push**: Add an item to the top.\\n- **Pop**: Remove the top item.\\n- **Peek**: Get the value of the top item without removing it.\\n\\n#### Complexity:\\n- Push/Pop/Peek: O(1) time.`,\n verbalScript: \"Let's explore the Stack operations using a stack of plates. Plates are placed on top and removed from the top.\",\n topicType: \"stack\",\n codeExample: `public class Stack {\n private List list = new ArrayList<>();\n public void push(String item) { list.add(item); }\n public String pop() { return list.remove(list.size() - 1); }\n public String peek() { return list.get(list.size() - 1); }\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initial empty stack\",\n state: { elements: [], action: \"idle\", activeVal: \"\" },\n spokenText: \"We start with an empty stack cupboard.\"\n },\n {\n stepNumber: 2,\n description: \"Push Plate A onto the stack\",\n state: { elements: [\"Plate A\"], action: \"push\", activeVal: \"Plate A\" },\n spokenText: \"We push Plate A onto the stack. It sits at the bottom.\"\n },\n {\n stepNumber: 3,\n description: \"Push Plate B onto the stack\",\n state: { elements: [\"Plate A\", \"Plate B\"], action: \"push\", activeVal: \"Plate B\" },\n spokenText: \"We push Plate B onto the stack. It sits on top of Plate A.\"\n },\n {\n stepNumber: 4,\n description: \"Peek the top plate\",\n state: { elements: [\"Plate A\", \"Plate B\"], action: \"peek\", activeVal: \"Plate B\" },\n spokenText: \"Peeking at the stack tells us Plate B is currently at the top.\"\n },\n {\n stepNumber: 5,\n description: \"Pop the top plate (Plate B)\",\n state: { elements: [\"Plate A\"], action: \"pop\", activeVal: \"Plate B\" },\n spokenText: \"We pop the top plate. Plate B is removed first, leaving Plate A on the stack.\"\n }\n ]\n };\n }\n\n if (topicLower.includes('queue')) {\n return {\n title: \"Queue Data Structure\",\n analogy: \"A line of customers waiting at a movie ticket counter\",\n explanation: `### Queue Visualization & Tutorial\\n\\nA Queue is a linear data structure that follows the FIFO (First In First Out) principle. The first item added is the first one to be removed.\\n\\n#### Operations:\\n- **Enqueue**: Add an item to the back.\\n- **Dequeue**: Remove an item from the front.\\n\\n#### Complexity:\\n- Enqueue/Dequeue: O(1) time.`,\n verbalScript: \"Let's explore the Queue operations using a customer line. Customers join at the back and are served at the front.\",\n topicType: \"queue\",\n codeExample: `public class Queue {\n private List list = new ArrayList<>();\n public void enqueue(String item) { list.add(item); }\n public String dequeue() { return list.remove(0); }\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initial empty queue\",\n state: { elements: [], action: \"idle\", activeVal: \"\" },\n spokenText: \"The ticket line is empty.\"\n },\n {\n stepNumber: 2,\n description: \"Enqueue Alice to the line\",\n state: { elements: [\"Alice\"], action: \"enqueue\", activeVal: \"Alice\" },\n spokenText: \"Alice joins the line. She is at the front.\"\n },\n {\n stepNumber: 3,\n description: \"Enqueue Bob to the line\",\n state: { elements: [\"Alice\", \"Bob\"], action: \"enqueue\", activeVal: \"Bob\" },\n spokenText: \"Bob joins behind Alice.\"\n },\n {\n stepNumber: 4,\n description: \"Dequeue served customer (Alice)\",\n state: { elements: [\"Bob\"], action: \"dequeue\", activeVal: \"Alice\" },\n spokenText: \"Alice is served and leaves the front of the line. Bob is now next in line.\"\n }\n ]\n };\n }\n\n if (topicLower.includes('list') || topicLower.includes('link')) {\n return {\n title: \"Linked List Reversal\",\n analogy: \"Reversing a chain of linked train carriages\",\n explanation: `### Linked List Reversal\\n\\nReversing a singly linked list involves modifying the next pointers of every node so they point to the preceding node instead of the succeeding one.\\n\\n#### How It Works:\\n1. Maintain three pointers: prev (null), curr (head), and next (null).\\n2. Iterate through the list.\\n3. Save the next node: next = curr.next.\\n4. Reverse the pointer: curr.next = prev.\\n5. Move prev and curr forward: prev = curr, curr = next.`,\n verbalScript: \"Let's reverse a linked list of train carriages. We will detach and point each carriage's coupler backward step-by-step.\",\n topicType: \"linked_list\",\n codeExample: `public void reverse() {\n Node prev = null;\n Node curr = head;\n Node next = null;\n while (curr != null) {\n next = curr.next;\n curr.next = prev;\n prev = curr;\n curr = next;\n }\n head = prev;\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Original linked list: Node 1 -> Node 2 -> Node 3\",\n state: {\n nodes: [\n { id: 1, val: 10, label: \"Carriage 1\", nextId: 2 },\n { id: 2, val: 20, label: \"Carriage 2\", nextId: 3 },\n { id: 3, val: 30, label: \"Carriage 3\", nextId: null }\n ],\n action: \"idle\",\n activeNodeId: 1\n },\n spokenText: \"Here is our initial list of carriages pointing from left to right.\"\n },\n {\n stepNumber: 2,\n description: \"Reverse Carriage 1 pointer to point to Null\",\n state: {\n nodes: [\n { id: 1, val: 10, label: \"Carriage 1\", nextId: null },\n { id: 2, val: 20, label: \"Carriage 2\", nextId: 3 },\n { id: 3, val: 30, label: \"Carriage 3\", nextId: null }\n ],\n action: \"reverse\",\n activeNodeId: 1\n },\n spokenText: \"We make Carriage 1 point to null, since it will become the new tail carriage.\"\n },\n {\n stepNumber: 3,\n description: \"Reverse Carriage 2 pointer to point to Carriage 1\",\n state: {\n nodes: [\n { id: 1, val: 10, label: \"Carriage 1\", nextId: null },\n { id: 2, val: 20, label: \"Carriage 2\", nextId: 1 },\n { id: 3, val: 30, label: \"Carriage 3\", nextId: null }\n ],\n action: \"reverse\",\n activeNodeId: 2\n },\n spokenText: \"Now, Carriage 2's coupler is reversed to point back to Carriage 1.\"\n },\n {\n stepNumber: 4,\n description: \"Reverse Carriage 3 pointer to point to Carriage 2\",\n state: {\n nodes: [\n { id: 1, val: 10, label: \"Carriage 1\", nextId: null },\n { id: 2, val: 20, label: \"Carriage 2\", nextId: 1 },\n { id: 3, val: 30, label: \"Carriage 3\", nextId: 2 }\n ],\n action: \"reverse\",\n activeNodeId: 3\n },\n spokenText: \"Finally, Carriage 3 points back to Carriage 2. Carriage 3 is now the new head of our reversed chain.\"\n }\n ]\n };\n }\n\n // Fallback for everything else: General Flow\n return {\n title: topicName,\n analogy: `Understanding ${topicName} step-by-step`,\n explanation: `### Visual Explanation of ${topicName}\\n\\nHere is a step-by-step breakdown of how **${topicName}** operates in production systems, detailing its core components and architectural layers.\\n\\n#### Highlights:\\n- Clear modular boundaries.\\n- Step-by-step data flows.\\n- High-availability considerations.\\n\\n#### Code Example:\\n\\`\\`\\`javascript\\n// Conceptual illustration\\nfunction executeTask() {\\n console.log(\"Executing ${topicName} workflow...\");\\n}\\n\\`\\`\\``,\n verbalScript: `Let's walk through the steps of ${topicName}. We'll examine how data flows from the initialization phase down to completion.`,\n topicType: \"general_flow\",\n codeExample: `// Conceptual example\\nfunction executeWorkflow() {\\n console.log(\"Executing ${topicName} workflow...\");\\n}`,\n visualSteps: [\n {\n stepNumber: 1,\n description: \"Initialize client-side action\",\n state: {\n nodes: [\n { id: 1, label: \"Client Request\", status: \"active\" },\n { id: 2, label: \"Processing Service\", status: \"idle\" },\n { id: 3, label: \"Data Warehouse\", status: \"idle\" }\n ],\n connections: [\n { from: 1, to: 2 },\n { from: 2, to: 3 }\n ],\n activeNodeId: 1\n },\n spokenText: \"First, the user initiates a client request to trigger the workflow.\"\n },\n {\n stepNumber: 2,\n description: \"Processing request through services\",\n state: {\n nodes: [\n { id: 1, label: \"Client Request\", status: \"done\" },\n { id: 2, label: \"Processing Service\", status: \"active\" },\n { id: 3, label: \"Data Warehouse\", status: \"idle\" }\n ],\n connections: [\n { from: 1, to: 2 },\n { from: 2, to: 3 }\n ],\n activeNodeId: 2\n },\n spokenText: \"Next, our processing service validates and transforms the request data.\"\n },\n {\n stepNumber: 3,\n description: \"Persisting changes to database\",\n state: {\n nodes: [\n { id: 1, label: \"Client Request\", status: \"done\" },\n { id: 2, label: \"Processing Service\", status: \"done\" },\n { id: 3, label: \"Data Warehouse\", status: \"active\" }\n ],\n connections: [\n { from: 1, to: 2 },\n { from: 2, to: 3 }\n ],\n activeNodeId: 3\n },\n spokenText: \"Finally, the changes are committed to the data warehouse, and a success response is returned.\"\n }\n ]\n };\n}\n\nasync function explainTopicWithVisuals(topicName) {\n const prompt = `You are a world-class computer science teacher and algorithm visualizer designer.\nExplain the following topic or problem: \"${topicName}\"\n\nCRITICAL INSTRUCTION:\n1. Every explanation and visualization MUST be built around a concrete, relatable real-world example/analogy (e.g. sorting books on a shelf by size, searching for a name in an alphabetical student roster/phonebook, a stack of dinner plates, a ticket queue of customers at a cinema, reversing a chain of linked train carriages, or tracking recursive family ancestors).\n2. The explanation, code example, visual step descriptions, and speech scripts must consistently refer to this concrete analogy/example and its items.\n3. Provide granular, step-by-step state logs. For algorithms, do not skip steps. Generate between 8 and 15 steps representing the full flow of the analogy/example from start to finish.\n4. Ensure the JSON returned is fully complete and valid.\n\nIf the topic is a standard algorithm/data structure, classify it into one of these types:\n- \"bubble_sort\" (for sorting algorithms like bubble, selection, insertion, quick, merge)\n- \"binary_search\" (for search algorithms and interval division)\n- \"stack\" (for stack operations LIFO)\n- \"queue\" (for queue operations FIFO)\n- \"linked_list\" (for linked lists insertion, deletion, reversal)\n- \"binary_tree\" (for tree traversals BFS, DFS, insertion)\n- \"recursion\" (for recursive operations like Fibonacci or factorial)\n- \"general_flow\" (for any general concepts like JS closures, DB indexing, API calls, OAuth, etc.)\n\nFor \"general_flow\", you will provide a sequence of visual nodes/elements that represent the flow of the explanation.\nFor the specific types, provide the corresponding structural state configuration for each animation step.\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"title\": \"${topicName}\",\n \"analogy\": \"Short 1-2 sentence describing the real-world analogy chosen (e.g., 'Sorting 5 books of different heights on a shelf').\",\n \"explanation\": \"Markdown string containing complete written explanation, detail of the analogy, best practices, and code example\",\n \"verbalScript\": \"A continuous narration script to be read aloud via Text-to-Speech (TTS). It should be engaging, informative, and synchronized step-by-step with the animation steps.\",\n \"topicType\": \"bubble_sort|binary_search|stack|queue|linked_list|binary_tree|recursion|general_flow\",\n \"codeExample\": \"A complete, working code implementation of the concept.\",\n \"visualSteps\": [\n {\n \"stepNumber\": 1,\n \"description\": \"Short label/action for this step referencing the real-world example (e.g. 'Compare height of Book A (15cm) and Book B (12cm)')\",\n \"state\": {\n // state config\n },\n \"spokenText\": \"The specific portion of the narration corresponding to this animation step, explaining the action in terms of the real-world analogy.\"\n }\n ]\n}`;\n\n try {\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n } catch (err) {\n console.warn('âš ī¸ AI explain topic fallback used for topic:', topicName, 'Error:', err.message);\n return getVisualExplanationFallback(topicName);\n }\n}\n\nmodule.exports = { callAI, cleanJSONResponse, analyzeFile, analyzeImage, analyzeUrlContent, generateQuestionsByAI, answerAndCategorizeQuestion, explainTopicWithVisuals };\n\n\n"}, "c45e18b93b788df7c14daa1508c5a5b5dfad0a13": {"t": 1785766989.6194189, "n": 18163, "h": "6d6a3d9165d2bc18", "c": "require('dotenv').config();\n\n/**\n * Universal AI Service\n * Supports OpenAI, OpenRouter, Groq, DeepSeek, Ollama (Local), Claude, or any OpenAI-compatible API.\n */\n\nasync function callAI(prompt) {\n // Always reload dotenv with override: true so changes in .env take effect immediately\n require('dotenv').config({ override: true });\n\n let provider = (process.env.AI_PROVIDER || 'openai').trim().toLowerCase();\n let apiKey = (process.env.AI_API_KEY || process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY || process.env.GEMINI_API_KEY || '').trim();\n\n // Strip inline comments if any\n if (apiKey.includes('#')) {\n apiKey = apiKey.split('#')[0].trim();\n }\n\n if (!apiKey || apiKey.includes('PASTE_') || apiKey.includes('your_api') || apiKey.includes('...')) {\n throw new Error('Please paste your real API key (from Groq, OpenAI, etc.) into server/.env file under AI_API_KEY=');\n }\n\n // Auto-detect provider from API key format\n if (apiKey.startsWith('gsk_')) {\n provider = 'groq';\n } else if (apiKey.startsWith('sk-or-')) {\n provider = 'openrouter';\n }\n\n let model = (process.env.AI_MODEL || '').trim();\n if (!model) {\n if (provider === 'groq') model = 'llama-3.3-70b-versatile';\n else if (provider === 'openrouter') model = 'meta-llama/llama-3.3-70b-instruct';\n else if (provider === 'deepseek') model = 'deepseek-chat';\n else if (provider === 'ollama') model = 'llama3';\n else model = 'gpt-4o-mini';\n }\n\n let baseUrl = (process.env.AI_BASE_URL || '').trim();\n if (!baseUrl) {\n if (provider === 'groq') baseUrl = 'https://api.groq.com/openai/v1';\n else if (provider === 'openrouter') baseUrl = 'https://openrouter.ai/api/v1';\n else if (provider === 'deepseek') baseUrl = 'https://api.deepseek.com/v1';\n else if (provider === 'ollama') baseUrl = 'http://localhost:11434/v1';\n else baseUrl = 'https://api.openai.com/v1';\n }\n\n // Remove trailing slashes\n baseUrl = baseUrl.replace(/\\/+$/, '');\n\n // 1. Gemini Fallback if provider explicitly set to gemini\n if (provider === 'gemini') {\n const { GoogleGenerativeAI } = require('@google/generative-ai');\n const genAI = new GoogleGenerativeAI(apiKey);\n const geminiModel = genAI.getGenerativeModel({ model: model || 'gemini-2.0-flash' });\n const res = await geminiModel.generateContent(prompt);\n return res.response.text();\n }\n\n // 2. OpenAI / OpenRouter / Groq / DeepSeek / Ollama / Custom API (Standard Chat Completion)\n const endpoint = `${baseUrl}/chat/completions`;\n const headers = {\n 'Content-Type': 'application/json'\n };\n\n if (apiKey) {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n if (provider === 'openrouter') {\n headers['HTTP-Referer'] = 'http://localhost:3000';\n headers['X-Title'] = 'InterviewHub';\n }\n\n const maxTokens = provider === 'groq' ? 2500 : 8192;\n\n const body = {\n model: model,\n messages: [\n {\n role: 'system',\n content: 'You are a Principal Software Engineer and Technical Interviewer. Always respond in strict valid JSON format. All code snippets in string fields must have properly escaped quotes and newline characters.'\n },\n {\n role: 'user',\n content: prompt\n }\n ],\n temperature: 0.2,\n max_tokens: maxTokens,\n response_format: { type: 'json_object' }\n };\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`❌ AI API Error (${provider}):`, response.status, errorText);\n\n // Auto-retry with lighter model / lower max_tokens if HTTP 400, 413 (TPM limit), or 429\n if (response.status === 400 || response.status === 413 || response.status === 429) {\n console.log('🔄 Retrying AI call with fallback parameters and model...');\n delete body.response_format;\n if (provider === 'groq') {\n body.model = 'llama-3.1-8b-instant';\n body.max_tokens = 2500;\n } else {\n body.max_tokens = 2000;\n }\n const retryRes = await fetch(endpoint, { method: 'POST', headers, body: JSON.stringify(body) });\n if (retryRes.ok) {\n const retryData = await retryRes.json();\n return retryData.choices && retryData.choices[0] && retryData.choices[0].message ? retryData.choices[0].message.content : '';\n }\n }\n\n if (response.status === 401) {\n throw new Error(`Invalid API Key for ${provider.toUpperCase()}. Please check your key in server/.env.`);\n }\n if (response.status === 429) {\n throw new Error(`Rate limit reached for ${provider.toUpperCase()}. Please wait a few seconds and try again.`);\n }\n throw new Error(`AI API call failed [${response.status}]: ${errorText}`);\n }\n\n const data = await response.json();\n const content = data.choices && data.choices[0] && data.choices[0].message ? data.choices[0].message.content : '';\n return content;\n}\n\nfunction cleanJSONResponse(rawText) {\n if (!rawText) throw new Error(\"AI returned an empty response. Please try again.\");\n let cleaned = rawText.trim();\n\n // Strip ```json markdown blocks if present\n if (cleaned.startsWith('```')) {\n cleaned = cleaned.replace(/^```[a-z]*\\n?/i, '').replace(/\\n?```$/i, '').trim();\n }\n\n // Extract JSON object substring\n const match = cleaned.match(/\\{[\\s\\S]*\\}/);\n if (match) {\n cleaned = match[0];\n }\n\n try {\n return JSON.parse(cleaned);\n } catch (err) {\n // 1. Try sanitizing control characters\n try {\n const sanitized = cleaned\n .replace(/[\\u0000-\\u001F]+/g, (m) => {\n if (m === '\\n') return '\\\\n';\n if (m === '\\r') return '\\\\r';\n if (m === '\\t') return '\\\\t';\n return '';\n });\n return JSON.parse(sanitized);\n } catch (e) {\n // 2. Salvage partial questions array if JSON was truncated by max tokens\n try {\n const questionsMatch = cleaned.match(/\"questions\"\\s*:\\s*\\[([\\s\\S]*)/);\n if (questionsMatch) {\n let questionsStr = questionsMatch[1];\n const lastObjEnd = questionsStr.lastIndexOf('}');\n if (lastObjEnd !== -1) {\n let validArrayStr = '[' + questionsStr.substring(0, lastObjEnd + 1) + ']';\n validArrayStr = validArrayStr.replace(/[\\u0000-\\u001F]+/g, (m) => (m === '\\n' ? '\\\\n' : m === '\\t' ? '\\\\t' : ''));\n const parsedQuestions = JSON.parse(validArrayStr);\n if (Array.isArray(parsedQuestions) && parsedQuestions.length > 0) {\n return {\n explanation: \"Successfully extracted questions from document\",\n technologies: [\"Java\", \"DSA\"],\n questions: parsedQuestions\n };\n }\n }\n }\n } catch (salvageErr) {\n console.error(\"Salvage error:\", salvageErr);\n }\n console.error(\"JSON parse error on raw text snippet:\", rawText.substring(0, 300));\n throw new Error(\"AI returned invalid JSON format. Please click Analyze again.\");\n }\n }\n}\n\nasync function analyzeFile(fileContent, fileName, fileType) {\n const prompt = `Analyze the following technical file/document/code content thoroughly and extract structured interview questions & answers across ALL sections.\n\nCRITICAL INSTRUCTIONS FOR ACCURATE EXTRACTION:\n1. DO NOT summarize, abbreviate, or shorten any code snippets or output comments! Preserve the exact, full code (including import statements, class headers, and main methods) in \"codeExample\".\n2. Include the exact output comment (e.g. \"// output :- ...\") and step-by-step logic in the \"answer\" field verbatim.\n3. Extract EVERY distinct topic, pattern, or code block present in the text (such as Variables, Conditional Statements, Switch Statement, Loops, Methods, Star Patterns, Arrays, Basic Sorting, 2D Arrays, String Manipulation, Bit Manipulation, OOP Concepts, Recursion, ArrayList, LinkedList, Doubly LinkedList, Cycle Detection, etc.).\n4. Assign clear, descriptive question titles based on the topic comments or logic (e.g. \"Variables in Java - Declaration & Print\", \"Check Even or Odd Number using Modulo\", \"Tax Calculator Logic\", \"Print Largest of 3 Numbers\", \"Butterfly Star Pattern\", \"Reverse a Linked List\", \"Detect Cycle in Linked List using Slow & Fast Pointers\").\n5. Auto-detect language precisely: Use \"DSA (Java)\" for Java algorithms & Data Structures, \"Java\" for core Java, \"Python\", \"JavaScript\", \"C++\", \"SQL\", etc.\n6. Map category accurately:\n - \"Basics\" for Variables, Conditionals, Loops, Switch Statements, Methods\n - \"OOP\" for Classes, Inheritance, Constructors, Abstraction, Polymorphism\n - \"Data Structures\" for Arrays, 2D Arrays, Strings, ArrayList, LinkedList, Doubly LinkedList\n - \"Algorithms\" for Sorting (Bubble, Selection, Insertion), Binary Search, Linear Search, Recursion, Bit Manipulation\n - \"Design Patterns\", \"Database\", \"System Design\"\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Brief overview of the entire document and topics covered\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|Java|Python|JavaScript|...\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"Descriptive Question Title\",\n \"answer\": \"Detailed answer with exact output explanation verbatim\",\n \"codeExample\": \"Full intact code snippet with all imports, class, and main method\"\n }\n ]\n}\n\nFILE NAME: ${fileName}\nFILE TYPE: ${fileType}\nCONTENT:\n${fileContent}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nasync function analyzeImage(imageBase64, mimeType, fileName) {\n require('dotenv').config({ override: true });\n let apiKey = (process.env.AI_API_KEY || process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY || process.env.GEMINI_API_KEY || '').trim();\n if (apiKey.includes('#')) apiKey = apiKey.split('#')[0].trim();\n\n // 1. Try Gemini Vision if Gemini API key or gemini provider\n if (apiKey.startsWith('AIza') || (process.env.AI_PROVIDER || '').toLowerCase() === 'gemini') {\n try {\n const { GoogleGenerativeAI } = require('@google/generative-ai');\n const genAI = new GoogleGenerativeAI(apiKey);\n const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });\n const prompt = `Analyze this image (which may be a screenshot of code, diagram, question paper, resume, or technical notes).\nExtract all technical details and generate 8-15 technical interview questions with full detailed answers and code examples.\n\nReturn ONLY valid JSON:\n{\n \"explanation\": \"...\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"Java|JavaScript|Python|React|DSA (Java)|System Design\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n const imagePart = {\n inlineData: {\n data: imageBase64,\n mimeType: mimeType || 'image/jpeg'\n }\n };\n const res = await model.generateContent([prompt, imagePart]);\n return cleanJSONResponse(res.response.text());\n } catch (err) {\n console.warn('Gemini vision failed, falling back to OpenAI/Groq vision or prompt:', err.message);\n }\n }\n\n // 2. Try OpenAI / Groq Vision or Vision chat completion endpoint\n const endpoint = `https://api.groq.com/openai/v1/chat/completions`;\n const body = {\n model: 'llama-3.2-11b-vision-preview',\n messages: [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `Analyze this image containing technical content, code, questions, or notes.\nGenerate 8-15 technical interview questions with comprehensive answers and code examples.\nReturn ONLY valid JSON matching this exact structure:\n{\n \"explanation\": \"...\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|DSA (Python)|Java|JavaScript|Python|React|Database\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`\n },\n {\n type: 'image_url',\n image_url: {\n url: `data:${mimeType};base64,${imageBase64}`\n }\n }\n ]\n }\n ]\n };\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`\n },\n body: JSON.stringify(body)\n });\n if (response.ok) {\n const data = await response.json();\n const content = data.choices[0].message.content;\n return cleanJSONResponse(content);\n }\n } catch (err) {\n console.warn('Groq vision failed:', err.message);\n }\n\n // 3. Fallback: Use text model to generate technical interview questions for the uploaded image/file topic\n try {\n console.log(`â„šī¸ Vision model unavailable/restricted, using standard AI model for image: ${fileName}`);\n const fallbackPrompt = `The user uploaded/pasted an image screenshot/file named \"${fileName}\".\nGenerate 8-12 comprehensive, high-frequency technical interview questions with answers and code examples covering relevant computer science concepts (Data Structures, Algorithms, OOP, Web/System Architecture).\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Analyzed technical content from file '${fileName}'. Extracted concepts and generated technical Q&As.\",\n \"technologies\": [\"Software Engineering\", \"Algorithms\", \"Data Structures\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)\",\n \"category\": \"Algorithms\",\n \"difficulty\": \"Medium\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n const responseText = await callAI(fallbackPrompt);\n return cleanJSONResponse(responseText);\n } catch (err) {\n throw new Error(`Image analysis could not be completed. Please paste the copied text directly into the text tab!`);\n }\n}\n\nasync function analyzeUrlContent(urlContent, url) {\n const prompt = `Analyze the web page / document content extracted from the URL: \"${url}\".\n\nCRITICAL INSTRUCTION: EXTRACT EVERY SINGLE INTERVIEW QUESTION & ANSWER PRESENT IN THIS DOCUMENT OR CHAT CONVERSATION! Generate up to 30+ questions covering ALL topics mentioned. DO NOT limit or cap the question count to 8!\n\n1. Summarize the content in 3-5 sentences.\n2. List detected technologies in array format.\n3. For EVERY question, topic, or code problem found:\n - Provide a clear, descriptive question title.\n - Put the exact answer and output explanation verbatim in \"answer\".\n - Put the full, intact code snippet (including imports, classes, and main methods) in \"codeExample\".\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"explanation\": \"Brief overview of all topics in the document\",\n \"technologies\": [\"Tech1\", \"Tech2\"],\n \"questions\": [\n {\n \"language\": \"DSA (Java)|Java|Python|JavaScript|React|Node.js|SQL|...\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"Descriptive Question Title\",\n \"answer\": \"Detailed answer with exact output explanation\",\n \"codeExample\": \"Full intact code snippet\"\n }\n ]\n}\n\nURL: ${url}\nCONTENT:\n${urlContent.substring(0, 60000)}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nasync function generateQuestionsByAI(language, category = 'All', count = 10, difficulty = 'Mixed') {\n const prompt = `Generate exactly ${count} realistic, high-frequency technical interview questions for ${language}${category && category !== 'All' ? ` focused on ${category}` : ''}.\n\nIMPORTANT: Format and arrange questions in a logical STEP-BY-STEP ROADMAP PATTERN (starting from Step 1: Core Basics, moving to Step 2: OOP, Step 3: Data Structures, Step 4: Algorithms, up to Advanced System Design).\n\nMix of difficulty: ${difficulty}.\nCategories allowed: \"Basics\", \"OOP\", \"Data Structures\", \"Algorithms\", \"Design Patterns\", \"Framework-Specific\", \"Database\", \"System Design\".\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"questions\": [\n {\n \"language\": \"${language}\",\n \"category\": \"Basics|OOP|Data Structures|Algorithms|Design Patterns|Framework-Specific|Database|System Design\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"...\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n }\n ]\n}`;\n\n const responseText = await callAI(prompt);\n const parsed = cleanJSONResponse(responseText);\n return parsed.questions || [];\n}\n\nasync function answerAndCategorizeQuestion(userQuestion, suggestedLanguage = '') {\n const prompt = `You are a Senior Technical Interview Expert.\nA user asked the following interview question:\n\"${userQuestion}\"\n\nYour job:\n1. Auto-detect the programming language / technology. Use \"DSA (Java)\" for Java algorithms/Data Structures, \"DSA (Python)\" for Python algorithms, \"DSA (C++)\" for C++ algorithms, or \"Java\", \"JavaScript\", \"Python\", \"React\", \"Node.js\", \"SQL\", \"MongoDB\", \"Project: RealityEngine\", etc.\n2. Auto-categorize into ONE of: \"Basics\", \"OOP\", \"Data Structures\", \"Algorithms\", \"Design Patterns\", \"Framework-Specific\", \"Database\", \"System Design\".\n3. Auto-assign difficulty: \"Easy\", \"Medium\", or \"Hard\".\n4. Write a comprehensive, expert interview answer (4-8 sentences).\n5. Write a clean, commented code example demonstrating the answer.\n\nReturn ONLY valid JSON in this exact structure:\n{\n \"language\": \"...\",\n \"category\": \"...\",\n \"difficulty\": \"Easy|Medium|Hard\",\n \"question\": \"${userQuestion.replace(/\"/g, '\\\\\"')}\",\n \"answer\": \"...\",\n \"codeExample\": \"...\"\n}`;\n\n const responseText = await callAI(prompt);\n return cleanJSONResponse(responseText);\n}\n\nmodule.exports = { callAI, cleanJSONResponse, analyzeFile, analyzeImage, analyzeUrlContent, generateQuestionsByAI, answerAndCategorizeQuestion };\n\n\n"}}}}