Proeycto de images-worker creado
This commit is contained in:
37
mvp/image-worker/skills/image-generator.md
Normal file
37
mvp/image-worker/skills/image-generator.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Generador de imágenes — via OpenRouter
|
||||
|
||||
## Responsabilidad
|
||||
Recibir un prompt en inglés y una foto "antes", devolver el render "después" como data URI base64.
|
||||
|
||||
## Llamada a OpenRouter
|
||||
POST https://openrouter.ai/api/v1/chat/completions
|
||||
Authorization: Bearer {OPENROUTER_API_KEY}
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"model": "{OPENROUTER_MODEL_IMAGEN}", // google/gemini-2.0-flash-exp-image-generation
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{ "type": "text", "text": promptGenerado },
|
||||
{ "type": "image_url", "image_url": { "url": fotoAntesDataUri } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
## Manejo de la respuesta
|
||||
Extraer la imagen generada de la respuesta. Buscar en:
|
||||
1. content directo como data URI
|
||||
2. expresion regular data:image/...;base64,...
|
||||
3. URL de imagen
|
||||
4. Partes del mensaje (choices[0].message.content si es array)
|
||||
|
||||
Devolver siempre como data:image/png;base64,...
|
||||
|
||||
## Errores
|
||||
- Error de red → lanzar excepción, pipeline.service reintentará
|
||||
- Respuesta 429 (rate limit) → esperar 5s y reintentar 1 vez
|
||||
- Respuesta 5xx → lanzar excepción inmediatamente
|
||||
26
mvp/image-worker/skills/pipeline.md
Normal file
26
mvp/image-worker/skills/pipeline.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Pipeline de 3 etapas
|
||||
|
||||
## Responsabilidad
|
||||
Orquestar el procesamiento completo de un lead: desde recibir el perfil hasta entregar los renders a la app principal.
|
||||
|
||||
## Flujo por zona
|
||||
Para cada zona del lead que tenga fotos "antes":
|
||||
1. Etapa 1 → prompt-builder genera el prompt en inglés
|
||||
2. Etapa 2 → image-generator produce el render
|
||||
3. Etapa 3 → supervisor valida la coherencia
|
||||
4. Si rechazado → reintentar máximo MAX_RETRIES veces desde Etapa 2
|
||||
5. Si sigue rechazado → usar el último render de todos modos y loguear
|
||||
|
||||
## Reglas
|
||||
- Zonas sin fotos "antes": saltar y loguear, nunca lanzar error
|
||||
- Procesar todas las zonas antes de llamar a /ingesta
|
||||
- Enviar todos los renders en una sola llamada con finalizar: true
|
||||
- El pipeline corre en background, no bloquea el webhook
|
||||
|
||||
## Logs obligatorios
|
||||
- [leadId] Iniciando pipeline para N zonas
|
||||
- [leadId] Zona X: prompt generado
|
||||
- [leadId] Zona X: imagen generada
|
||||
- [leadId] Zona X: aprobada/rechazada (score: N)
|
||||
- [leadId] Zona X: reintento N de MAX_RETRIES
|
||||
- [leadId] Renders entregados correctamente
|
||||
26
mvp/image-worker/skills/prompt-builder.md
Normal file
26
mvp/image-worker/skills/prompt-builder.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Prompt Builder — Claude Haiku 4.5 via OpenRouter
|
||||
|
||||
## Responsabilidad
|
||||
Generar un prompt técnico detallado en inglés para el modelo de image-to-image a partir del contexto del lead (tipo de reforma, m², calidad, notas del cliente).
|
||||
|
||||
## Llamada a OpenRouter
|
||||
POST https://openrouter.ai/api/v1/chat/completions
|
||||
Authorization: Bearer {OPENROUTER_API_KEY}
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"model": "{OPENROUTER_MODEL_TEXTO}", // anthropic/claude-3.5-haiku-20241022
|
||||
"messages": [
|
||||
{ "role": "system", "content": "You are an expert interior designer..." },
|
||||
{ "role": "user", "content": "Generate a render prompt for a cocina renovation..." }
|
||||
],
|
||||
"max_tokens": 512,
|
||||
"temperature": 0.5
|
||||
}
|
||||
|
||||
## System prompt
|
||||
Contenido de prompts/prompt-builder.txt
|
||||
|
||||
## Respuesta
|
||||
Texto plano con el prompt en inglés. Sin markdown, sin JSON, sin explicaciones.
|
||||
40
mvp/image-worker/skills/reformix-api.md
Normal file
40
mvp/image-worker/skills/reformix-api.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# API de la app principal Reformix
|
||||
|
||||
## Responsabilidad
|
||||
Entregar los renders generados al endpoint /ingesta de la app Reformix.
|
||||
|
||||
## Endpoint
|
||||
POST {REFORMIX_API_URL}/api/leads/{leadId}/ingesta
|
||||
Authorization: Bearer {FUNNEL_API_KEY}
|
||||
Content-Type: application/json
|
||||
|
||||
## Body
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"tipo": "foto",
|
||||
"zona": "cocina", // zona que se procesó
|
||||
"momento": "despues", // siempre "despues" para renders generados
|
||||
"imagen": "data:image/png;base64,..."
|
||||
}
|
||||
// un item por cada zona procesada
|
||||
],
|
||||
"finalizar": true // siempre true, dispara PDF + email + WhatsApp
|
||||
}
|
||||
|
||||
## Enums válidos (no usar otros valores)
|
||||
tipo item: "foto" | "texto"
|
||||
momento: "antes" | "despues"
|
||||
zona: "cocina" | "bano" | "salon" | "comedor" | "integral" | "otro"
|
||||
|
||||
## Códigos de respuesta
|
||||
200 { ok: true } → éxito
|
||||
401 → FUNNEL_API_KEY incorrecta
|
||||
404 → leadId no existe, no reintentar
|
||||
422 → payload mal formado, revisar el body
|
||||
|
||||
## Reintentos
|
||||
En caso de error 5xx o error de red:
|
||||
→ reintentar 3 veces con 2 segundos de espera entre intentos
|
||||
→ si sigue fallando, loguear como error crítico con el leadId
|
||||
→ nunca reintentar en caso de 404
|
||||
43
mvp/image-worker/skills/supervisor.md
Normal file
43
mvp/image-worker/skills/supervisor.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Supervisor de calidad — Claude Haiku 4.5 Vision via OpenRouter
|
||||
|
||||
## Responsabilidad
|
||||
Comparar la foto "antes" con el render "después" y verificar que el resultado es coherente
|
||||
con el brief del cliente. Devolver aprobación y score.
|
||||
|
||||
## Llamada a OpenRouter
|
||||
POST https://openrouter.ai/api/v1/chat/completions
|
||||
Authorization: Bearer {OPENROUTER_API_KEY}
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"model": "{OPENROUTER_MODEL_TEXTO}", // anthropic/claude-3.5-haiku-20241022
|
||||
"messages": [
|
||||
{ "role": "system", "content": "You are a quality supervisor..." },
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{ "type": "text", "text": "Reforma tipo: cocina\nMetros: 12..." },
|
||||
{ "type": "image_url", "image_url": { "url": "data:image/...foto_antes" } },
|
||||
{ "type": "image_url", "image_url": { "url": "data:image/...render_despues" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"max_tokens": 256,
|
||||
"temperature": 0.2
|
||||
}
|
||||
|
||||
## System prompt
|
||||
Contenido de prompts/supervisor.txt
|
||||
|
||||
## Respuesta esperada
|
||||
JSON estricto:
|
||||
{ "aprobado": boolean, "score": number, "motivo": string }
|
||||
|
||||
## Manejo de respuesta malformada
|
||||
Si el modelo no devuelve JSON válido:
|
||||
→ devolver { aprobado: false, score: 0, motivo: "Error parseando respuesta del supervisor" }
|
||||
Nunca lanzar excepción desde el supervisor, siempre devolver el objeto.
|
||||
|
||||
## Umbral de aprobación
|
||||
aprobado: true AND score >= SUPERVISOR_MIN_SCORE (del .env, por defecto 70)
|
||||
35
mvp/image-worker/skills/webhook.md
Normal file
35
mvp/image-worker/skills/webhook.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Webhook entrante — /perfil-completo
|
||||
|
||||
## Responsabilidad
|
||||
Recibir el payload de la app Reformix, validarlo y arrancar el pipeline en background.
|
||||
|
||||
## Endpoint
|
||||
POST /perfil-completo
|
||||
|
||||
## Respuesta inmediata (siempre)
|
||||
{ "ok": true, "message": "Procesando renders en background..." }
|
||||
Nunca bloquear esperando al pipeline.
|
||||
|
||||
## Payload esperado
|
||||
{
|
||||
leadId: string (UUID),
|
||||
cliente: { nombre, telefono, email, provincia },
|
||||
reforma: {
|
||||
tipo: "cocina" | "bano" | "salon" | "comedor" | "integral" | "otro",
|
||||
m2Suelo: number,
|
||||
calidad: "basica" | "media" | "premium",
|
||||
estructural: boolean,
|
||||
urgencia: "alta" | "media" | "baja",
|
||||
presupuestoTarget: number // en céntimos
|
||||
},
|
||||
empresa: { tenantId: string, nombre: string },
|
||||
zonas: Array<{
|
||||
zona: string,
|
||||
notas: string[],
|
||||
fotos: { antes: string[], despues: string[] }
|
||||
}>
|
||||
}
|
||||
|
||||
## Validación
|
||||
Usar class-validator con decoradores en webhook.dto.ts.
|
||||
Si el payload no es válido → responder 400 con el error, no arrancar el pipeline.
|
||||
Reference in New Issue
Block a user