Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
1 line
55 KiB
JSON
1 line
55 KiB
JSON
{"repo": "bsh54/minerva", "files": {"src/app/api/chat/route.ts": {"524c30e3ec729b727dd648adce8afc0cc5c43c1f": {"t": 1785745570.6886315, "n": 4090, "h": "2664ef08f8638450", "c": "import { NextRequest } from 'next/server';\nimport { createClient } from '@/utils/supabase/server';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const supabase = await createClient();\n const { data: { user }, error: authError } = await supabase.auth.getUser();\n\n if (authError || !user) {\n return new Response(\n JSON.stringify({ success: false, error: 'Non authentifié' }),\n { status: 401, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return new Response(\n JSON.stringify({ success: false, error: 'Configuration API manquante' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const { messages } = await request.json();\n\n if (!messages || !Array.isArray(messages)) {\n return new Response(\n JSON.stringify({ success: false, error: 'Messages invalides' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await fetch(apiUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n },\n body: JSON.stringify({\n model: 'gpt-5.4-nano',\n messages: [\n {\n role: 'system',\n content: 'You are a helpful AI assistant for MINERVA, a STEM learning platform. Answer questions clearly and concisely. If asked in French, respond in French. If asked in English, respond in English.'\n },\n ...messages\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: true\n })\n });\n\n if (!response.ok) {\n console.error('DeepSeek API error:', response.status, response.statusText);\n return new Response(\n JSON.stringify({ success: false, error: 'Erreur API DeepSeek' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const encoder = new TextEncoder();\n const stream = new ReadableStream({\n async start(controller) {\n const reader = response.body?.getReader();\n if (!reader) {\n controller.close();\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed === 'data: [DONE]') continue;\n if (trimmed.startsWith('data: ')) {\n try {\n const json = JSON.parse(trimmed.slice(6));\n const content = json.choices?.[0]?.delta?.content;\n if (content) {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify({ content })}\\n\\n`));\n }\n } catch (e) {\n console.error('Parse error:', e);\n }\n }\n }\n }\n } catch (error) {\n console.error('Stream error:', error);\n } finally {\n controller.close();\n }\n }\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n }\n });\n\n } catch (error: any) {\n console.error('Erreur chat API:', error);\n return new Response(\n JSON.stringify({ success: false, error: 'Erreur lors de la génération de la réponse' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}\n"}, "672163afc6df0704fed4e727d7bcfc38b21605c4": {"t": 1785754351.936039, "n": 4090, "h": "2664ef08f8638450", "c": "import { NextRequest } from 'next/server';\nimport { createClient } from '@/utils/supabase/server';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const supabase = await createClient();\n const { data: { user }, error: authError } = await supabase.auth.getUser();\n\n if (authError || !user) {\n return new Response(\n JSON.stringify({ success: false, error: 'Non authentifié' }),\n { status: 401, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return new Response(\n JSON.stringify({ success: false, error: 'Configuration API manquante' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const { messages } = await request.json();\n\n if (!messages || !Array.isArray(messages)) {\n return new Response(\n JSON.stringify({ success: false, error: 'Messages invalides' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await fetch(apiUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n },\n body: JSON.stringify({\n model: 'gpt-5.4-nano',\n messages: [\n {\n role: 'system',\n content: 'You are a helpful AI assistant for MINERVA, a STEM learning platform. Answer questions clearly and concisely. If asked in French, respond in French. If asked in English, respond in English.'\n },\n ...messages\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: true\n })\n });\n\n if (!response.ok) {\n console.error('DeepSeek API error:', response.status, response.statusText);\n return new Response(\n JSON.stringify({ success: false, error: 'Erreur API DeepSeek' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const encoder = new TextEncoder();\n const stream = new ReadableStream({\n async start(controller) {\n const reader = response.body?.getReader();\n if (!reader) {\n controller.close();\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed === 'data: [DONE]') continue;\n if (trimmed.startsWith('data: ')) {\n try {\n const json = JSON.parse(trimmed.slice(6));\n const content = json.choices?.[0]?.delta?.content;\n if (content) {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify({ content })}\\n\\n`));\n }\n } catch (e) {\n console.error('Parse error:', e);\n }\n }\n }\n }\n } catch (error) {\n console.error('Stream error:', error);\n } finally {\n controller.close();\n }\n }\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n }\n });\n\n } catch (error: any) {\n console.error('Erreur chat API:', error);\n return new Response(\n JSON.stringify({ success: false, error: 'Erreur lors de la génération de la réponse' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}\n"}, "e1654bcd76a2b41c83013dc5774d901493055248": {"t": 1785754353.2885306, "n": 4091, "h": "1f56ec05edc95f00", "c": "import { NextRequest } from 'next/server';\nimport { createClient } from '@/utils/supabase/server';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const supabase = await createClient();\n const { data: { user }, error: authError } = await supabase.auth.getUser();\n\n if (authError || !user) {\n return new Response(\n JSON.stringify({ success: false, error: 'Non authentifié' }),\n { status: 401, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return new Response(\n JSON.stringify({ success: false, error: 'Configuration API manquante' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const { messages } = await request.json();\n\n if (!messages || !Array.isArray(messages)) {\n return new Response(\n JSON.stringify({ success: false, error: 'Messages invalides' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await fetch(apiUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n },\n body: JSON.stringify({\n model: 'deepseek-chat',\n messages: [\n {\n role: 'system',\n content: 'You are a helpful AI assistant for MINERVA, a STEM learning platform. Answer questions clearly and concisely. If asked in French, respond in French. If asked in English, respond in English.'\n },\n ...messages\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: true\n })\n });\n\n if (!response.ok) {\n console.error('DeepSeek API error:', response.status, response.statusText);\n return new Response(\n JSON.stringify({ success: false, error: 'Erreur API DeepSeek' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const encoder = new TextEncoder();\n const stream = new ReadableStream({\n async start(controller) {\n const reader = response.body?.getReader();\n if (!reader) {\n controller.close();\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed === 'data: [DONE]') continue;\n if (trimmed.startsWith('data: ')) {\n try {\n const json = JSON.parse(trimmed.slice(6));\n const content = json.choices?.[0]?.delta?.content;\n if (content) {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify({ content })}\\n\\n`));\n }\n } catch (e) {\n console.error('Parse error:', e);\n }\n }\n }\n }\n } catch (error) {\n console.error('Stream error:', error);\n } finally {\n controller.close();\n }\n }\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n }\n });\n\n } catch (error: any) {\n console.error('Erreur chat API:', error);\n return new Response(\n JSON.stringify({ success: false, error: 'Erreur lors de la génération de la réponse' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}\n"}, "6a3c11b1c0ad6a9525f7181c6350051486ccfe49": {"t": 1785754354.7379332, "n": 2328, "h": "99352ea918da018b", "c": "import { NextRequest, NextResponse } from 'next/server';\nimport { createClient } from '@/utils/supabase/server';\nimport axios from 'axios';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const supabase = await createClient();\n const { data: { user }, error: authError } = await supabase.auth.getUser();\n\n if (authError || !user) {\n return NextResponse.json(\n { success: false, error: 'Non authentifié' },\n { status: 401 }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return NextResponse.json(\n { success: false, error: 'Configuration API manquante' },\n { status: 500 }\n );\n }\n\n const { messages } = await request.json();\n\n if (!messages || !Array.isArray(messages)) {\n return NextResponse.json(\n { success: false, error: 'Messages invalides' },\n { status: 400 }\n );\n }\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await axios.post(apiUrl, {\n model: 'deepseek-chat',\n messages: [\n {\n role: 'system',\n content: 'You are a helpful AI assistant for MINERVA, a STEM learning platform. Answer questions clearly and concisely. If asked in French, respond in French. If asked in English, respond in English.'\n },\n ...messages\n ],\n temperature: 0.7,\n max_tokens: 1000,\n stream: false\n }, {\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n 'Accept-Encoding': 'identity'\n },\n timeout: 30000,\n decompress: false,\n responseType: 'text'\n });\n\n const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n const assistantMessage = data.choices?.[0]?.message?.content || \"\";\n\n return NextResponse.json({\n success: true,\n message: assistantMessage\n });\n\n } catch (error: any) {\n console.error('Erreur chat API:', error);\n return NextResponse.json(\n { success: false, error: 'Erreur lors de la génération de la réponse' },\n { status: 500 }\n );\n }\n}\n"}, "ee5939d7b46f86ef68eec121f4b8c33dfd1a4470": {"t": 1786068273.5207458, "n": 4090, "h": "2664ef08f8638450", "c": "import { NextRequest } from 'next/server';\nimport { createClient } from '@/utils/supabase/server';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const supabase = await createClient();\n const { data: { user }, error: authError } = await supabase.auth.getUser();\n\n if (authError || !user) {\n return new Response(\n JSON.stringify({ success: false, error: 'Non authentifié' }),\n { status: 401, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return new Response(\n JSON.stringify({ success: false, error: 'Configuration API manquante' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const { messages } = await request.json();\n\n if (!messages || !Array.isArray(messages)) {\n return new Response(\n JSON.stringify({ success: false, error: 'Messages invalides' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await fetch(apiUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n },\n body: JSON.stringify({\n model: 'gpt-5.4-nano',\n messages: [\n {\n role: 'system',\n content: 'You are a helpful AI assistant for MINERVA, a STEM learning platform. Answer questions clearly and concisely. If asked in French, respond in French. If asked in English, respond in English.'\n },\n ...messages\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: true\n })\n });\n\n if (!response.ok) {\n console.error('DeepSeek API error:', response.status, response.statusText);\n return new Response(\n JSON.stringify({ success: false, error: 'Erreur API DeepSeek' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const encoder = new TextEncoder();\n const stream = new ReadableStream({\n async start(controller) {\n const reader = response.body?.getReader();\n if (!reader) {\n controller.close();\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed === 'data: [DONE]') continue;\n if (trimmed.startsWith('data: ')) {\n try {\n const json = JSON.parse(trimmed.slice(6));\n const content = json.choices?.[0]?.delta?.content;\n if (content) {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify({ content })}\\n\\n`));\n }\n } catch (e) {\n console.error('Parse error:', e);\n }\n }\n }\n }\n } catch (error) {\n console.error('Stream error:', error);\n } finally {\n controller.close();\n }\n }\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n }\n });\n\n } catch (error: any) {\n console.error('Erreur chat API:', error);\n return new Response(\n JSON.stringify({ success: false, error: 'Erreur lors de la génération de la réponse' }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}\n"}}, "src/app/api/generate-course/route.ts": {"524c30e3ec729b727dd648adce8afc0cc5c43c1f": {"t": 1785745584.2791407, "n": 5778, "h": "666749a5eb0da105", "c": "import { NextRequest, NextResponse } from 'next/server';\nimport axios from 'axios';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const { text, locale = 'en', explanationLevel = 'detailed', additionalSpecs = '' } = await request.json();\n\n if (!text || text.trim().length < 10) {\n return NextResponse.json(\n { success: false, error: 'Texte fourni trop court ou invalide.' },\n { status: 400 }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return NextResponse.json(\n { success: false, error: 'Configuration API manquante' },\n { status: 500 }\n );\n }\n\n const languageInstruction = locale === 'fr'\n ? 'Tu DOIS répondre en FRANÇAIS.'\n : 'You MUST respond in ENGLISH.';\n\n const levelInstruction = explanationLevel === 'simple'\n ? (locale === 'fr'\n ? 'Crée un plan CONCIS et SYNTHÉTIQUE avec 2-4 modules maximum et 3-5 topics par module. Reste bref et va à l\\'essentiel. IMPORTANT: L\\'étudiant est DÉBUTANT, explique chaque concept de manière accessible.'\n : 'Create a CONCISE and SYNTHETIC plan with 2-4 modules maximum and 3-5 topics per module. Keep it brief and to the point. IMPORTANT: The student is a BEGINNER, explain each concept in an accessible way.')\n : (locale === 'fr'\n ? 'Crée un plan TRÈS DÉTAILLÉ ET EXHAUSTIF avec autant de modules et topics que nécessaire (pas de limite - 5, 10, 15+ modules si le contenu est riche). Sois GRANULAIRE et décompose en profondeur. IMPORTANT: L\\'étudiant est DÉBUTANT, décompose chaque concept complexe en petites notions simples et progressives.'\n : 'Create a VERY DETAILED AND EXHAUSTIVE plan with as many modules and topics as necessary (no limit - 5, 10, 15+ modules if content is rich). Be GRANULAR and break down in depth. IMPORTANT: The student is a BEGINNER, break down each complex concept into small simple and progressive notions.');\n\n const additionalContext = additionalSpecs.trim()\n ? (locale === 'fr'\n ? `\\n\\nINSTRUCTIONS SUPPLÉMENTAIRES DE L'UTILISATEUR:\\n${additionalSpecs}`\n : `\\n\\nADDITIONAL USER INSTRUCTIONS:\\n${additionalSpecs}`)\n : '';\n\n const systemPrompt = `You are an expert STEM teacher. The student provides raw notes or text.\nYour role is to generate a STUDY PLAN structured in main Modules, each containing several specific sub-chapters (topics).\nThe goal is for the student to check exactly the small concepts they want to review.\n\n${languageInstruction}\n${levelInstruction}\n${additionalContext}\n\nABSOLUTE RULES:\n1. You MUST respond ONLY with a valid and extremely compact JSON object (no unnecessary spaces).\n2. Do NOT use markdown, no \\`\\`\\`json. Return direct JSON text.\n3. Analyze the provided content and adapt the number of modules and topics based on the explanation level chosen.\n4. Be GRANULAR: break down complex concepts into small precise and actionable notions.\n\nSTRICTLY expected format:\n{\"courseTitle\":\"Catchy title\",\"summary\":\"Very brief summary\",\"modules\":[{\"id\":1,\"title\":\"Main Theme (e.g., Kinematics)\",\"estimatedMinutes\":20,\"topics\":[{\"id\":\"1-1\",\"title\":\"Notion 1 (e.g., Speed and Acceleration)\"},{\"id\":\"1-2\",\"title\":\"Notion 2 (e.g., Free Fall)\"}]}]}\n\nAdapt the number of modules and topics to the RICHNESS and COMPLEXITY of the provided content AND the explanation level requested.`;\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await axios.post(apiUrl, {\n model: 'gpt-5.4-nano',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: `Text to analyze and transform into detailed plan:\\n\"${text}\"\\n\\nGenerate the JSON now:` }\n ],\n temperature: 0.6,\n max_tokens: 1500,\n stream: false\n }, {\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n 'Accept-Encoding': 'identity' // Demander une réponse NON compressée\n },\n timeout: 120000,\n decompress: false, // Ne pas décompresser automatiquement\n responseType: 'text' // Lire comme texte brut\n });\n\n // Parser la réponse texte en JSON\n const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n let generatedContent = data.choices?.[0]?.message?.content || \"\";\n\n // Nettoyage Markdown\n let cleanJson = generatedContent.trim();\n if (cleanJson.startsWith('```json')) cleanJson = cleanJson.replace(/^```json/, '');\n else if (cleanJson.startsWith('```')) cleanJson = cleanJson.replace(/^```/, '');\n if (cleanJson.endsWith('```')) cleanJson = cleanJson.replace(/```$/, '');\n cleanJson = cleanJson.trim();\n\n // Auto-réparation basique si le json coupe net à la fin\n if (!cleanJson.endsWith('}') && cleanJson.includes('\"modules\"')) {\n if (!cleanJson.endsWith(']')) cleanJson += ']}';\n else cleanJson += '}';\n }\n\n try {\n const parsedData = JSON.parse(cleanJson);\n return NextResponse.json({ success: true, data: parsedData });\n } catch (parseError) {\n console.error(\"Erreur de parsing JSON. Réponse brute:\", generatedContent);\n return NextResponse.json(\n { success: false, error: \"L'IA a généré un format de plan invalide. Réessayez avec un texte plus court.\" },\n { status: 500 }\n );\n }\n\n } catch (error: any) {\n console.error(\"Erreur de communication IA:\", error);\n return NextResponse.json(\n { success: false, error: \"Erreur lors de la communication avec l'IA. \" + (error.message || \"\") },\n { status: 500 }\n );\n }\n}\n"}, "672163afc6df0704fed4e727d7bcfc38b21605c4": {"t": 1785754351.9154027, "n": 5778, "h": "666749a5eb0da105", "c": "import { NextRequest, NextResponse } from 'next/server';\nimport axios from 'axios';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const { text, locale = 'en', explanationLevel = 'detailed', additionalSpecs = '' } = await request.json();\n\n if (!text || text.trim().length < 10) {\n return NextResponse.json(\n { success: false, error: 'Texte fourni trop court ou invalide.' },\n { status: 400 }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return NextResponse.json(\n { success: false, error: 'Configuration API manquante' },\n { status: 500 }\n );\n }\n\n const languageInstruction = locale === 'fr'\n ? 'Tu DOIS répondre en FRANÇAIS.'\n : 'You MUST respond in ENGLISH.';\n\n const levelInstruction = explanationLevel === 'simple'\n ? (locale === 'fr'\n ? 'Crée un plan CONCIS et SYNTHÉTIQUE avec 2-4 modules maximum et 3-5 topics par module. Reste bref et va à l\\'essentiel. IMPORTANT: L\\'étudiant est DÉBUTANT, explique chaque concept de manière accessible.'\n : 'Create a CONCISE and SYNTHETIC plan with 2-4 modules maximum and 3-5 topics per module. Keep it brief and to the point. IMPORTANT: The student is a BEGINNER, explain each concept in an accessible way.')\n : (locale === 'fr'\n ? 'Crée un plan TRÈS DÉTAILLÉ ET EXHAUSTIF avec autant de modules et topics que nécessaire (pas de limite - 5, 10, 15+ modules si le contenu est riche). Sois GRANULAIRE et décompose en profondeur. IMPORTANT: L\\'étudiant est DÉBUTANT, décompose chaque concept complexe en petites notions simples et progressives.'\n : 'Create a VERY DETAILED AND EXHAUSTIVE plan with as many modules and topics as necessary (no limit - 5, 10, 15+ modules if content is rich). Be GRANULAR and break down in depth. IMPORTANT: The student is a BEGINNER, break down each complex concept into small simple and progressive notions.');\n\n const additionalContext = additionalSpecs.trim()\n ? (locale === 'fr'\n ? `\\n\\nINSTRUCTIONS SUPPLÉMENTAIRES DE L'UTILISATEUR:\\n${additionalSpecs}`\n : `\\n\\nADDITIONAL USER INSTRUCTIONS:\\n${additionalSpecs}`)\n : '';\n\n const systemPrompt = `You are an expert STEM teacher. The student provides raw notes or text.\nYour role is to generate a STUDY PLAN structured in main Modules, each containing several specific sub-chapters (topics).\nThe goal is for the student to check exactly the small concepts they want to review.\n\n${languageInstruction}\n${levelInstruction}\n${additionalContext}\n\nABSOLUTE RULES:\n1. You MUST respond ONLY with a valid and extremely compact JSON object (no unnecessary spaces).\n2. Do NOT use markdown, no \\`\\`\\`json. Return direct JSON text.\n3. Analyze the provided content and adapt the number of modules and topics based on the explanation level chosen.\n4. Be GRANULAR: break down complex concepts into small precise and actionable notions.\n\nSTRICTLY expected format:\n{\"courseTitle\":\"Catchy title\",\"summary\":\"Very brief summary\",\"modules\":[{\"id\":1,\"title\":\"Main Theme (e.g., Kinematics)\",\"estimatedMinutes\":20,\"topics\":[{\"id\":\"1-1\",\"title\":\"Notion 1 (e.g., Speed and Acceleration)\"},{\"id\":\"1-2\",\"title\":\"Notion 2 (e.g., Free Fall)\"}]}]}\n\nAdapt the number of modules and topics to the RICHNESS and COMPLEXITY of the provided content AND the explanation level requested.`;\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await axios.post(apiUrl, {\n model: 'gpt-5.4-nano',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: `Text to analyze and transform into detailed plan:\\n\"${text}\"\\n\\nGenerate the JSON now:` }\n ],\n temperature: 0.6,\n max_tokens: 1500,\n stream: false\n }, {\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n 'Accept-Encoding': 'identity' // Demander une réponse NON compressée\n },\n timeout: 120000,\n decompress: false, // Ne pas décompresser automatiquement\n responseType: 'text' // Lire comme texte brut\n });\n\n // Parser la réponse texte en JSON\n const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n let generatedContent = data.choices?.[0]?.message?.content || \"\";\n\n // Nettoyage Markdown\n let cleanJson = generatedContent.trim();\n if (cleanJson.startsWith('```json')) cleanJson = cleanJson.replace(/^```json/, '');\n else if (cleanJson.startsWith('```')) cleanJson = cleanJson.replace(/^```/, '');\n if (cleanJson.endsWith('```')) cleanJson = cleanJson.replace(/```$/, '');\n cleanJson = cleanJson.trim();\n\n // Auto-réparation basique si le json coupe net à la fin\n if (!cleanJson.endsWith('}') && cleanJson.includes('\"modules\"')) {\n if (!cleanJson.endsWith(']')) cleanJson += ']}';\n else cleanJson += '}';\n }\n\n try {\n const parsedData = JSON.parse(cleanJson);\n return NextResponse.json({ success: true, data: parsedData });\n } catch (parseError) {\n console.error(\"Erreur de parsing JSON. Réponse brute:\", generatedContent);\n return NextResponse.json(\n { success: false, error: \"L'IA a généré un format de plan invalide. Réessayez avec un texte plus court.\" },\n { status: 500 }\n );\n }\n\n } catch (error: any) {\n console.error(\"Erreur de communication IA:\", error);\n return NextResponse.json(\n { success: false, error: \"Erreur lors de la communication avec l'IA. \" + (error.message || \"\") },\n { status: 500 }\n );\n }\n}\n"}, "4f4e94fbd44368da5846157895a31d3069790129": {"t": 1785754353.2572157, "n": 5779, "h": "b50062c7dac5e707", "c": "import { NextRequest, NextResponse } from 'next/server';\nimport axios from 'axios';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const { text, locale = 'en', explanationLevel = 'detailed', additionalSpecs = '' } = await request.json();\n\n if (!text || text.trim().length < 10) {\n return NextResponse.json(\n { success: false, error: 'Texte fourni trop court ou invalide.' },\n { status: 400 }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return NextResponse.json(\n { success: false, error: 'Configuration API manquante' },\n { status: 500 }\n );\n }\n\n const languageInstruction = locale === 'fr'\n ? 'Tu DOIS répondre en FRANÇAIS.'\n : 'You MUST respond in ENGLISH.';\n\n const levelInstruction = explanationLevel === 'simple'\n ? (locale === 'fr'\n ? 'Crée un plan CONCIS et SYNTHÉTIQUE avec 2-4 modules maximum et 3-5 topics par module. Reste bref et va à l\\'essentiel. IMPORTANT: L\\'étudiant est DÉBUTANT, explique chaque concept de manière accessible.'\n : 'Create a CONCISE and SYNTHETIC plan with 2-4 modules maximum and 3-5 topics per module. Keep it brief and to the point. IMPORTANT: The student is a BEGINNER, explain each concept in an accessible way.')\n : (locale === 'fr'\n ? 'Crée un plan TRÈS DÉTAILLÉ ET EXHAUSTIF avec autant de modules et topics que nécessaire (pas de limite - 5, 10, 15+ modules si le contenu est riche). Sois GRANULAIRE et décompose en profondeur. IMPORTANT: L\\'étudiant est DÉBUTANT, décompose chaque concept complexe en petites notions simples et progressives.'\n : 'Create a VERY DETAILED AND EXHAUSTIVE plan with as many modules and topics as necessary (no limit - 5, 10, 15+ modules if content is rich). Be GRANULAR and break down in depth. IMPORTANT: The student is a BEGINNER, break down each complex concept into small simple and progressive notions.');\n\n const additionalContext = additionalSpecs.trim()\n ? (locale === 'fr'\n ? `\\n\\nINSTRUCTIONS SUPPLÉMENTAIRES DE L'UTILISATEUR:\\n${additionalSpecs}`\n : `\\n\\nADDITIONAL USER INSTRUCTIONS:\\n${additionalSpecs}`)\n : '';\n\n const systemPrompt = `You are an expert STEM teacher. The student provides raw notes or text.\nYour role is to generate a STUDY PLAN structured in main Modules, each containing several specific sub-chapters (topics).\nThe goal is for the student to check exactly the small concepts they want to review.\n\n${languageInstruction}\n${levelInstruction}\n${additionalContext}\n\nABSOLUTE RULES:\n1. You MUST respond ONLY with a valid and extremely compact JSON object (no unnecessary spaces).\n2. Do NOT use markdown, no \\`\\`\\`json. Return direct JSON text.\n3. Analyze the provided content and adapt the number of modules and topics based on the explanation level chosen.\n4. Be GRANULAR: break down complex concepts into small precise and actionable notions.\n\nSTRICTLY expected format:\n{\"courseTitle\":\"Catchy title\",\"summary\":\"Very brief summary\",\"modules\":[{\"id\":1,\"title\":\"Main Theme (e.g., Kinematics)\",\"estimatedMinutes\":20,\"topics\":[{\"id\":\"1-1\",\"title\":\"Notion 1 (e.g., Speed and Acceleration)\"},{\"id\":\"1-2\",\"title\":\"Notion 2 (e.g., Free Fall)\"}]}]}\n\nAdapt the number of modules and topics to the RICHNESS and COMPLEXITY of the provided content AND the explanation level requested.`;\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await axios.post(apiUrl, {\n model: 'deepseek-chat',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: `Text to analyze and transform into detailed plan:\\n\"${text}\"\\n\\nGenerate the JSON now:` }\n ],\n temperature: 0.6,\n max_tokens: 1500,\n stream: false\n }, {\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n 'Accept-Encoding': 'identity' // Demander une réponse NON compressée\n },\n timeout: 120000,\n decompress: false, // Ne pas décompresser automatiquement\n responseType: 'text' // Lire comme texte brut\n });\n\n // Parser la réponse texte en JSON\n const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n let generatedContent = data.choices?.[0]?.message?.content || \"\";\n\n // Nettoyage Markdown\n let cleanJson = generatedContent.trim();\n if (cleanJson.startsWith('```json')) cleanJson = cleanJson.replace(/^```json/, '');\n else if (cleanJson.startsWith('```')) cleanJson = cleanJson.replace(/^```/, '');\n if (cleanJson.endsWith('```')) cleanJson = cleanJson.replace(/```$/, '');\n cleanJson = cleanJson.trim();\n\n // Auto-réparation basique si le json coupe net à la fin\n if (!cleanJson.endsWith('}') && cleanJson.includes('\"modules\"')) {\n if (!cleanJson.endsWith(']')) cleanJson += ']}';\n else cleanJson += '}';\n }\n\n try {\n const parsedData = JSON.parse(cleanJson);\n return NextResponse.json({ success: true, data: parsedData });\n } catch (parseError) {\n console.error(\"Erreur de parsing JSON. Réponse brute:\", generatedContent);\n return NextResponse.json(\n { success: false, error: \"L'IA a généré un format de plan invalide. Réessayez avec un texte plus court.\" },\n { status: 500 }\n );\n }\n\n } catch (error: any) {\n console.error(\"Erreur de communication IA:\", error);\n return NextResponse.json(\n { success: false, error: \"Erreur lors de la communication avec l'IA. \" + (error.message || \"\") },\n { status: 500 }\n );\n }\n}\n"}, "8ce5fed6137713d3d0280467fdefc3580d188605": {"t": 1785754354.4957414, "n": 5591, "h": "9283cdbf4a0ff1fa", "c": "import { NextRequest, NextResponse } from 'next/server';\nimport axios from 'axios';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const { text, locale = 'en', explanationLevel = 'detailed', additionalSpecs = '' } = await request.json();\n\n if (!text || text.trim().length < 10) {\n return NextResponse.json(\n { success: false, error: 'Texte fourni trop court ou invalide.' },\n { status: 400 }\n );\n }\n\n const languageInstruction = locale === 'fr'\n ? 'Tu DOIS répondre en FRANÇAIS.'\n : 'You MUST respond in ENGLISH.';\n\n const levelInstruction = explanationLevel === 'simple'\n ? (locale === 'fr'\n ? 'Crée un plan CONCIS et SYNTHÉTIQUE avec 2-4 modules maximum et 3-5 topics par module. Reste bref et va à l\\'essentiel. IMPORTANT: L\\'étudiant est DÉBUTANT, explique chaque concept de manière accessible.'\n : 'Create a CONCISE and SYNTHETIC plan with 2-4 modules maximum and 3-5 topics per module. Keep it brief and to the point. IMPORTANT: The student is a BEGINNER, explain each concept in an accessible way.')\n : (locale === 'fr'\n ? 'Crée un plan TRÈS DÉTAILLÉ ET EXHAUSTIF avec autant de modules et topics que nécessaire (pas de limite - 5, 10, 15+ modules si le contenu est riche). Sois GRANULAIRE et décompose en profondeur. IMPORTANT: L\\'étudiant est DÉBUTANT, décompose chaque concept complexe en petites notions simples et progressives.'\n : 'Create a VERY DETAILED AND EXHAUSTIVE plan with as many modules and topics as necessary (no limit - 5, 10, 15+ modules if content is rich). Be GRANULAR and break down in depth. IMPORTANT: The student is a BEGINNER, break down each complex concept into small simple and progressive notions.');\n\n const additionalContext = additionalSpecs.trim()\n ? (locale === 'fr'\n ? `\\n\\nINSTRUCTIONS SUPPLÉMENTAIRES DE L'UTILISATEUR:\\n${additionalSpecs}`\n : `\\n\\nADDITIONAL USER INSTRUCTIONS:\\n${additionalSpecs}`)\n : '';\n\n const systemPrompt = `You are an expert STEM teacher. The student provides raw notes or text.\nYour role is to generate a STUDY PLAN structured in main Modules, each containing several specific sub-chapters (topics).\nThe goal is for the student to check exactly the small concepts they want to review.\n\n${languageInstruction}\n${levelInstruction}\n${additionalContext}\n\nABSOLUTE RULES:\n1. You MUST respond ONLY with a valid and extremely compact JSON object (no unnecessary spaces).\n2. Do NOT use markdown, no \\`\\`\\`json. Return direct JSON text.\n3. Analyze the provided content and adapt the number of modules and topics based on the explanation level chosen.\n4. Be GRANULAR: break down complex concepts into small precise and actionable notions.\n\nSTRICTLY expected format:\n{\"courseTitle\":\"Catchy title\",\"summary\":\"Very brief summary\",\"modules\":[{\"id\":1,\"title\":\"Main Theme (e.g., Kinematics)\",\"estimatedMinutes\":20,\"topics\":[{\"id\":\"1-1\",\"title\":\"Notion 1 (e.g., Speed and Acceleration)\"},{\"id\":\"1-2\",\"title\":\"Notion 2 (e.g., Free Fall)\"}]}]}\n\nAdapt the number of modules and topics to the RICHNESS and COMPLEXITY of the provided content AND the explanation level requested.`;\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await axios.post(apiUrl, {\n model: 'deepseek-chat',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: `Text to analyze and transform into detailed plan:\\n\"${text}\"\\n\\nGenerate the JSON now:` }\n ],\n temperature: 0.6,\n max_tokens: 1500,\n stream: false\n }, {\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n 'Accept-Encoding': 'identity' // Demander une réponse NON compressée\n },\n timeout: 120000,\n decompress: false, // Ne pas décompresser automatiquement\n responseType: 'text' // Lire comme texte brut\n });\n\n // Parser la réponse texte en JSON\n const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n let generatedContent = data.choices?.[0]?.message?.content || \"\";\n\n // Nettoyage Markdown\n let cleanJson = generatedContent.trim();\n if (cleanJson.startsWith('```json')) cleanJson = cleanJson.replace(/^```json/, '');\n else if (cleanJson.startsWith('```')) cleanJson = cleanJson.replace(/^```/, '');\n if (cleanJson.endsWith('```')) cleanJson = cleanJson.replace(/```$/, '');\n cleanJson = cleanJson.trim();\n\n // Auto-réparation basique si le json coupe net à la fin\n if (!cleanJson.endsWith('}') && cleanJson.includes('\"modules\"')) {\n if (!cleanJson.endsWith(']')) cleanJson += ']}';\n else cleanJson += '}';\n }\n\n try {\n const parsedData = JSON.parse(cleanJson);\n return NextResponse.json({ success: true, data: parsedData });\n } catch (parseError) {\n console.error(\"Erreur de parsing JSON. Réponse brute:\", generatedContent);\n return NextResponse.json(\n { success: false, error: \"L'IA a généré un format de plan invalide. Réessayez avec un texte plus court.\" },\n { status: 500 }\n );\n }\n\n } catch (error: any) {\n console.error(\"Erreur de communication IA:\", error);\n return NextResponse.json(\n { success: false, error: \"Erreur lors de la communication avec l'IA. \" + (error.message || \"\") },\n { status: 500 }\n );\n }\n}\n"}, "f57ec067df84a5429c54b9e7fda6c00684f45378": {"t": 1785754355.8196647, "n": 5671, "h": "90ab9556bca3f713", "c": "import { NextRequest, NextResponse } from 'next/server';\nimport axios from 'axios';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL || 'https://ds2api-tau-woad.vercel.app/v1';\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || 'sk-ds2api-key-1-your-custom-key';\n\nexport async function POST(request: NextRequest) {\n try {\n const { text, locale = 'en', explanationLevel = 'detailed', additionalSpecs = '' } = await request.json();\n\n if (!text || text.trim().length < 10) {\n return NextResponse.json(\n { success: false, error: 'Texte fourni trop court ou invalide.' },\n { status: 400 }\n );\n }\n\n const languageInstruction = locale === 'fr'\n ? 'Tu DOIS répondre en FRANÇAIS.'\n : 'You MUST respond in ENGLISH.';\n\n const levelInstruction = explanationLevel === 'simple'\n ? (locale === 'fr'\n ? 'Crée un plan CONCIS et SYNTHÉTIQUE avec 2-4 modules maximum et 3-5 topics par module. Reste bref et va à l\\'essentiel. IMPORTANT: L\\'étudiant est DÉBUTANT, explique chaque concept de manière accessible.'\n : 'Create a CONCISE and SYNTHETIC plan with 2-4 modules maximum and 3-5 topics per module. Keep it brief and to the point. IMPORTANT: The student is a BEGINNER, explain each concept in an accessible way.')\n : (locale === 'fr'\n ? 'Crée un plan TRÈS DÉTAILLÉ ET EXHAUSTIF avec autant de modules et topics que nécessaire (pas de limite - 5, 10, 15+ modules si le contenu est riche). Sois GRANULAIRE et décompose en profondeur. IMPORTANT: L\\'étudiant est DÉBUTANT, décompose chaque concept complexe en petites notions simples et progressives.'\n : 'Create a VERY DETAILED AND EXHAUSTIVE plan with as many modules and topics as necessary (no limit - 5, 10, 15+ modules if content is rich). Be GRANULAR and break down in depth. IMPORTANT: The student is a BEGINNER, break down each complex concept into small simple and progressive notions.');\n\n const additionalContext = additionalSpecs.trim()\n ? (locale === 'fr'\n ? `\\n\\nINSTRUCTIONS SUPPLÉMENTAIRES DE L'UTILISATEUR:\\n${additionalSpecs}`\n : `\\n\\nADDITIONAL USER INSTRUCTIONS:\\n${additionalSpecs}`)\n : '';\n\n const systemPrompt = `You are an expert STEM teacher. The student provides raw notes or text.\nYour role is to generate a STUDY PLAN structured in main Modules, each containing several specific sub-chapters (topics).\nThe goal is for the student to check exactly the small concepts they want to review.\n\n${languageInstruction}\n${levelInstruction}\n${additionalContext}\n\nABSOLUTE RULES:\n1. You MUST respond ONLY with a valid and extremely compact JSON object (no unnecessary spaces).\n2. Do NOT use markdown, no \\`\\`\\`json. Return direct JSON text.\n3. Analyze the provided content and adapt the number of modules and topics based on the explanation level chosen.\n4. Be GRANULAR: break down complex concepts into small precise and actionable notions.\n\nSTRICTLY expected format:\n{\"courseTitle\":\"Catchy title\",\"summary\":\"Very brief summary\",\"modules\":[{\"id\":1,\"title\":\"Main Theme (e.g., Kinematics)\",\"estimatedMinutes\":20,\"topics\":[{\"id\":\"1-1\",\"title\":\"Notion 1 (e.g., Speed and Acceleration)\"},{\"id\":\"1-2\",\"title\":\"Notion 2 (e.g., Free Fall)\"}]}]}\n\nAdapt the number of modules and topics to the RICHNESS and COMPLEXITY of the provided content AND the explanation level requested.`;\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await axios.post(apiUrl, {\n model: 'deepseek-chat',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: `Text to analyze and transform into detailed plan:\\n\"${text}\"\\n\\nGenerate the JSON now:` }\n ],\n temperature: 0.6,\n max_tokens: 1500,\n stream: false\n }, {\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n 'Accept-Encoding': 'identity' // Demander une réponse NON compressée\n },\n timeout: 120000,\n decompress: false, // Ne pas décompresser automatiquement\n responseType: 'text' // Lire comme texte brut\n });\n\n // Parser la réponse texte en JSON\n const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n let generatedContent = data.choices?.[0]?.message?.content || \"\";\n\n // Nettoyage Markdown\n let cleanJson = generatedContent.trim();\n if (cleanJson.startsWith('```json')) cleanJson = cleanJson.replace(/^```json/, '');\n else if (cleanJson.startsWith('```')) cleanJson = cleanJson.replace(/^```/, '');\n if (cleanJson.endsWith('```')) cleanJson = cleanJson.replace(/```$/, '');\n cleanJson = cleanJson.trim();\n\n // Auto-réparation basique si le json coupe net à la fin\n if (!cleanJson.endsWith('}') && cleanJson.includes('\"modules\"')) {\n if (!cleanJson.endsWith(']')) cleanJson += ']}';\n else cleanJson += '}';\n }\n\n try {\n const parsedData = JSON.parse(cleanJson);\n return NextResponse.json({ success: true, data: parsedData });\n } catch (parseError) {\n console.error(\"Erreur de parsing JSON. Réponse brute:\", generatedContent);\n return NextResponse.json(\n { success: false, error: \"L'IA a généré un format de plan invalide. Réessayez avec un texte plus court.\" },\n { status: 500 }\n );\n }\n\n } catch (error: any) {\n console.error(\"Erreur de communication IA:\", error);\n return NextResponse.json(\n { success: false, error: \"Erreur lors de la communication avec l'IA. \" + (error.message || \"\") },\n { status: 500 }\n );\n }\n}\n"}, "ee5939d7b46f86ef68eec121f4b8c33dfd1a4470": {"t": 1786068279.2648032, "n": 5778, "h": "666749a5eb0da105", "c": "import { NextRequest, NextResponse } from 'next/server';\nimport axios from 'axios';\n\nconst DEEPSEEK_API_URL = process.env.DEEPSEEK_API_URL;\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\n\nexport async function POST(request: NextRequest) {\n try {\n const { text, locale = 'en', explanationLevel = 'detailed', additionalSpecs = '' } = await request.json();\n\n if (!text || text.trim().length < 10) {\n return NextResponse.json(\n { success: false, error: 'Texte fourni trop court ou invalide.' },\n { status: 400 }\n );\n }\n\n if (!DEEPSEEK_API_URL || !DEEPSEEK_API_KEY) {\n return NextResponse.json(\n { success: false, error: 'Configuration API manquante' },\n { status: 500 }\n );\n }\n\n const languageInstruction = locale === 'fr'\n ? 'Tu DOIS répondre en FRANÇAIS.'\n : 'You MUST respond in ENGLISH.';\n\n const levelInstruction = explanationLevel === 'simple'\n ? (locale === 'fr'\n ? 'Crée un plan CONCIS et SYNTHÉTIQUE avec 2-4 modules maximum et 3-5 topics par module. Reste bref et va à l\\'essentiel. IMPORTANT: L\\'étudiant est DÉBUTANT, explique chaque concept de manière accessible.'\n : 'Create a CONCISE and SYNTHETIC plan with 2-4 modules maximum and 3-5 topics per module. Keep it brief and to the point. IMPORTANT: The student is a BEGINNER, explain each concept in an accessible way.')\n : (locale === 'fr'\n ? 'Crée un plan TRÈS DÉTAILLÉ ET EXHAUSTIF avec autant de modules et topics que nécessaire (pas de limite - 5, 10, 15+ modules si le contenu est riche). Sois GRANULAIRE et décompose en profondeur. IMPORTANT: L\\'étudiant est DÉBUTANT, décompose chaque concept complexe en petites notions simples et progressives.'\n : 'Create a VERY DETAILED AND EXHAUSTIVE plan with as many modules and topics as necessary (no limit - 5, 10, 15+ modules if content is rich). Be GRANULAR and break down in depth. IMPORTANT: The student is a BEGINNER, break down each complex concept into small simple and progressive notions.');\n\n const additionalContext = additionalSpecs.trim()\n ? (locale === 'fr'\n ? `\\n\\nINSTRUCTIONS SUPPLÉMENTAIRES DE L'UTILISATEUR:\\n${additionalSpecs}`\n : `\\n\\nADDITIONAL USER INSTRUCTIONS:\\n${additionalSpecs}`)\n : '';\n\n const systemPrompt = `You are an expert STEM teacher. The student provides raw notes or text.\nYour role is to generate a STUDY PLAN structured in main Modules, each containing several specific sub-chapters (topics).\nThe goal is for the student to check exactly the small concepts they want to review.\n\n${languageInstruction}\n${levelInstruction}\n${additionalContext}\n\nABSOLUTE RULES:\n1. You MUST respond ONLY with a valid and extremely compact JSON object (no unnecessary spaces).\n2. Do NOT use markdown, no \\`\\`\\`json. Return direct JSON text.\n3. Analyze the provided content and adapt the number of modules and topics based on the explanation level chosen.\n4. Be GRANULAR: break down complex concepts into small precise and actionable notions.\n\nSTRICTLY expected format:\n{\"courseTitle\":\"Catchy title\",\"summary\":\"Very brief summary\",\"modules\":[{\"id\":1,\"title\":\"Main Theme (e.g., Kinematics)\",\"estimatedMinutes\":20,\"topics\":[{\"id\":\"1-1\",\"title\":\"Notion 1 (e.g., Speed and Acceleration)\"},{\"id\":\"1-2\",\"title\":\"Notion 2 (e.g., Free Fall)\"}]}]}\n\nAdapt the number of modules and topics to the RICHNESS and COMPLEXITY of the provided content AND the explanation level requested.`;\n\n const apiUrl = DEEPSEEK_API_URL.endsWith('/chat/completions')\n ? DEEPSEEK_API_URL\n : `${DEEPSEEK_API_URL}/chat/completions`;\n\n const response = await axios.post(apiUrl, {\n model: 'gpt-5.4-nano',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: `Text to analyze and transform into detailed plan:\\n\"${text}\"\\n\\nGenerate the JSON now:` }\n ],\n temperature: 0.6,\n max_tokens: 1500,\n stream: false\n }, {\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`,\n 'Accept-Encoding': 'identity' // Demander une réponse NON compressée\n },\n timeout: 120000,\n decompress: false, // Ne pas décompresser automatiquement\n responseType: 'text' // Lire comme texte brut\n });\n\n // Parser la réponse texte en JSON\n const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;\n let generatedContent = data.choices?.[0]?.message?.content || \"\";\n\n // Nettoyage Markdown\n let cleanJson = generatedContent.trim();\n if (cleanJson.startsWith('```json')) cleanJson = cleanJson.replace(/^```json/, '');\n else if (cleanJson.startsWith('```')) cleanJson = cleanJson.replace(/^```/, '');\n if (cleanJson.endsWith('```')) cleanJson = cleanJson.replace(/```$/, '');\n cleanJson = cleanJson.trim();\n\n // Auto-réparation basique si le json coupe net à la fin\n if (!cleanJson.endsWith('}') && cleanJson.includes('\"modules\"')) {\n if (!cleanJson.endsWith(']')) cleanJson += ']}';\n else cleanJson += '}';\n }\n\n try {\n const parsedData = JSON.parse(cleanJson);\n return NextResponse.json({ success: true, data: parsedData });\n } catch (parseError) {\n console.error(\"Erreur de parsing JSON. Réponse brute:\", generatedContent);\n return NextResponse.json(\n { success: false, error: \"L'IA a généré un format de plan invalide. Réessayez avec un texte plus court.\" },\n { status: 500 }\n );\n }\n\n } catch (error: any) {\n console.error(\"Erreur de communication IA:\", error);\n return NextResponse.json(\n { success: false, error: \"Erreur lors de la communication avec l'IA. \" + (error.message || \"\") },\n { status: 500 }\n );\n }\n}\n"}}}} |