chore: configure environment variables, update gitignore, and update build assets

This commit is contained in:
sebvtl728 2026-06-21 18:03:31 +02:00
parent e1b2692ce8
commit 227a03f6f8
7 changed files with 113 additions and 116 deletions

1
.env.ocal Normal file
View File

@ -0,0 +1 @@
VITE_MISTRAL_API= 1sMUeXgiRmhYUAKDk6mw9pWNaijtbwcu

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.superpowers/
.env.local

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/index-hUIr4MzD.css vendored Normal file

File diff suppressed because one or more lines are too long

4
dist/index.html vendored
View File

@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bilans de compétences</title>
<script type="module" crossorigin src="/assets/index-B2vp_x6J.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DijV1x30.css">
<script type="module" crossorigin src="/assets/index-CCT8mUTJ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-hUIr4MzD.css">
</head>
<body>
<div id="root"></div>

View File

@ -30,7 +30,8 @@ const LEVEL_LABEL = {
ac: "Acquis",
ma: "Maîtrisé",
};
const FALLBACK_MODELS = ["gpt-4o-mini", "gpt-4o", "gpt-4.1", "gpt-3.5-turbo"];
const MISTRAL_API_KEY = import.meta.env.VITE_MISTRAL_API_KEY || "";
const FALLBACK_MODELS = ["mistral-small-latest", "mistral-medium-latest", "mistral-large-latest"];
const titles = {
CP1: { h1: "Bilan-CP1", h2: "Réaliser des illustrations" },
@ -318,8 +319,7 @@ const Bilan = () => {
const [aiNotes, setAiNotes] = useState("");
const [aiTone, setAiTone] = useState("neutre");
const [aiPrepend, setAiPrepend] = useState(false);
const [openaiKey, setOpenaiKey] = useState("");
const [openaiModel, setOpenaiModel] = useState("gpt-4o-mini");
const [mistralModel, setMistralModel] = useState("mistral-small-latest");
const [observation, setObservation] = useState("");
const [toasts, setToasts] = useState([]);
const [isAiLoading, setIsAiLoading] = useState(false);
@ -585,9 +585,9 @@ const Bilan = () => {
};
}, [aiTone, firstName, items, lastName]);
const generateAISummaryOpenAI = useCallback(async () => {
if (!openaiKey.trim()) {
showStatus("Renseigne ta clé OpenAI (champ sk-...).", "error");
const generateAISummaryMistral = useCallback(async () => {
if (!MISTRAL_API_KEY) {
showStatus("Clé Mistral introuvable. Vérifie ton fichier .env.local (VITE_MISTRAL_API_KEY).", "error");
return;
}
@ -601,14 +601,14 @@ const Bilan = () => {
const notesFormateur = aiNotes.trim();
setIsAiLoading(true);
showStatus("Génération du résumé via OpenAI…", "info", true);
showStatus("Génération du résumé via Mistral…", "info", true);
const callOpenAI = async (model) => {
const response = await fetch("https://api.openai.com/v1/chat/completions", {
const callMistral = async (model) => {
const response = await fetch("https://api.mistral.ai/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${openaiKey.trim()}`,
Authorization: `Bearer ${MISTRAL_API_KEY}`,
},
body: JSON.stringify({
model,
@ -616,11 +616,9 @@ const Bilan = () => {
messages: [
{
role: "system",
content: `
Tu es ingénieur pédagogique et formateur en développement web.
À partir des données (critères et niveaux), du bilan automatique affiché et des notes éventuelles, rédige une synthèse finale d'environ 8 à 10 phrases.
Contraintes : valoriser les réussites, évoquer les difficultés, reformuler les notes formateur, varier le vocabulaire et les connecteurs, adopter un ton professionnel bienveillant.
`,
content: `Tu es ingénieur pédagogique spécialisé dans la formation CDUI (Titre Professionnel Concepteur Développeur d'Interfaces Utilisateur, niveau 6). Tu maîtrises parfaitement les référentiels de compétences du titre, les CP1 à CP9, et les exigences de France Compétences.
À partir des données de l'évaluation (critères et niveaux d'acquisition), du bilan automatique et des notes du formateur, rédige une synthèse pédagogique finale de 8 à 10 phrases.
Contraintes : valoriser les réussites de l'apprenant, évoquer les axes de progression avec bienveillance, reformuler et enrichir les notes du formateur, varier le vocabulaire et les connecteurs, adopter un ton professionnel et encourageant, en lien avec les attendus du titre CDUI.`,
},
{
role: "user",
@ -644,18 +642,18 @@ ${notesFormateur}`,
const tried = new Set();
const attemptOrder = [
openaiModel,
...FALLBACK_MODELS.filter((model) => model !== openaiModel),
mistralModel,
...FALLBACK_MODELS.filter((model) => model !== mistralModel),
];
try {
let content = "";
let usedModel = openaiModel;
let usedModel = mistralModel;
for (const model of attemptOrder) {
if (tried.has(model)) continue;
tried.add(model);
try {
const data = await callOpenAI(model);
const data = await callMistral(model);
content =
data?.choices?.[0]?.message?.content ||
data?.choices?.[0]?.delta?.content ||
@ -674,11 +672,11 @@ ${notesFormateur}`,
}
insertAISummaryIntoObservation(
`## RESUME FORMATEUR\n----------------------------\n${content.trim()}`
`## RÉSUMÉ IA (CDUI)\n----------------------------\n${content.trim()}`
);
showStatus(`Résumé OpenAI inséré (modèle : ${usedModel}).`, "success");
showStatus(`Résumé Mistral inséré (modèle : ${usedModel}).`, "success");
} catch (error) {
showStatus(`Erreur OpenAI : ${error.message || error}`, "error");
showStatus(`Erreur Mistral : ${error.message || error}`, "error");
} finally {
dismissProgressToasts();
setIsAiLoading(false);
@ -688,9 +686,8 @@ ${notesFormateur}`,
buildPayloadForAI,
dismissProgressToasts,
insertAISummaryIntoObservation,
mistralModel,
observation,
openaiKey,
openaiModel,
showStatus,
]);
@ -963,7 +960,7 @@ ${notesFormateur}`,
<button
type="button"
className="btn-topbar-primary"
onClick={generateAISummaryOpenAI}
onClick={generateAISummaryMistral}
disabled={isAiLoading}
>
Résumer avec l'IA
@ -1152,27 +1149,17 @@ ${notesFormateur}`,
className="api-toggle-btn"
onClick={() => setShowApiConfig(!showApiConfig)}
>
{showApiConfig ? "▲ Masquer les paramètres API" : "⚙️ Configuration API OpenAI (Optionnelle)"}
{showApiConfig ? "▲ Masquer les paramètres IA" : "⚙️ Paramètres Mistral IA"}
</button>
{showApiConfig && (
<div className="api-fields-grid">
<div className="input-group">
<label htmlFor="openaiKey">Clé API OpenAI</label>
<input
id="openaiKey"
type="password"
placeholder="sk-..."
value={openaiKey}
onChange={(event) => setOpenaiKey(event.target.value)}
/>
</div>
<div className="input-group">
<label htmlFor="openaiModel">Modèle</label>
<label htmlFor="mistralModel">Modèle Mistral</label>
<select
id="openaiModel"
value={openaiModel}
onChange={(event) => setOpenaiModel(event.target.value)}
id="mistralModel"
value={mistralModel}
onChange={(event) => setMistralModel(event.target.value)}
>
{FALLBACK_MODELS.map((model) => (
<option key={model} value={model}>
@ -1181,6 +1168,15 @@ ${notesFormateur}`,
))}
</select>
</div>
<div className="input-group">
<label>Clé API</label>
<input
type="text"
readOnly
value={MISTRAL_API_KEY ? "••••••••••••••••" : "Non configurée (.env.local)"}
style={{ color: MISTRAL_API_KEY ? "var(--text-success, #16a34a)" : "var(--text-error, #dc2626)", cursor: "default" }}
/>
</div>
</div>
)}
</div>