feat: enhance image model detection logic and improve fallback handling in AI service
This commit is contained in:
@@ -1,43 +0,0 @@
|
|||||||
import { CommandMessage } from "../../../core/types/commands";
|
|
||||||
import { aiService } from "../../../core/services/AIService";
|
|
||||||
import logger from "../../../core/lib/logger";
|
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
|
||||||
name: 'aimodels',
|
|
||||||
type: 'message',
|
|
||||||
aliases: ['listmodels'],
|
|
||||||
cooldown: 10,
|
|
||||||
description: 'Lista modelos de imagen disponibles y muestra el actual.',
|
|
||||||
category: 'IA',
|
|
||||||
usage: 'aimodels',
|
|
||||||
run: async (message, args) => {
|
|
||||||
try {
|
|
||||||
const models = await aiService.listImageModels();
|
|
||||||
const current = (aiService as any).imageModelName || 'No detectado';
|
|
||||||
|
|
||||||
if (models.length === 0) {
|
|
||||||
await message.reply({
|
|
||||||
content: `**Modelos de imagen disponibles:** Ninguno detectado
|
|
||||||
**Modelo actual:** ${current}
|
|
||||||
|
|
||||||
Para usar un modelo específico:
|
|
||||||
\`GENAI_IMAGE_MODEL=imagen-3.0-fast\``
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const modelList = models.map(m => `• ${m}`).join('\n');
|
|
||||||
await message.reply({
|
|
||||||
content: `**Modelos de imagen disponibles:**
|
|
||||||
${modelList}
|
|
||||||
|
|
||||||
**Modelo actual:** ${current}
|
|
||||||
|
|
||||||
Para cambiar: \`GENAI_IMAGE_MODEL=nombre_del_modelo\``
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
|
||||||
logger.error(error, 'Error listando modelos');
|
|
||||||
await message.reply({ content: `❌ Error: ${error?.message || 'Error desconocido'}` });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { CommandMessage } from "../../../core/types/commands";
|
|
||||||
import { aiService } from "../../../core/services/AIService";
|
|
||||||
import logger from "../../../core/lib/logger";
|
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
|
||||||
name: 'setimagemodel',
|
|
||||||
type: 'message',
|
|
||||||
aliases: ['setmodel'],
|
|
||||||
cooldown: 5,
|
|
||||||
description: 'Establece el modelo de imágenes manualmente.',
|
|
||||||
category: 'IA',
|
|
||||||
usage: 'setimagemodel <modelo>',
|
|
||||||
run: async (message, args) => {
|
|
||||||
try {
|
|
||||||
if (!args || args.length === 0) {
|
|
||||||
await message.reply({
|
|
||||||
content: 'Uso: setimagemodel <modelo>\nEjemplo: setimagemodel imagen-3.0-fast'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const model = args.join(' ').trim();
|
|
||||||
(aiService as any).setImageModel(model);
|
|
||||||
|
|
||||||
await message.reply({
|
|
||||||
content: `✅ Modelo de imágenes establecido: \`${model}\`\nPrueba con: \`aiimg un gato astronauta\``
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
|
||||||
logger.error(error, 'Error estableciendo modelo');
|
|
||||||
await message.reply({ content: `❌ Error: ${error?.message || 'Error desconocido'}` });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -150,61 +150,89 @@ export class AIService {
|
|||||||
return override;
|
return override;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lista de candidatos conocidos (orden de preferencia)
|
// Lista de candidatos más amplia y realista para cuentas Pro actuales
|
||||||
const candidates = [
|
const candidates = [
|
||||||
'gemini-2.5-flash-image', // puede no estar disponible aún en tu proyecto/region/apiVersion
|
'gemini-2.5-flash-exp',
|
||||||
|
'gemini-2.0-flash-exp',
|
||||||
|
'imagen-3.0-generate-001',
|
||||||
|
'imagen-3.0-fast-generate-001',
|
||||||
|
'imagen-3.0-001',
|
||||||
|
'imagegeneration@002',
|
||||||
|
'imagegeneration@001',
|
||||||
'imagen-3.0-generate',
|
'imagen-3.0-generate',
|
||||||
'imagen-3.0-fast',
|
'imagen-3.0-fast',
|
||||||
'imagen-3.0',
|
'imagen-3.0',
|
||||||
|
'gemini-2.5-flash-image',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Intentar listar modelos si el SDK lo soporta
|
// Intentar listar modelos primero
|
||||||
try {
|
try {
|
||||||
const listed: any = await (this.genAIv2 as any).models?.listModels?.();
|
const listed: any = await (this.genAIv2 as any).models?.listModels?.();
|
||||||
const models: string[] = Array.isArray(listed?.models)
|
if (listed?.models && Array.isArray(listed.models)) {
|
||||||
? listed.models.map((m: any) => m?.name || m?.model || m?.id).filter(Boolean)
|
const models: string[] = listed.models
|
||||||
: [];
|
.map((m: any) => m?.name || m?.model || m?.id || m?.displayName)
|
||||||
if (models.length) {
|
.filter(Boolean)
|
||||||
// Buscar modelos que parezcan de imagen
|
.map((name: string) => name.replace(/^models\//, '')); // Quitar prefijo models/
|
||||||
const imageLike = models.filter((id: string) => /imagen|image/i.test(id));
|
|
||||||
// Priorizar nuestros candidatos en el orden propuesto; si no, tomar el primero imageLike
|
logger.debug({ availableModels: models }, 'Modelos disponibles detectados');
|
||||||
for (const c of candidates) {
|
|
||||||
if (imageLike.some((m) => m.includes(c))) {
|
// Buscar modelos de imagen disponibles
|
||||||
this.imageModelName = imageLike.find((m) => m.includes(c))!;
|
const imageModels = models.filter((id: string) =>
|
||||||
logger.info({ model: this.imageModelName }, 'Modelo de imágenes detectado por listModels (preferencia)');
|
/imagen|image|generate|vision/i.test(id) &&
|
||||||
return this.imageModelName;
|
!/text|chat|embed|code/i.test(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (imageModels.length > 0) {
|
||||||
|
// Priorizar según orden de candidatos
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
const found = imageModels.find(m => m.includes(candidate.replace(/^models\//, '')));
|
||||||
|
if (found) {
|
||||||
|
this.imageModelName = found;
|
||||||
|
logger.info({ model: found, source: 'listModels' }, 'Modelo de imágenes detectado automáticamente');
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (imageLike[0]) {
|
|
||||||
this.imageModelName = imageLike[0];
|
// Si no coincide con candidatos conocidos, usar el primero disponible
|
||||||
logger.info({ model: this.imageModelName }, 'Modelo de imágenes detectado por listModels');
|
this.imageModelName = imageModels[0];
|
||||||
return this.imageModelName;
|
logger.info({ model: imageModels[0], source: 'listModels-fallback' }, 'Modelo de imágenes detectado (fallback)');
|
||||||
|
return imageModels[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Continuar con prueba directa de candidatos si listModels no existe o falla
|
logger.debug({ err: getErrorMessage(e) }, 'listModels no disponible');
|
||||||
logger.debug({ err: getErrorMessage(e) }, 'listModels no disponible o falló; probando candidatos conocidos');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Probar generar un ping mínimo con candidatos conocidos (sin coste excesivo)
|
// Fallback: probar modelos uno por uno con generateContent usando responseMimeType
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
try {
|
try {
|
||||||
// Intento muy ligero: usar generateImages con un prompt mínimo
|
// Usar generateContent con responseMimeType image/* como detector
|
||||||
await (this.genAIv2 as any).models.generateImages({
|
const testRes: any = await (this.genAIv2 as any).models.generateContent({
|
||||||
model: candidate,
|
model: candidate,
|
||||||
prompt: 'ping',
|
contents: [{ text: 'test' }],
|
||||||
config: { imageSize: '1:1' },
|
config: {
|
||||||
|
responseMimeType: 'image/png',
|
||||||
|
maxOutputTokens: 1,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Si no lanza error 404, el modelo existe
|
||||||
this.imageModelName = candidate;
|
this.imageModelName = candidate;
|
||||||
logger.info({ model: candidate }, 'Modelo de imágenes detectado por prueba directa');
|
logger.info({ model: candidate, source: 'direct-test' }, 'Modelo de imágenes detectado por prueba directa');
|
||||||
return candidate;
|
return candidate;
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
// 404/NOT_FOUND esperado para modelos no disponibles; seguir
|
const msg = getErrorMessage(e);
|
||||||
continue;
|
if (msg.includes('not found') || msg.includes('404')) {
|
||||||
|
continue; // Modelo no disponible, probar siguiente
|
||||||
|
}
|
||||||
|
// Otros errores pueden indicar que el modelo existe pero falló por otra razón
|
||||||
|
logger.debug({ candidate, err: msg }, 'Modelo podría existir pero falló la prueba');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No se encontró ningún modelo de imagen
|
||||||
this.imageModelName = null;
|
this.imageModelName = null;
|
||||||
|
logger.warn('No se detectó ningún modelo de imagen disponible');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user