- ChatbotV2.vue : Vue island, thread chat (input + messages bot/user), persistance sessionStorage, bandeau beta '120 fiches AEP, RAG-PE bientot', gestion erreurs 429/502/504 ; pas de streaming ni markdown V1 - /api/chatbot.ts : endpoint Astro server proxy POST vers CHATBOT_UPSTREAM (default https://aep.trans-former.fr/api/chatbot), timeout 25s, body { question, history } -> upstream classique chatbot AEP Mistral Small - astro.config.mjs : output 'server' + adapter @astrojs/node standalone (Astro 6 a supprime mode hybrid ; on opt-in prerender sur les pages) - Toutes les pages publiques (index, manifeste, manifeste/commander, a-propos, mentions-legales) ont 'export const prerender = true' - ColCentre.astro : remplace ChatbotPlaceholder par ChatbotV2 dans le tab - .env.example : ajoute CHATBOT_UPSTREAM (V1.5 = switch LightRAG-PE 1 ligne) Decision V1 : endpoint AEP /api/chatbot (classique, repond bien) au lieu de /api/chatbot-v2 qui retourne v2_ready=false ('base vectorielle en cours'). Bandeau beta reste valide ; switch v2 quand ready cote AEP via env var. Note PC8 deploy : Coolify doit booter avec 'node ./dist/server/entry.mjs' (SSR Node standalone) au lieu de servir dist/client/ static. Test end-to-end OK : SSR boot port 4399 + curl POST /api/chatbot -> reponse_texte 800+ chars de l'AEP backend.
66 lines
1.9 KiB
TypeScript
66 lines
1.9 KiB
TypeScript
// PC7 — Endpoint proxy POST /api/chatbot
|
|
// Forward la question vers CHATBOT_UPSTREAM (default : aep.trans-former.fr/api/chatbot).
|
|
// Tunnel CORS (l'upstream ne sert pas de header CORS pour trans-former.fr) + timeout 25s.
|
|
|
|
import type { APIRoute } from 'astro'
|
|
|
|
export const prerender = false
|
|
|
|
const UPSTREAM =
|
|
import.meta.env.CHATBOT_UPSTREAM || 'https://aep.trans-former.fr/api/chatbot'
|
|
const TIMEOUT_MS = 25_000
|
|
|
|
const jsonResponse = (status: number, payload: unknown) =>
|
|
new Response(JSON.stringify(payload), {
|
|
status,
|
|
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
|
})
|
|
|
|
export const POST: APIRoute = async ({ request }) => {
|
|
let body: { question?: string; q?: string; history?: unknown }
|
|
|
|
try {
|
|
body = await request.json()
|
|
} catch {
|
|
return jsonResponse(400, { error: 'invalid_json' })
|
|
}
|
|
|
|
const question = (body.question || body.q || '').toString().trim()
|
|
if (!question) {
|
|
return jsonResponse(400, { error: 'missing_question' })
|
|
}
|
|
|
|
const ctrl = new AbortController()
|
|
const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS)
|
|
|
|
try {
|
|
const upstream = await fetch(UPSTREAM, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
question,
|
|
history: Array.isArray(body.history) ? body.history : [],
|
|
}),
|
|
signal: ctrl.signal,
|
|
})
|
|
clearTimeout(timer)
|
|
|
|
const text = await upstream.text()
|
|
const contentType =
|
|
upstream.headers.get('content-type') || 'application/json; charset=utf-8'
|
|
|
|
return new Response(text, {
|
|
status: upstream.status,
|
|
headers: { 'Content-Type': contentType },
|
|
})
|
|
} catch (e) {
|
|
clearTimeout(timer)
|
|
const err = e as Error
|
|
const aborted = err.name === 'AbortError'
|
|
return jsonResponse(aborted ? 504 : 502, {
|
|
error: aborted ? 'upstream_timeout' : 'upstream_failed',
|
|
detail: err.message || 'unknown',
|
|
})
|
|
}
|
|
}
|