Skip to content
Extraits de code Groupes Projets
Valider a53c0a7b rédigé par zakariaeyahya's avatar zakariaeyahya
Parcourir les fichiers

add first version of details level

parent f029faaf
Branches
Étiquettes
1 requête de fusion!95Resolve IA-439 "Feauture/"
import { GATEWAY_API_URL } from 'src/config-global';
const detailLevelPrefix = `http://localhost:7084/api/gestion-modeles/detail-level`;
export const detailLevelEndpoints = {
get: (versionId: string) => `${detailLevelPrefix}/${versionId}`,
update: (versionId: string) => `${detailLevelPrefix}/${versionId}`,
create: (versionId: string) => `${detailLevelPrefix}/${versionId}`,
delete: (versionId: string) => `${detailLevelPrefix}/${versionId}`,
reset: (versionId: string) => `${detailLevelPrefix}/${versionId}/reset`,
temperatureMapping: `${detailLevelPrefix}/temperature-mapping`,
calculateTemperature: `${detailLevelPrefix}/calculate-temperature`,
};
\ No newline at end of file
export interface DetailLevelConfigDTO {
id: number;
versionId: number;
detailLevel: number;
calculatedTemperature: number;
conversionMethod: string;
createdAt: string;
updatedAt: string;
createdBy?: string;
lastModifiedBy?: string;
}
export interface UpdateDetailLevelRequest {
detailLevel: number;
conversionMethod?: string;
changeReason?: string;
}
export interface ApiResponse<T> {
success: boolean;
message: string;
data: T;
errorCode?: string;
timestamp: number;
}
export interface TemperatureMappingResponse {
LINEAR: Record<number, number>;
EXPONENTIAL: Record<number, number>;
CONTEXTUAL: Record<number, number>;
}
export interface TemperatureCalculationResponse {
detailLevel: number;
conversionMethod: string;
calculatedTemperature: number;
}
export type ConversionMethod = 'LINEAR' | 'EXPONENTIAL' | 'CONTEXTUAL';
export interface DetailLevelFormData {
level: number;
method: ConversionMethod;
comment?: string;
}
export interface DetailLevelHistoryEntry {
id: number;
previousLevel?: number;
newLevel: number;
previousTemperature?: number;
newTemperature: number;
changeReason?: string;
changedBy?: string;
changedAt: string;
}
export interface DetailLevelState {
config: DetailLevelConfigDTO | null;
history: DetailLevelHistoryEntry[];
mapping: TemperatureMappingResponse | null;
loading: boolean;
error: string | null;
}
export interface DetailLevelUIProps {
versionId: string;
onSave?: (config: DetailLevelConfigDTO) => void;
onError?: (error: string) => void;
readOnly?: boolean;
}
\ No newline at end of file
import { create } from 'zustand';
import axiosInstance from 'src/utils/axios';
import { detailLevelEndpoints } from 'src/shared/api/endpoints/detail-level';
import type {
DetailLevelConfigDTO,
UpdateDetailLevelRequest,
ApiResponse,
TemperatureMappingResponse,
TemperatureCalculationResponse
} from 'src/types/detail-level.types'
interface DetailLevelStore {
detailLevelConfig: DetailLevelConfigDTO | null;
temperatureMapping: TemperatureMappingResponse | null;
loading: boolean;
error: string | null;
getDetailLevel: (versionId: string) => Promise<void>;
updateDetailLevel: (versionId: string, request: UpdateDetailLevelRequest) => Promise<void>;
createDetailLevel: (versionId: string, request: UpdateDetailLevelRequest) => Promise<void>;
deleteDetailLevel: (versionId: string) => Promise<void>;
resetDetailLevel: (versionId: string) => Promise<void>;
getTemperatureMapping: () => Promise<void>;
calculateTemperature: (level: number, method?: string) => Promise<TemperatureCalculationResponse | null>;
clearError: () => void;
clearConfig: () => void;
}
export const useDetailLevelStore = create<DetailLevelStore>((set, get) => ({
detailLevelConfig: null,
temperatureMapping: null,
loading: false,
error: null,
getDetailLevel: async (versionId: string) => {
set({ loading: true, error: null });
try {
const response = await axiosInstance.get(detailLevelEndpoints.get(versionId));
let config;
if (response.data.success !== undefined) {
if (response.data.success) {
config = response.data.data;
} else {
throw new Error(response.data.message || 'API Error');
}
} else {
config = response.data;
}
set({
detailLevelConfig: config,
loading: false
});
} catch (error: any) {
set({
error: error.response?.data?.message || error.message || 'Error during retrieval',
loading: false
});
}
},
updateDetailLevel: async (versionId: string, request: UpdateDetailLevelRequest) => {
set({ loading: true, error: null });
try {
const response = await axiosInstance.put(
detailLevelEndpoints.update(versionId),
request
);
let config;
if (response.data.success !== undefined) {
if (response.data.success) {
config = response.data.data;
} else {
throw new Error(response.data.message || 'API Error');
}
} else {
config = response.data;
}
set({
detailLevelConfig: config,
loading: false
});
await get().getDetailLevel(versionId);
} catch (error: any) {
set({
error: error.response?.data?.message || error.message || 'Error during update',
loading: false
});
}
},
createDetailLevel: async (versionId: string, request: UpdateDetailLevelRequest) => {
set({ loading: true, error: null });
try {
const response = await axiosInstance.post(
detailLevelEndpoints.create(versionId),
request
);
let config;
if (response.data.success !== undefined) {
if (response.data.success) {
config = response.data.data;
} else {
throw new Error(response.data.message || 'API Error');
}
} else {
config = response.data;
}
set({
detailLevelConfig: config,
loading: false
});
} catch (error: any) {
set({
error: error.response?.data?.message || error.message || 'Error during creation',
loading: false
});
}
},
deleteDetailLevel: async (versionId: string) => {
set({ loading: true, error: null });
try {
await axiosInstance.delete(detailLevelEndpoints.delete(versionId));
set({
detailLevelConfig: null,
loading: false
});
} catch (error: any) {
set({
error: error.response?.data?.message || error.message || 'Error during deletion',
loading: false
});
}
},
resetDetailLevel: async (versionId: string) => {
set({ loading: true, error: null });
try {
const response = await axiosInstance.post(detailLevelEndpoints.reset(versionId));
let config;
if (response.data.success !== undefined) {
if (response.data.success) {
config = response.data.data;
} else {
throw new Error(response.data.message || 'API Error');
}
} else {
config = response.data;
}
set({
detailLevelConfig: config,
loading: false
});
} catch (error: any) {
set({
error: error.response?.data?.message || error.message || 'Error during reset',
loading: false
});
}
},
getTemperatureMapping: async () => {
set({ loading: true, error: null });
try {
const response = await axiosInstance.get(detailLevelEndpoints.temperatureMapping);
set({
temperatureMapping: response.data,
loading: false
});
} catch (error: any) {
set({
error: error.response?.data?.message || error.message || 'Error during mapping retrieval',
loading: false
});
}
},
calculateTemperature: async (level: number, method: string = 'LINEAR') => {
try {
const response = await axiosInstance.get(
detailLevelEndpoints.calculateTemperature,
{
params: { level, method }
}
);
if (response.data.success !== undefined) {
return response.data.success ? response.data.data : null;
} else {
return response.data;
}
} catch (error: any) {
set({
error: error.response?.data?.message || error.message || 'Error during temperature calculation'
});
return null;
}
},
clearError: () => {
set({ error: null });
},
clearConfig: () => {
set({ detailLevelConfig: null, temperatureMapping: null, error: null });
},
}));
import type { AIAssistantDetailLevelProps } from 'src/types/ai-assistant';
import React, { useState, useEffect } from "react";
import {
Box,
Paper,
Button,
Slider,
Alert,
Chip,
Typography,
CircularProgress
} from "@mui/material";
import { useDetailLevelStore } from 'src/shared/api/stores/detailLevelStore';
import type { ConversionMethod } from 'src/types/detail-level.types';
import { useState, useEffect } from "react";
interface AIAssistantDetailLevelProps {
assistantId: string;
}
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
import Button from "@mui/material/Button";
import Slider from "@mui/material/Slider";
import Typography from "@mui/material/Typography";
import { AIAssistantHistoryService } from "../AIAssistantHistoryService";
import { AIAssistantDetailLevelService } from "./AIAssistantDetailLevelService";
export default function AIAssistantDetailLevel({ assistantId }: AIAssistantDetailLevelProps) {
// Initialize with safe values
const [currentLevel, setCurrentLevel] = useState<number>(3);
const [currentMethod, setCurrentMethod] = useState<ConversionMethod>('LINEAR');
const [calculatedTemp, setCalculatedTemp] = useState<number>(0.4);
import type { IAIAssistantDetailLevel } from "./AIAssistantDetailLevelService";
const [savedLevel, setSavedLevel] = useState<number>(3);
const [savedMethod, setSavedMethod] = useState<ConversionMethod>('LINEAR');
const [savedTemp, setSavedTemp] = useState<number>(0.4);
export default function AIAssistantDetailLevel({ assistantId }: AIAssistantDetailLevelProps) {
const [detailLevel, setDetailLevel] = useState<IAIAssistantDetailLevel>({ level: 3 });
const [isSaved, setIsSaved] = useState(false);
const {
detailLevelConfig,
loading,
error,
getDetailLevel,
updateDetailLevel,
resetDetailLevel,
clearError
} = useDetailLevelStore();
useEffect(() => {
// Si un assistantId est fourni, récupérer les paramètres spécifiques à cet assistant
if (assistantId) {
setDetailLevel(AIAssistantDetailLevelService.getDetailLevelById(assistantId));
} else {
setDetailLevel(AIAssistantDetailLevelService.getDetailLevel());
getDetailLevel(assistantId);
}
}, [assistantId, getDetailLevel]);
useEffect(() => {
if (detailLevelConfig) {
// Update current and saved values with fallback for temperature
const level = detailLevelConfig.detailLevel;
const method = detailLevelConfig.conversionMethod as ConversionMethod;
const temp = detailLevelConfig.calculatedTemperature ?? 0.4;
setCurrentLevel(level);
setCurrentMethod(method);
setCalculatedTemp(temp);
setSavedLevel(level);
setSavedMethod(method);
setSavedTemp(temp);
}
}, [assistantId]);
}, [detailLevelConfig]);
const handleChange = (_event: Event, newValue: number | number[]) => {
setDetailLevel({ level: newValue as number });
setIsSaved(false);
// Local temperature calculation
const calculateLocalTemperature = (level: number, method: ConversionMethod): number => {
switch (method) {
case 'LINEAR':
return (level - 1) * 0.2;
case 'EXPONENTIAL':
const exponentialMap = { 1: 0.1, 2: 0.25, 3: 0.45, 4: 0.7, 5: 0.9 };
return exponentialMap[level as keyof typeof exponentialMap] || 0.4;
case 'CONTEXTUAL':
const contextualMap = { 1: 0.0, 2: 0.15, 3: 0.35, 4: 0.6, 5: 0.8 };
return contextualMap[level as keyof typeof contextualMap] || 0.35;
default:
return (level - 1) * 0.2;
}
};
const handleSaveWithHistory = () => {
AIAssistantHistoryService.addEntry({
id: `hist-${Date.now()}`,
date: new Date().toLocaleString(),
user: "Admin", // Modifier si nécessaire
section: "Niveau de Détail",
action: "modify",
comment: `Modification du niveau de détail à ${detailLevel.level}`,
});
// Enregistrer les paramètres
if (assistantId) {
AIAssistantDetailLevelService.saveDetailLevelById(assistantId, detailLevel);
} else {
AIAssistantDetailLevelService.saveDetailLevel(detailLevel);
const handleLevelChange = (_event: Event, newValue: number | number[]) => {
const level = newValue as number;
setCurrentLevel(level);
// Local calculation without API call
const newTemp = calculateLocalTemperature(level, currentMethod);
setCalculatedTemp(newTemp);
};
const handleMethodChange = (newMethod: ConversionMethod) => {
setCurrentMethod(newMethod);
// Local calculation without API call
const newTemp = calculateLocalTemperature(currentLevel, newMethod);
setCalculatedTemp(newTemp);
};
const handleSave = async () => {
if (!assistantId) return;
try {
await updateDetailLevel(assistantId, {
detailLevel: currentLevel,
conversionMethod: currentMethod,
changeReason: `Modification du niveau de détail à ${currentLevel} avec méthode ${currentMethod}`
});
// Update saved values after success
setSavedLevel(currentLevel);
setSavedMethod(currentMethod);
setSavedTemp(calculatedTemp);
} catch (err) {
console.error('Erreur sauvegarde:', err);
}
setIsSaved(true);
setTimeout(() => setIsSaved(false), 3000);
};
// Récupérer les descriptions et exemples
const LEVEL_DESCRIPTIONS = AIAssistantDetailLevelService.getLevelDescriptions();
const EXAMPLES = AIAssistantDetailLevelService.getLevelExamples();
const handleReset = async () => {
if (!assistantId) return;
try {
await resetDetailLevel(assistantId);
} catch (err) {
console.error('Erreur reset:', err);
}
};
// Cancel changes (revert to saved values)
const handleCancel = () => {
setCurrentLevel(savedLevel);
setCurrentMethod(savedMethod);
setCalculatedTemp(savedTemp);
};
// Check for unsaved changes
const hasUnsavedChanges =
currentLevel !== savedLevel ||
currentMethod !== savedMethod ||
Math.abs(calculatedTemp - savedTemp) > 0.01;
// Helper function to safely format temperature
const formatTemp = (temp?: number) => typeof temp === 'number' ? temp.toFixed(2) : '0.00';
const LEVEL_DESCRIPTIONS = [
"Niveau 1 : Indices minimaux - Guidage très léger",
"Niveau 2 : Indices basiques - Orientations générales",
"Niveau 3 : Indices modérés - Explications concises des concepts clés",
"Niveau 4 : Indices détaillés - Avec exemples concrets et étapes",
"Niveau 5 : Indices très détaillés - Explications approfondies avec méthodes complètes"
];
const EXAMPLES = [
"Exemple (Niveau 1) : 'Vérifie la terminaison du verbe.'",
"Exemple (Niveau 2) : 'Pense aux règles du présent de l'indicatif.'",
"Exemple (Niveau 3) : 'Les verbes du 1er groupe prennent -e, -es, -e…'",
"Exemple (Niveau 4) : 'Regarde comment le radical change en fonction du pronom sujet.'",
"Exemple (Niveau 5) : 'Ce verbe suit une conjugaison irrégulière, voici la règle complète avec tous les cas particuliers…'"
];
if (loading && !detailLevelConfig) {
return (
<Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}>
<CircularProgress />
<Typography sx={{ ml: 2 }}>Chargement de la configuration...</Typography>
</Box>
);
}
return (
<Box sx={{ width: "100%", p: 3 }}>
{error && (
<Alert severity="error" sx={{ mb: 2 }} onClose={clearError}>
{error}
</Alert>
)}
{/* Alert for unsaved changes */}
{hasUnsavedChanges && (
<Alert severity="warning" sx={{ mb: 2 }}>
Vous avez des modifications non sauvegardées
</Alert>
)}
{/* Current configuration */}
<Paper elevation={2} sx={{ p: 3, mb: 4 }}>
{/* Curseur de Niveau */}
<Typography variant="h6" sx={{ mb: 1 }}>
Niveau Actuel : {detailLevel.level}
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h6">
Niveau de Détail : {currentLevel}
</Typography>
<Chip
label={`Température IA: ${formatTemp(calculatedTemp)}`}
color={hasUnsavedChanges ? "warning" : "primary"}
variant="outlined"
size="medium"
/>
</Box>
{/* Display saved vs current values */}
{hasUnsavedChanges && (
<Box sx={{ mb: 2, p: 2, bgcolor: 'grey.100', borderRadius: 1 }}>
<Typography variant="body2">
<strong>Valeur sauvegardée:</strong> Niveau {savedLevel} - Température {formatTemp(savedTemp)}
</Typography>
<Typography variant="body2">
<strong>Valeur actuelle:</strong> Niveau {currentLevel} - Température {formatTemp(calculatedTemp)}
</Typography>
</Box>
)}
{/* Level slider */}
<Slider
value={detailLevel.level}
value={currentLevel}
min={1}
max={5}
step={1}
marks
onChange={handleChange}
marks={[
{ value: 1, label: '1' },
{ value: 2, label: '2' },
{ value: 3, label: '3' },
{ value: 4, label: '4' },
{ value: 5, label: '5' }
]}
onChange={handleLevelChange}
sx={{ mt: 2, mb: 3 }}
valueLabelDisplay="auto"
disabled={loading}
/>
{/* Description du niveau sélectionné */}
<Typography variant="body1" sx={{ fontWeight: 'medium', mb: 2 }}>
{LEVEL_DESCRIPTIONS[detailLevel.level - 1]}
{LEVEL_DESCRIPTIONS[currentLevel - 1]}
</Typography>
</Paper>
{/* Exemple dynamique basé sur le niveau sélectionné */}
{/* Example hint */}
<Paper elevation={2} sx={{ p: 3, mb: 4, bgcolor: "background.default" }}>
<Typography variant="h6" sx={{ mb: 2 }}>
Exemple d&apos;Indice
Exemple d&apos;Indice Généré
</Typography>
<Box sx={{ p: 2, bgcolor: "background.paper", borderRadius: 1, border: '1px solid #e0e0e0' }}>
<Typography variant="body2">
{EXAMPLES[detailLevel.level - 1]}
<Box sx={{
p: 2,
bgcolor: "background.paper",
borderRadius: 1,
border: '1px solid #e0e0e0',
borderLeft: `4px solid ${currentLevel <= 2 ? '#ff9800' : currentLevel <= 4 ? '#2196f3' : '#4caf50'}`
}}>
<Typography variant="body2" sx={{ fontStyle: 'italic' }}>
{EXAMPLES[currentLevel - 1]}
</Typography>
</Box>
</Paper>
{/* Boutons Enregistrement */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Button
{/* Actions */}
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
{hasUnsavedChanges && (
<Button
variant="text"
color="secondary"
onClick={handleCancel}
disabled={loading}
>
Annuler
</Button>
)}
<Button
variant="outlined"
color="secondary"
onClick={handleReset}
disabled={loading}
>
Réinitialiser aux valeurs par défaut
</Button>
<Button
variant="contained"
color="primary"
onClick={handleSaveWithHistory}
onClick={handleSave}
disabled={loading || !assistantId || !hasUnsavedChanges}
sx={{ minWidth: 200 }}
>
Enregistrer les modifications
{loading ? 'Sauvegarde...' : 'Enregistrer les modifications'}
</Button>
{isSaved && (
<Typography variant="body2" color="success.main">
Modifications enregistrées avec succès
</Typography>
)}
</Box>
</Box>
);
......
export interface IAIAssistantDetailLevel {
level: number; // Niveau entre 1 et 5
}
export class AIAssistantDetailLevelService {
private static STORAGE_KEY = "assistant_detail_level";
// Récupérer le niveau de détail depuis localStorage
static getDetailLevel(): IAIAssistantDetailLevel {
const data = localStorage.getItem(this.STORAGE_KEY);
return data ? JSON.parse(data) : { level: 3 }; // Niveau 3 par défaut
}
// Sauvegarder le niveau de détail dans localStorage
static saveDetailLevel(settings: IAIAssistantDetailLevel) {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(settings));
}
// Récupérer le niveau de détail pour un assistant spécifique
static getDetailLevelById(assistantId: string): IAIAssistantDetailLevel {
const key = `${this.STORAGE_KEY}_${assistantId}`;
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : { level: 3 }; // Niveau 3 par défaut
}
// Sauvegarder le niveau de détail pour un assistant spécifique
static saveDetailLevelById(assistantId: string, settings: IAIAssistantDetailLevel) {
const key = `${this.STORAGE_KEY}_${assistantId}`;
localStorage.setItem(key, JSON.stringify(settings));
}
// Obtenir les descriptions pour chaque niveau
static getLevelDescriptions(): string[] {
return [
"Niveau 1 : Indices minimaux",
"Niveau 2 : Indices basiques",
"Niveau 3 : Indices modérés : explications concises des concepts clés",
"Niveau 4 : Indices détaillés avec exemples concrets",
"Niveau 5 : Indices très détaillés avec explication approfondie",
];
}
// Obtenir les exemples pour chaque niveau
static getLevelExamples(): string[] {
return [
"Exemple (Niveau 1) : 'Vérifie la terminaison du verbe.'",
"Exemple (Niveau 2) : 'Pense aux règles du présent de l'indicatif.'",
"Exemple (Niveau 3) : 'Les verbes du 1er groupe prennent -e, -es, -e…'",
"Exemple (Niveau 4) : 'Regarde comment le radical change en fonction du pronom.'",
"Exemple (Niveau 5) : 'Ce verbe suit une conjugaison irrégulière, voici la règle complète…'",
];
}
}
\ No newline at end of file
export interface DetailLevelConfigDTO {
id: number;
versionId: number;
detailLevel: number;
calculatedTemperature: number;
conversionMethod: string;
createdAt: string;
updatedAt: string;
createdBy?: string;
lastModifiedBy?: string;
}
export interface UpdateDetailLevelRequest {
detailLevel: number;
conversionMethod?: string;
changeReason?: string;
}
export interface ApiResponse<T> {
success: boolean;
message: string;
data: T;
errorCode?: string;
timestamp: number;
}
export interface TemperatureMappingResponse {
LINEAR: Record<number, number>;
EXPONENTIAL: Record<number, number>;
CONTEXTUAL: Record<number, number>;
}
export interface TemperatureCalculationResponse {
detailLevel: number;
conversionMethod: string;
calculatedTemperature: number;
}
export type ConversionMethod = 'LINEAR' | 'EXPONENTIAL' | 'CONTEXTUAL';
export interface DetailLevelFormData {
level: number;
method: ConversionMethod;
comment?: string;
}
export interface DetailLevelHistoryEntry {
id: number;
previousLevel?: number;
newLevel: number;
previousTemperature?: number;
newTemperature: number;
changeReason?: string;
changedBy?: string;
changedAt: string;
}
export interface DetailLevelState {
config: DetailLevelConfigDTO | null;
history: DetailLevelHistoryEntry[];
mapping: TemperatureMappingResponse | null;
loading: boolean;
error: string | null;
}
export interface DetailLevelUIProps {
versionId: string;
onSave?: (config: DetailLevelConfigDTO) => void;
onError?: (error: string) => void;
readOnly?: boolean;
}
\ No newline at end of file
0% ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter