Actualización 20250718
This commit is contained in:
@@ -1,12 +1,34 @@
|
||||
# ComfyUI BFL API Pro Nodes
|
||||
|
||||
Este nodo personalizado para ComfyUI proporciona integración con la API de BFL (Black Forest Labs) para mejorar y optimizar el procesamiento de imágenes. Permite utilizar Flux Pro, directamente dentro de ComfyUI, ofreciendo capacidades avanzadas de procesamiento de imágenes.
|
||||
Este nodo personalizado para ComfyUI proporciona integración completa con la API de BFL (Black Forest Labs) para mejorar y optimizar el procesamiento de imágenes. Permite utilizar todos los modelos de Flux Pro, Flux Kontext y Flux Ultra directamente dentro de ComfyUI, ofreciendo capacidades avanzadas de procesamiento de imágenes.
|
||||
|
||||
## Características
|
||||
|
||||
- Integración con la API de BFL
|
||||
- Procesamiento de imágenes optimizado
|
||||
- Compatible con el flujo de trabajo de ComfyUI
|
||||
- ✅ **Integración completa con la API de BFL**
|
||||
- ✅ **Múltiples modelos soportados**: Flux Pro, Flux Kontext, Flux Ultra
|
||||
- ✅ **ControlNet avanzado**: Canny, Depth
|
||||
- ✅ **Inpainting y expansión de imágenes**
|
||||
- ✅ **Sistema flexible de API keys** con prioridad automática
|
||||
- ✅ **Procesamiento de imágenes optimizado**
|
||||
- ✅ **Compatible con el flujo de trabajo de ComfyUI**
|
||||
- ✅ **Manejo robusto de errores**
|
||||
|
||||
## Nodos Disponibles
|
||||
|
||||
### 🎨 Generación de Imágenes
|
||||
- **BFL Image Generator (Pro 1.1/1.0)**: Generación básica de imágenes con soporte para versiones 1.0 y 1.1
|
||||
- **BFL Flux Ultra (Pro 1.1)**: Modelo Ultra para máxima calidad
|
||||
|
||||
### 🎭 ControlNet
|
||||
- **BFL Canny Control (Pro 1.0)**: Control de bordes con detección Canny
|
||||
- **BFL Depth Control (Pro 1.0)**: Control de profundidad
|
||||
|
||||
### 🔧 Edición y Manipulación
|
||||
- **BFL Inpainting (Pro 1.0 Fill)**: Rellenado inteligente de áreas
|
||||
- **BFL Image Expander (Pro 1.0)**: Expansión de imágenes con IA
|
||||
|
||||
### 🌟 Modelos Especializados
|
||||
- **BFL Flux Kontext (Pro/Max)**: Modelos Kontext Pro y Max para casos especiales
|
||||
|
||||
## Instalación
|
||||
|
||||
@@ -17,10 +39,9 @@ git clone https://github.com/charlyad142/ComfyUI_bfl_api_pro_nodes.git
|
||||
```
|
||||
3. Reinicia ComfyUI
|
||||
|
||||
## Configuración
|
||||
|
||||
Para utilizar este nodo, necesitas configurar tu API key de BFL. Sigue estos pasos:
|
||||
## Configuración de API Key
|
||||
|
||||
### Opción 1: Archivo config.ini (Recomendado)
|
||||
1. Crea un archivo `config.ini` en la raíz del nodo
|
||||
2. Agrega la siguiente configuración:
|
||||
|
||||
@@ -29,15 +50,136 @@ Para utilizar este nodo, necesitas configurar tu API key de BFL. Sigue estos pas
|
||||
X_KEY = tu_api_key_aquí
|
||||
```
|
||||
|
||||
### Opción 2: Campo x_key en cada nodo
|
||||
- Cada nodo tiene un campo `x_key` donde puedes ingresar tu API key directamente
|
||||
- Útil para usar diferentes keys en diferentes nodos
|
||||
|
||||
### Sistema de Prioridad de API Keys
|
||||
El sistema busca la API key en el siguiente orden:
|
||||
1. **Archivo config.ini** (prioridad más alta)
|
||||
2. **Campo x_key del nodo** (si config.ini no existe o no tiene key)
|
||||
3. **Error** (si no hay key en ningún lado)
|
||||
|
||||
### Obtención de API Key
|
||||
|
||||
1. Regístrate en [BFL Platform](https://auth.bfl.ai/)
|
||||
2. Ve a tu panel de control
|
||||
3. En la sección de API Keys, genera una nueva key
|
||||
4. Copia la key y pégala en tu archivo `config.ini`
|
||||
4. Copia la key y pégala en tu archivo `config.ini` o en el campo `x_key` del nodo
|
||||
|
||||
## Uso
|
||||
|
||||
### Básico
|
||||
1. En ComfyUI, busca los nodos "BFL" en el menú de nodos
|
||||
2. Arrastra el nodo deseado a tu flujo de trabajo
|
||||
3. Conecta los nodos según tus necesidades
|
||||
3. Configura tu API key (en config.ini o en el campo x_key)
|
||||
4. Conecta los nodos según tus necesidades
|
||||
|
||||
### Ejemplos de Uso
|
||||
|
||||
#### Generación de Imágenes
|
||||
```
|
||||
BFL Image Generator → Prompt → Output
|
||||
```
|
||||
|
||||
#### ControlNet con Canny
|
||||
```
|
||||
Input Image → BFL Canny Control → Output
|
||||
```
|
||||
|
||||
#### Inpainting
|
||||
```
|
||||
Image + Mask → BFL Inpainting → Output
|
||||
```
|
||||
|
||||
#### Expansión de Imágenes
|
||||
```
|
||||
Image → BFL Image Expander → Output
|
||||
```
|
||||
|
||||
## Parámetros Comunes
|
||||
|
||||
### Campos Requeridos
|
||||
- **prompt**: Descripción de la imagen a generar
|
||||
- **x_key**: Tu API key de BFL
|
||||
- **output_format**: Formato de salida (jpeg/png)
|
||||
- **safety_tolerance**: Tolerancia de seguridad (0-6)
|
||||
|
||||
### Campos Opcionales
|
||||
- **seed**: Semilla para reproducibilidad (-1 para aleatorio)
|
||||
- **steps**: Número de pasos de generación (15-50)
|
||||
- **guidance**: Guía de generación (1.0-100.0)
|
||||
|
||||
## Modelos Específicos
|
||||
|
||||
### Flux Pro 1.1/1.0
|
||||
- **Versión 1.1**: Sin parámetros de steps/guidance
|
||||
- **Versión 1.0**: Con parámetros de steps/guidance
|
||||
|
||||
### Flux Ultra
|
||||
- **Aspect Ratio**: Control de relación de aspecto
|
||||
- **Raw Mode**: Modo raw para mayor control
|
||||
- **Image Prompt**: Soporte para imágenes de referencia
|
||||
|
||||
### Flux Kontext
|
||||
- **Pro/Max**: Dos modelos especializados
|
||||
- **Aspect Ratio**: Control de relación de aspecto
|
||||
|
||||
### ControlNet
|
||||
- **Canny**: Detección de bordes con umbrales configurables
|
||||
- **Depth**: Control de profundidad
|
||||
|
||||
## Solución de Problemas
|
||||
|
||||
### Error de API Key
|
||||
```
|
||||
"No se encontró una API key válida. Por favor, configure una API key en el nodo o en el archivo config.ini"
|
||||
```
|
||||
**Solución**: Verifica que tu API key esté configurada correctamente en config.ini o en el campo x_key del nodo.
|
||||
|
||||
### Error de Conexión
|
||||
```
|
||||
"API Error 401: Unauthorized"
|
||||
```
|
||||
**Solución**: Verifica que tu API key sea válida y tenga créditos disponibles.
|
||||
|
||||
### Error de Formato
|
||||
```
|
||||
"Invalid API response format"
|
||||
```
|
||||
**Solución**: Verifica que todos los parámetros requeridos estén configurados correctamente.
|
||||
|
||||
## Contribuir
|
||||
|
||||
Las contribuciones son bienvenidas. Por favor:
|
||||
|
||||
1. Fork el repositorio
|
||||
2. Crea una rama para tu feature
|
||||
3. Commit tus cambios
|
||||
4. Push a la rama
|
||||
5. Abre un Pull Request
|
||||
|
||||
## Licencia
|
||||
|
||||
Este proyecto está bajo la Licencia MIT. Ver el archivo `LICENSE` para más detalles.
|
||||
|
||||
## Soporte
|
||||
|
||||
Para soporte técnico o preguntas:
|
||||
- Abre un issue en GitHub
|
||||
- Consulta la documentación de BFL: [BFL Platform](https://auth.bfl.ai/)
|
||||
|
||||
## Changelog
|
||||
|
||||
### v2.0.0
|
||||
- ✅ Agregado soporte para Flux Ultra
|
||||
- ✅ Agregado nodo de Depth Control
|
||||
- ✅ Agregado nodo de Flux Kontext
|
||||
- ✅ Sistema flexible de API keys con prioridad automática
|
||||
- ✅ Mejorado el manejo de errores
|
||||
- ✅ Eliminados campos de webhook innecesarios
|
||||
|
||||
### v1.0.0
|
||||
- ✅ Nodos básicos de generación, inpainting y control
|
||||
- ✅ Soporte para config.ini
|
||||
- ✅ Integración con ComfyUI
|
||||
|
||||
+174
@@ -132,6 +132,7 @@ class BFL_CannyControl:
|
||||
"optional": {
|
||||
"preprocessed_image": ("IMAGE",),
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"prompt_upsampling": ("BOOLEAN", {"default": False}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +154,7 @@ class BFL_CannyControl:
|
||||
"output_format": kwargs['output_format'],
|
||||
"safety_tolerance": kwargs['safety_tolerance'],
|
||||
"seed": kwargs['seed'] if kwargs['seed'] != -1 else None,
|
||||
"prompt_upsampling": kwargs.get('prompt_upsampling', False),
|
||||
}
|
||||
|
||||
if kwargs.get('preprocessed_image'):
|
||||
@@ -230,11 +232,180 @@ class BFL_ImageExpander:
|
||||
print(f"BFL Expansion Error: {str(e)}")
|
||||
return (create_error_image(),)
|
||||
|
||||
class BFL_FluxKontext:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"prompt": ("STRING", {"multiline": True}),
|
||||
"model": (["flux-kontext-pro", "flux-kontext-max"], {"default": "flux-kontext-pro"}),
|
||||
"output_format": (["jpeg", "png"], {"default": "png"}),
|
||||
"safety_tolerance": ("INT", {"default": 2, "min": 0, "max": 6}),
|
||||
"x_key": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"input_image": ("IMAGE",),
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"aspect_ratio": ("STRING", {"default": ""}),
|
||||
"prompt_upsampling": ("BOOLEAN", {"default": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "generate"
|
||||
CATEGORY = "BFL/Kontext"
|
||||
|
||||
def generate(self, **kwargs):
|
||||
try:
|
||||
config = BFLConfigLoader()
|
||||
|
||||
payload = {
|
||||
"prompt": kwargs['prompt'],
|
||||
"output_format": kwargs['output_format'],
|
||||
"safety_tolerance": kwargs['safety_tolerance'],
|
||||
"seed": kwargs['seed'] if kwargs['seed'] != -1 else None,
|
||||
"prompt_upsampling": kwargs.get('prompt_upsampling', False),
|
||||
}
|
||||
|
||||
# Agregar campos opcionales solo si tienen valor
|
||||
if kwargs.get('input_image') is not None:
|
||||
payload['input_image'] = image_to_base64(kwargs['input_image'])
|
||||
|
||||
if kwargs.get('aspect_ratio') and kwargs['aspect_ratio'].strip():
|
||||
payload['aspect_ratio'] = kwargs['aspect_ratio']
|
||||
|
||||
response = requests.post(
|
||||
url=f"https://api.us1.bfl.ai/v1/{kwargs['model']}",
|
||||
headers={"x-key": config.get_api_key(kwargs['x_key']), "Content-Type": "application/json"},
|
||||
json={k: v for k, v in payload.items() if v is not None},
|
||||
timeout=30
|
||||
)
|
||||
|
||||
return (handle_api_response(response, kwargs['output_format'], kwargs['x_key']),)
|
||||
|
||||
except Exception as e:
|
||||
print(f"BFL Flux Kontext Error: {str(e)}")
|
||||
return (create_error_image(),)
|
||||
|
||||
class BFL_DepthControl:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"prompt": ("STRING", {"multiline": True}),
|
||||
"control_image": ("IMAGE",),
|
||||
"steps": ("INT", {"default": 50, "min": 15, "max": 50}),
|
||||
"guidance": ("FLOAT", {"default": 15.0, "min": 1.0, "max": 100.0}),
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"}),
|
||||
"safety_tolerance": ("INT", {"default": 2, "min": 0, "max": 6}),
|
||||
"x_key": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"preprocessed_image": ("IMAGE",),
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"prompt_upsampling": ("BOOLEAN", {"default": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "generate"
|
||||
CATEGORY = "BFL/ControlNet"
|
||||
|
||||
def generate(self, **kwargs):
|
||||
try:
|
||||
config = BFLConfigLoader()
|
||||
|
||||
payload = {
|
||||
"prompt": kwargs['prompt'],
|
||||
"control_image": image_to_base64(kwargs['control_image']),
|
||||
"steps": kwargs['steps'],
|
||||
"guidance": kwargs['guidance'],
|
||||
"output_format": kwargs['output_format'],
|
||||
"safety_tolerance": kwargs['safety_tolerance'],
|
||||
"seed": kwargs['seed'] if kwargs['seed'] != -1 else None,
|
||||
"prompt_upsampling": kwargs.get('prompt_upsampling', False),
|
||||
}
|
||||
|
||||
if kwargs.get('preprocessed_image'):
|
||||
payload['preprocessed_image'] = image_to_base64(kwargs['preprocessed_image'])
|
||||
|
||||
response = requests.post(
|
||||
url="https://api.us1.bfl.ai/v1/flux-pro-1.0-depth",
|
||||
headers={"x-key": config.get_api_key(kwargs['x_key']), "Content-Type": "application/json"},
|
||||
json=payload,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
return (handle_api_response(response, kwargs['output_format'], kwargs['x_key']),)
|
||||
|
||||
except Exception as e:
|
||||
print(f"BFL Depth Error: {str(e)}")
|
||||
return (create_error_image(),)
|
||||
|
||||
class BFL_FluxUltra:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"prompt": ("STRING", {"multiline": True}),
|
||||
"aspect_ratio": ("STRING", {"default": "16:9"}),
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"}),
|
||||
"safety_tolerance": ("INT", {"default": 2, "min": 0, "max": 6}),
|
||||
"x_key": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"prompt_upsampling": ("BOOLEAN", {"default": False}),
|
||||
"raw": ("BOOLEAN", {"default": False}),
|
||||
"image_prompt": ("IMAGE",),
|
||||
"image_prompt_strength": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "generate"
|
||||
CATEGORY = "BFL/Ultra"
|
||||
|
||||
def generate(self, **kwargs):
|
||||
try:
|
||||
config = BFLConfigLoader()
|
||||
|
||||
payload = {
|
||||
"prompt": kwargs['prompt'],
|
||||
"aspect_ratio": kwargs['aspect_ratio'],
|
||||
"output_format": kwargs['output_format'],
|
||||
"safety_tolerance": kwargs['safety_tolerance'],
|
||||
"seed": kwargs['seed'] if kwargs['seed'] != -1 else None,
|
||||
"prompt_upsampling": kwargs.get('prompt_upsampling', False),
|
||||
"raw": kwargs.get('raw', False),
|
||||
}
|
||||
|
||||
# Agregar image_prompt solo si se proporciona
|
||||
if kwargs.get('image_prompt') is not None:
|
||||
payload['image_prompt'] = image_to_base64(kwargs['image_prompt'])
|
||||
payload['image_prompt_strength'] = kwargs.get('image_prompt_strength', 0.1)
|
||||
|
||||
response = requests.post(
|
||||
url="https://api.us1.bfl.ai/v1/flux-pro-1.1-ultra",
|
||||
headers={"x-key": config.get_api_key(kwargs['x_key']), "Content-Type": "application/json"},
|
||||
json={k: v for k, v in payload.items() if v is not None},
|
||||
timeout=30
|
||||
)
|
||||
|
||||
return (handle_api_response(response, kwargs['output_format'], kwargs['x_key']),)
|
||||
|
||||
except Exception as e:
|
||||
print(f"BFL Flux Ultra Error: {str(e)}")
|
||||
return (create_error_image(),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"BFL Image Generator": BFL_ImageGenerator,
|
||||
"BFL Inpainting": BFL_Inpainting,
|
||||
"BFL Canny Control": BFL_CannyControl,
|
||||
"BFL Image Expander": BFL_ImageExpander,
|
||||
"BFL Flux Kontext": BFL_FluxKontext,
|
||||
"BFL Depth Control": BFL_DepthControl,
|
||||
"BFL Flux Ultra": BFL_FluxUltra,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -242,4 +413,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"BFL Inpainting": "BFL Inpainting (Pro 1.0 Fill)",
|
||||
"BFL Canny Control": "BFL Canny Control (Pro 1.0)",
|
||||
"BFL Image Expander": "BFL Image Expander (Pro 1.0)",
|
||||
"BFL Flux Kontext": "BFL Flux Kontext (Pro/Max)",
|
||||
"BFL Depth Control": "BFL Depth Control (Pro 1.0)",
|
||||
"BFL Flux Ultra": "BFL Flux Ultra (Pro 1.1)",
|
||||
}
|
||||
+110
-13
@@ -55,17 +55,53 @@ def mask_to_base64(mask_tensor, format='PNG'):
|
||||
return base64.b64encode(buffered.getvalue()).decode('utf-8')
|
||||
|
||||
def handle_api_response(response, output_format, node_api_key=None):
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"API Error {response.status_code}: {response.text}")
|
||||
# Manejar diferentes códigos de estado HTTP
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
result = response.json()
|
||||
|
||||
if 'id' not in result:
|
||||
raise Exception("Invalid API response format: missing task ID")
|
||||
|
||||
return poll_task_result(result['id'], output_format, node_api_key=node_api_key)
|
||||
|
||||
except ValueError as e:
|
||||
raise Exception(f"Invalid JSON response from API: {str(e)}")
|
||||
|
||||
result = response.json()
|
||||
elif response.status_code == 422:
|
||||
try:
|
||||
error_data = response.json()
|
||||
detail = error_data.get('detail', 'Unknown validation error')
|
||||
if isinstance(detail, list) and len(detail) > 0:
|
||||
error_msg = detail[0].get('msg', 'Validation error')
|
||||
else:
|
||||
error_msg = str(detail)
|
||||
raise Exception(f"Validation Error: {error_msg}")
|
||||
except ValueError:
|
||||
raise Exception(f"Validation Error: {response.text}")
|
||||
|
||||
if 'id' not in result:
|
||||
raise Exception("Invalid API response format")
|
||||
elif response.status_code == 401:
|
||||
raise Exception("Unauthorized: Please check your API key")
|
||||
|
||||
return poll_task_result(result['id'], output_format, node_api_key=node_api_key)
|
||||
elif response.status_code == 403:
|
||||
raise Exception("Forbidden: Insufficient permissions or quota exceeded")
|
||||
|
||||
elif response.status_code == 429:
|
||||
raise Exception("Rate limit exceeded: Please wait before making another request")
|
||||
|
||||
elif response.status_code >= 500:
|
||||
raise Exception(f"Server Error ({response.status_code}): Please try again later")
|
||||
|
||||
else:
|
||||
try:
|
||||
error_data = response.json()
|
||||
error_msg = error_data.get('error', error_data.get('message', response.text))
|
||||
except ValueError:
|
||||
error_msg = response.text
|
||||
|
||||
raise Exception(f"API Error {response.status_code}: {error_msg}")
|
||||
|
||||
def poll_task_result(task_id, output_format, max_attempts=15, node_api_key=None):
|
||||
def poll_task_result(task_id, output_format, max_attempts=10, node_api_key=None):
|
||||
config = BFLConfigLoader()
|
||||
base_url = "https://api.us1.bfl.ai/v1/"
|
||||
|
||||
@@ -77,11 +113,27 @@ def poll_task_result(task_id, output_format, max_attempts=15, node_api_key=None)
|
||||
timeout=30
|
||||
)
|
||||
|
||||
# Manejar errores HTTP
|
||||
if response.status_code == 422:
|
||||
error_data = response.json()
|
||||
raise Exception(f"Validation Error: {error_data.get('detail', 'Unknown validation error')}")
|
||||
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
if data['status'] == 'Ready':
|
||||
# Verificar que tenemos los campos requeridos
|
||||
if 'status' not in data:
|
||||
raise Exception("Invalid API response: missing status field")
|
||||
|
||||
status = data['status']
|
||||
|
||||
# Manejar diferentes estados según la documentación
|
||||
if status == 'Ready':
|
||||
if 'result' not in data or not data['result'] or 'sample' not in data['result']:
|
||||
raise Exception("Task completed but no result available")
|
||||
|
||||
img_response = requests.get(data['result']['sample'])
|
||||
img_response.raise_for_status()
|
||||
img = Image.open(io.BytesIO(img_response.content))
|
||||
|
||||
with io.BytesIO() as buffer:
|
||||
@@ -90,14 +142,59 @@ def poll_task_result(task_id, output_format, max_attempts=15, node_api_key=None)
|
||||
img_array = np.array(Image.open(buffer)).astype(np.float32) / 255.0
|
||||
return torch.from_numpy(img_array)[None,]
|
||||
|
||||
time.sleep(2 ** attempt + 5)
|
||||
elif status == 'Error':
|
||||
error_msg = "Task failed"
|
||||
if 'details' in data and data['details']:
|
||||
error_msg = f"Task failed: {data['details']}"
|
||||
raise Exception(error_msg)
|
||||
|
||||
elif status == 'Task not found':
|
||||
raise Exception(f"Task not found: {task_id}")
|
||||
|
||||
elif status == 'Request Moderated':
|
||||
raise Exception("Request was moderated by content filters")
|
||||
|
||||
elif status == 'Content Moderated':
|
||||
raise Exception("Generated content was moderated by safety filters")
|
||||
|
||||
elif status == 'Pending':
|
||||
# Tarea en progreso, continuar polling
|
||||
progress = data.get('progress', 0)
|
||||
print(f"Task {task_id} in progress: {progress}% (attempt {attempt + 1}/{max_attempts})")
|
||||
|
||||
# Tiempo de espera más razonable: máximo 60 segundos por intento
|
||||
wait_time = min(2 ** attempt + 5, 60)
|
||||
time.sleep(wait_time)
|
||||
continue
|
||||
|
||||
else:
|
||||
# Estado desconocido
|
||||
print(f"Unknown status '{status}' for task {task_id}, retrying...")
|
||||
wait_time = min(2 ** attempt + 5, 60)
|
||||
time.sleep(wait_time)
|
||||
continue
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print(f"Timeout on attempt {attempt + 1}/{max_attempts} for task {task_id}")
|
||||
if attempt == max_attempts - 1:
|
||||
raise Exception(f"Request timeout after {max_attempts} attempts")
|
||||
wait_time = min(2 ** attempt + 5, 60)
|
||||
time.sleep(wait_time)
|
||||
continue
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Request error on attempt {attempt + 1}/{max_attempts}: {str(e)}")
|
||||
if attempt == max_attempts - 1:
|
||||
raise Exception(f"Request failed after {max_attempts} attempts: {str(e)}")
|
||||
wait_time = min(2 ** attempt + 5, 60)
|
||||
time.sleep(wait_time)
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
if attempt == max_attempts - 1:
|
||||
raise
|
||||
continue
|
||||
# Para otros errores (parsing JSON, etc.), lanzar inmediatamente
|
||||
raise Exception(f"Error processing task {task_id}: {str(e)}")
|
||||
|
||||
raise Exception("Max polling attempts reached")
|
||||
raise Exception(f"Max polling attempts ({max_attempts}) reached for task {task_id}")
|
||||
|
||||
def create_error_image():
|
||||
blank = Image.new('RGB', (512, 512), color='red')
|
||||
|
||||
Reference in New Issue
Block a user