chore: configure environment variables, update gitignore, and update build assets
This commit is contained in:
parent
e1b2692ce8
commit
227a03f6f8
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.superpowers/
|
||||
.env.local
|
||||
File diff suppressed because one or more lines are too long
1
dist/assets/index-DijV1x30.css
vendored
1
dist/assets/index-DijV1x30.css
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/index-hUIr4MzD.css
vendored
Normal file
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
4
dist/index.html
vendored
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user