feat(media): Phase 8.G noeuds-ecoles + popup RAG info + lien Bonpote + migration Nebius

- CartePensees: noeuds ecole visibles (cercles proportionnels count auteurs, cliquables, emit select-ecole)
- CartePensees: collision D3 ajustee pour repulsion auteurs autour des noeuds ecole
- FicheEcole: nouveau composant modal (liste auteurs ingeres/non-ingeres, interroger RAG)
- media: header lien Bonpote V2 cliquable + bouton i info RAG
- media: popup FRACAS (description RAG, 662 dimensions, 3 couches, localStorage 1ere visite)
- media: FicheEcole branchee (select-ecole, select-auteur-from-ecole, interroger-ecole)
- ChatbotPensees: suppression mention corpusCount hardcoded (double source de verite)
- chatbot, chatbot-v2, chatbot-reseaux, chatbot-taff: migration Mistral -> Nebius DeepSeek-V3.2
- nuxt.config: ajout nebiusApiKey runtime config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jules Neny
2026-05-14 05:56:09 +02:00
parent 46f57ae5fe
commit 40b406bd41
9 changed files with 286 additions and 41 deletions

View File

@@ -82,18 +82,18 @@ export default defineEventHandler(async (event) => {
const systemPrompt = SYSTEM_PROMPT.replace('{{STRUCTURES_JSON}}', JSON.stringify(context, null, 0))
const mistralApiKey = config.mistralApiKey as string
if (!mistralApiKey) throw createError({ statusCode: 500, message: 'Clé API Mistral manquante.' })
const nebiusApiKey = config.nebiusApiKey as string
if (!nebiusApiKey) throw createError({ statusCode: 500, message: 'Clé API Nebius manquante.' })
let mistralRaw: string
try {
const res = await $fetch<{ choices: { message: { content: string } }[] }>(
'https://api.mistral.ai/v1/chat/completions',
'https://api.tokenfactory.nebius.com/v1/chat/completions',
{
method: 'POST',
headers: { Authorization: `Bearer ${mistralApiKey}`, 'Content-Type': 'application/json' },
headers: { Authorization: `Bearer ${nebiusApiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'mistral-small-latest',
model: 'deepseek-ai/DeepSeek-V3.2',
temperature: 0.3,
max_tokens: 700,
response_format: { type: 'json_object' },

View File

@@ -91,20 +91,20 @@ export default defineEventHandler(async (event) => {
const systemPrompt = SYSTEM_PROMPT.replace('{{PLATEFORMES_JSON}}', JSON.stringify(context, null, 0))
const mistralApiKey = config.mistralApiKey as string
if (!mistralApiKey) {
throw createError({ statusCode: 500, statusMessage: 'Clé API Mistral manquante.' })
const nebiusApiKey = config.nebiusApiKey as string
if (!nebiusApiKey) {
throw createError({ statusCode: 500, statusMessage: 'Clé API Nebius manquante.' })
}
let mistralRaw: string
try {
const res = await $fetch<{ choices: { message: { content: string } }[] }>(
'https://api.mistral.ai/v1/chat/completions',
'https://api.tokenfactory.nebius.com/v1/chat/completions',
{
method: 'POST',
headers: { Authorization: `Bearer ${mistralApiKey}`, 'Content-Type': 'application/json' },
headers: { Authorization: `Bearer ${nebiusApiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'mistral-small-latest',
model: 'deepseek-ai/DeepSeek-V3.2',
temperature: 0.3,
max_tokens: 700,
response_format: { type: 'json_object' },

View File

@@ -145,19 +145,22 @@ export default defineEventHandler(async (event) => {
const systemPrompt = SYSTEM_PROMPT_V2.replace('{{CONTEXTE_RAG}}', contextStr)
// 7. Mistral Small - génération réponse
// 7. Nebius DeepSeek-V3.2 - génération réponse
const nebiusApiKey = config.nebiusApiKey as string
if (!nebiusApiKey) throw createError({ statusCode: 500, statusMessage: 'Clé API Nebius manquante.' })
let mistralRaw: string
try {
const mistralRes = await $fetch<{
const nebiusRes = await $fetch<{
choices: { message: { content: string } }[]
}>('https://api.mistral.ai/v1/chat/completions', {
}>('https://api.tokenfactory.nebius.com/v1/chat/completions', {
method: 'POST',
headers: {
Authorization: `Bearer ${mistralApiKey}`,
Authorization: `Bearer ${nebiusApiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'mistral-small-latest',
model: 'deepseek-ai/DeepSeek-V3.2',
temperature: 0.3,
max_tokens: 600,
response_format: { type: 'json_object' },
@@ -167,10 +170,10 @@ export default defineEventHandler(async (event) => {
]
})
})
mistralRaw = mistralRes.choices?.[0]?.message?.content ?? '{}'
mistralRaw = nebiusRes.choices?.[0]?.message?.content ?? '{}'
} catch (e: any) {
console.error('[chatbot-v2] Erreur Mistral Small :', e?.message ?? e)
throw createError({ statusCode: 502, statusMessage: 'Erreur appel Mistral Small.' })
console.error('[chatbot-v2] Erreur Nebius DeepSeek :', e?.message ?? e)
throw createError({ statusCode: 502, statusMessage: 'Erreur appel Nebius DeepSeek.' })
}
// 8. Parse JSON

View File

@@ -247,13 +247,13 @@ export default defineEventHandler(async (event) => {
JSON.stringify(fichesContext, null, 0),
)
// 6. Appel Mistral Small
const mistralApiKey = config.mistralApiKey as string
// 6. Appel Nebius DeepSeek-V3.2
const nebiusApiKey = config.nebiusApiKey as string
if (!mistralApiKey) {
if (!nebiusApiKey) {
throw createError({
statusCode: 500,
statusMessage: 'Clé API Mistral manquante.',
statusMessage: 'Clé API Nebius manquante.',
})
}
@@ -262,17 +262,17 @@ export default defineEventHandler(async (event) => {
let tokensOut = 0
try {
const mistralRes = await $fetch<{
const nebiusRes = await $fetch<{
choices: { message: { content: string } }[]
usage?: { prompt_tokens: number; completion_tokens: number }
}>('https://api.mistral.ai/v1/chat/completions', {
}>('https://api.tokenfactory.nebius.com/v1/chat/completions', {
method: 'POST',
headers: {
Authorization: `Bearer ${mistralApiKey}`,
Authorization: `Bearer ${nebiusApiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'mistral-small-latest',
model: 'deepseek-ai/DeepSeek-V3.2',
temperature: 0.3,
max_tokens: 600,
response_format: { type: 'json_object' },
@@ -283,11 +283,11 @@ export default defineEventHandler(async (event) => {
}),
})
mistralRaw = mistralRes.choices?.[0]?.message?.content ?? '{}'
tokensIn = mistralRes.usage?.prompt_tokens ?? 0
tokensOut = mistralRes.usage?.completion_tokens ?? 0
mistralRaw = nebiusRes.choices?.[0]?.message?.content ?? '{}'
tokensIn = nebiusRes.usage?.prompt_tokens ?? 0
tokensOut = nebiusRes.usage?.completion_tokens ?? 0
} catch (e: any) {
console.error('[chatbot] Erreur Mistral Small:', e?.message ?? e)
console.error('[chatbot] Erreur Nebius DeepSeek:', e?.message ?? e)
throw createError({
statusCode: 502,
statusMessage: 'Erreur appel IA — réessaie dans quelques instants.',