Refactor app flows and refresh UI components
This commit is contained in:
+76
-31
@@ -7,8 +7,46 @@ const { setTimeout } = require('timers/promises')
|
||||
|
||||
// --- CONFIGURATION ---
|
||||
// Plus intelligente que le Flash original, ultra rapide, et stable sur l'API.
|
||||
const TEXT_MODEL_NAME = 'gemini-flash-latest'
|
||||
const TEXT_MODEL_NAME = 'gemini-3.5-flash'
|
||||
const TEXT_FALLBACK_MODEL_NAME = 'gemini-3.1-flash-lite'
|
||||
const IMAGE_MODEL_NAME = 'gemini-3-pro-image'
|
||||
const TEXT_GENERATION_MAX_ATTEMPTS = 3
|
||||
const TEXT_GENERATION_RETRY_DELAY_MS = 1000
|
||||
|
||||
const isRetryableTextGenerationError = (error) => {
|
||||
const status = Number(error?.status || error?.response?.status)
|
||||
return [429, 500, 502, 503, 504].includes(status)
|
||||
}
|
||||
|
||||
const generateTextWithRetry = async (ai, request, operation) => {
|
||||
let lastError = null
|
||||
|
||||
for (let attempt = 1; attempt <= TEXT_GENERATION_MAX_ATTEMPTS; attempt += 1) {
|
||||
const modelName =
|
||||
attempt === TEXT_GENERATION_MAX_ATTEMPTS ? TEXT_FALLBACK_MODEL_NAME : TEXT_MODEL_NAME
|
||||
|
||||
try {
|
||||
return await ai.generate({
|
||||
...request,
|
||||
model: googleAI.model(modelName),
|
||||
})
|
||||
} catch (error) {
|
||||
lastError = error
|
||||
const shouldRetry =
|
||||
isRetryableTextGenerationError(error) && attempt < TEXT_GENERATION_MAX_ATTEMPTS
|
||||
|
||||
if (!shouldRetry) throw error
|
||||
|
||||
const delayMs = TEXT_GENERATION_RETRY_DELAY_MS * 2 ** (attempt - 1)
|
||||
console.warn(
|
||||
`🔄 [${operation}] ${modelName} indisponible (${error?.status || 'réseau'}), nouvelle tentative ${attempt + 1}/${TEXT_GENERATION_MAX_ATTEMPTS} dans ${delayMs}ms`
|
||||
)
|
||||
await setTimeout(delayMs)
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError
|
||||
}
|
||||
|
||||
// --- SINGLETON PATTERN (WARM START) ---
|
||||
// On stocke l'instance en dehors de la fonction pour la réutiliser
|
||||
@@ -39,15 +77,18 @@ exports.generateAI = async ({ system = '', prompt = '', schema }) => {
|
||||
const startedAt = Date.now()
|
||||
|
||||
try {
|
||||
const { output } = await ai.generate({
|
||||
model: googleAI.model(TEXT_MODEL_NAME),
|
||||
system,
|
||||
prompt,
|
||||
output: { schema },
|
||||
config: {
|
||||
temperature: 0.7, // Créativité équilibrée
|
||||
const { output } = await generateTextWithRetry(
|
||||
ai,
|
||||
{
|
||||
system,
|
||||
prompt,
|
||||
output: { schema },
|
||||
config: {
|
||||
temperature: 0.7, // Créativité équilibrée
|
||||
},
|
||||
},
|
||||
})
|
||||
'generateAI'
|
||||
)
|
||||
|
||||
return output
|
||||
} catch (error) {
|
||||
@@ -97,7 +138,7 @@ exports.analyseLyrics = async ({ title = '', lyrics }) => {
|
||||
flagged: z.boolean().describe('Vrai si le contenu nécessite un avertissement.'),
|
||||
blocked: z.boolean().describe('Vrai UNIQUEMENT si violation grave (Haine, Violence réelle).'),
|
||||
score: z.number().min(0).max(1).describe('Score de risque (0=Sûr, 1=Dangereux).'),
|
||||
reasons: z.array(z.string()).describe('Liste concise des raisons.'),
|
||||
reasons: z.array(z.string()).max(5).describe('Liste concise des raisons.'),
|
||||
excerpts: z
|
||||
.array(
|
||||
z.object({
|
||||
@@ -106,7 +147,7 @@ exports.analyseLyrics = async ({ title = '', lyrics }) => {
|
||||
severity: z.string(),
|
||||
})
|
||||
)
|
||||
.max(10),
|
||||
.max(3),
|
||||
success: z.boolean(),
|
||||
})
|
||||
|
||||
@@ -133,27 +174,31 @@ ${lyricsText}
|
||||
const startedAt = Date.now()
|
||||
|
||||
try {
|
||||
const { output } = await ai.generate({
|
||||
model: googleAI.model(TEXT_MODEL_NAME),
|
||||
system,
|
||||
prompt: userPrompt,
|
||||
output: { schema: moderationSchema },
|
||||
config: {
|
||||
// Paramètres de sécurité permissifs pour laisser l'IA voir et juger le contenu
|
||||
safetySettings: [
|
||||
{ category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
|
||||
{
|
||||
category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
||||
threshold: 'BLOCK_NONE',
|
||||
},
|
||||
{
|
||||
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
|
||||
threshold: 'BLOCK_NONE',
|
||||
},
|
||||
{ category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_NONE' },
|
||||
],
|
||||
const { output } = await generateTextWithRetry(
|
||||
ai,
|
||||
{
|
||||
system,
|
||||
prompt: userPrompt,
|
||||
output: { schema: moderationSchema },
|
||||
config: {
|
||||
temperature: 0,
|
||||
// Paramètres de sécurité permissifs pour laisser l'IA voir et juger le contenu
|
||||
safetySettings: [
|
||||
{ category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
|
||||
{
|
||||
category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
||||
threshold: 'BLOCK_NONE',
|
||||
},
|
||||
{
|
||||
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
|
||||
threshold: 'BLOCK_NONE',
|
||||
},
|
||||
{ category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_NONE' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
'analyseLyrics'
|
||||
)
|
||||
|
||||
if (!output) throw new Error("Échec de l'analyse de modération.")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user