feat: fix clouds functions and heygen

This commit is contained in:
2026-09-04 12:22:29 +02:00
parent 7bf913e014
commit efbb7994f2
12 changed files with 276 additions and 75 deletions
+94 -34
View File
@@ -6,8 +6,64 @@ const { Buffer } = require('buffer')
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'
// Utilise des versions stables explicites en production. L'alias `latest` peut
// changer sans déploiement et pointer temporairement vers un modèle saturé.
const TEXT_MODEL_NAMES = ['gemini-3.6-flash', 'gemini-3.5-flash', 'gemini-3.1-flash-lite']
const MODERATION_MODEL_NAMES = ['gemini-3.1-flash-lite', 'gemini-3.6-flash', 'gemini-3.5-flash']
const RETRYABLE_STATUS_CODES = new Set([404, 429, 500, 502, 503, 504])
const getErrorStatus = (error) => {
const status = Number(error?.status || error?.statusCode || error?.response?.status)
return Number.isFinite(status) ? status : null
}
const canTryFallbackModel = (error) => {
const status = getErrorStatus(error)
if (status && RETRYABLE_STATUS_CODES.has(status)) return true
const message = String(error?.message || '').toLowerCase()
return (
message.includes('high demand') ||
message.includes('overloaded') ||
message.includes('temporarily unavailable') ||
message.includes('model not found')
)
}
const generateWithModelFallback = async ({ ai, modelNames, label, request }) => {
let lastError = null
for (let index = 0; index < modelNames.length; index++) {
const modelName = modelNames[index]
try {
console.log(`🤖 [${label}] Modèle ${modelName} (${index + 1}/${modelNames.length})`)
const response = await ai.generate({
...request,
model: googleAI.model(modelName),
})
if (!response?.output) {
throw new Error(`${label}: réponse structurée vide pour ${modelName}.`)
}
return response.output
} catch (error) {
lastError = error
const hasFallback = index < modelNames.length - 1
if (!hasFallback || !canTryFallbackModel(error)) throw error
console.warn(`⚠️ [${label}] ${modelName} indisponible, essai du modèle suivant`, {
status: getErrorStatus(error),
message: error?.message,
})
await setTimeout(500 * (index + 1))
}
}
throw lastError || new Error(`${label}: aucun modèle disponible.`)
}
const IMAGE_MODEL_NAME = 'gemini-3-pro-image'
// --- SINGLETON PATTERN (WARM START) ---
@@ -35,21 +91,23 @@ exports.generateAI = async ({ system = '', prompt = '', schema }) => {
throw new Error('Vous devez spécifier un prompt pour effectuer cette action.')
}
console.log(`🧠 [generateAI] Start (${TEXT_MODEL_NAME})`)
console.log(`🧠 [generateAI] Start (${TEXT_MODEL_NAMES.join(' -> ')})`)
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
return await generateWithModelFallback({
ai,
modelNames: TEXT_MODEL_NAMES,
label: 'generateAI',
request: {
system,
prompt,
output: { schema },
config: {
temperature: 0.7, // Créativité équilibrée
},
},
})
return output
} catch (error) {
console.error('❌ [generateAI] Error:', error.message)
throw error
@@ -129,34 +187,36 @@ ${lyricsText}
"""
`
console.log(`🛡️ [analyseLyrics] Start (${TEXT_MODEL_NAME})`)
console.log(`🛡️ [analyseLyrics] Start (${MODERATION_MODEL_NAMES.join(' -> ')})`)
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 generateWithModelFallback({
ai,
modelNames: MODERATION_MODEL_NAMES,
label: 'analyseLyrics',
request: {
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' },
],
},
},
})
if (!output) throw new Error("Échec de l'analyse de modération.")
// Correction de cohérence
if (output.blocked) {
output.flagged = true