Spaces:
Running
Running
File size: 11,355 Bytes
8528be4 0521784 8528be4 01c4dd9 a44fa84 5c2c5e3 a05bdda 9cba0d4 aa93b8f 5c2c5e3 8528be4 6829277 a6139d5 5c2c5e3 a6139d5 5c2c5e3 a6139d5 5c2c5e3 6829277 a05bdda 6829277 a05bdda 6829277 a44fa84 5a8692e a44fa84 5a8692e a44fa84 5a8692e 6829277 7fb15fa a05bdda 7fb15fa a05bdda d5f8a1c a05bdda 7fb15fa 054beb9 3b65bfd 054beb9 aa93b8f 3b65bfd 054beb9 7fb15fa 922c047 7fb15fa 11fa61a 7fb15fa 11fa61a 7fb15fa a44fa84 6829277 a44fa84 8528be4 a44fa84 8528be4 6829277 a44fa84 8528be4 6829277 a44fa84 8528be4 a44fa84 5c2c5e3 5c7fb94 a05bdda 8528be4 a44fa84 a05bdda a44fa84 347bfd4 054beb9 347bfd4 b42541c 347bfd4 a05bdda 347bfd4 b42541c a05bdda b42541c 054beb9 b42541c 054beb9 b42541c 054beb9 b42541c 054beb9 b42541c 054beb9 b42541c 054beb9 b42541c 054beb9 b42541c 054beb9 b42541c 5c2c5e3 054beb9 b42541c a05bdda 347bfd4 a44fa84 8528be4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | # agents/specialized.py
import os
from core.llm_client import llm
from utils.prompts import get_today_prefix, get_chat_style
from agents.registry import AGENT_REGISTRY
from core.file_builder import build_docx, build_excel
import re
import json
import asyncio
import requests
import base64
from pathlib import Path
from datetime import datetime
AGENT_ROLES = {
"manager": """
Eres el gerente principal. Cuando no haya delegación o la tarea sea simple, responde de forma natural, amigable y útil al usuario.
No necesitas generar JSON aquí, solo contesta directamente.
Mantén un tono profesional pero cercano.
""",
"image_agent": """
Eres el agente de imágenes.
Cuando se te pida generar, crear, mostrar o visualizar una imagen, responde SOLO con un JSON:
{"image_queries": ["término en inglés 1", "término en inglés 2", "término en inglés 3"]}
Los términos deben ser específicos, detallados y en inglés para obtener mejores resultados.
Si la tarea no es sobre imágenes, responde: {"skip":"no image task"}
""",
"backend_dev": """
Eres programador backend senior.
REGLAS ABSOLUTAS:
1. Entrega SOLO el código pedido, sin explicaciones innecesarias.
2. Para excel/planilla → responde con EXCEL_TEMPLATE:{"title":"...","sheet_name":"...","headers":[...],"sample_rows":[[...],...]}
3. Python → entrega código Python puro y funcional.
4. Groovy/Jenkins → entrega el script completo.
5. Si hay frontend_dev en el equipo, TÚ haces servidor/backend, él hace HTML.
6. Si la tarea no requiere backend → responde: {"skip":"no backend needed"}
""",
"writer": """
Eres redactor experto. Escribe SOLO contenido real y extenso (500+ palabras).
Sin placeholders. Usa ## para secciones y ### para subsecciones.
Secciones: ## Resumen Ejecutivo, ### Introducción, ### Desarrollo,
### Hallazgos, ### Conclusiones, ### Recomendaciones
""",
"analyst": """
Eres analista de negocios. REGLAS:
1. Solo haz lo que el manager delegó: revisar documentos, evaluar viabilidad, analizar riesgos.
2. NUNCA describas imágenes ni hagas trabajo de otros agentes.
3. Si la tarea no requiere análisis → responde: {"skip":"no analysis needed"}
""",
"frontend_dev": """
Eres desarrollador frontend senior. REGLAS ABSOLUTAS:
1. Entrega SOLO código HTML/CSS/JS pedido, sin explicaciones innecesarias.
2. Si hay backend_dev, TÚ haces HTML/interfaz, él hace servidor/lógica.
3. Si la tarea NO requiere frontend → responde: {"skip":"no frontend needed"}
4. Entrega siempre HTML completo y funcional con los estilos incluidos.
"""
}
"""
async def generate_real_image(prompts: list, model: str = "black-forest-labs/FLUX.1-schnell"):
Genera una o más imágenes reales usando HF Inference API
results = []
for i, prompt in enumerate(prompts[:3]): # límite de 3 imágenes por seguridad
try:
from huggingface_hub import InferenceClient
client = InferenceClient(model=model, token=os.getenv("HF_API_TOKEN"))
enhanced_prompt = f"{prompt}, highly detailed, cinematic lighting, 8k, masterpiece, best quality"
image = await asyncio.to_thread(
client.text_to_image,
enhanced_prompt,
num_inference_steps=20,
guidance_scale=7.5,
height=768,
width=768
)
DOCS_DIR = Path("data/docs")
DOCS_DIR.mkdir(exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
file_name = f"generated_image_{ts}_{i+1}.png"
file_path = DOCS_DIR / file_name
image.save(file_path)
results.append({
"success": True,
"image_url": f"/docs/{file_name}",
"prompt_used": enhanced_prompt
})
except Exception as e:
results.append({"success": False, "error": str(e)})
return results
"""
# 🔵 FUNCIONES AUXILIARES (AQUÍ VAN)
async def generate_with_gemini(queries, model_name):
# ⚠️ por ahora fallback
return []
async def generate_with_hf(queries):
urls = []
for i, prompt in enumerate(queries[:2]):
response = requests.post(
"https://router.huggingface.co/v1/images/generations",
headers={
"Authorization": f"Bearer {os.getenv('HF_API_TOKEN')}"
},
json={
"model": "black-forest-labs/FLUX.1-schnell",
"prompt": prompt,
"size": "1024x1024"
}
)
if response.status_code == 200:
data = response.json()
image_base64 = data["data"][0]["b64_json"]
image_bytes = base64.b64decode(image_base64)
DOCS_DIR = Path("data/docs")
DOCS_DIR.mkdir(exist_ok=True)
file_name = f"hf_image_{datetime.now().timestamp()}.png"
file_path = DOCS_DIR / file_name
with open(file_path, "wb") as f:
f.write(image_bytes)
urls.append(f"/docs/{file_name}")
else:
print("HF error:", response.text)
return urls
# 🟢 FUNCIÓN PRINCIPAL
async def generate_real_image(prompts: list):
results = []
for prompt in prompts[:3]:
try:
response = requests.post(
"https://router.huggingface.co/v1/images/generations",
headers={
"Authorization": f"Bearer {os.getenv('HF_API_TOKEN')}",
"Content-Type": "application/json"
},
json={
"model": "black-forest-labs/FLUX.1-schnell",
"prompt": prompt,
"size": "1024x1024"
}
)
# 🔥 DEBUG
print("STATUS:", response.status_code)
print("RAW:", response.text[:200])
if response.status_code != 200:
raise Exception(f"HTTP {response.status_code}: {response.text}")
# intentar parsear JSON
try:
data = response.json()
except:
raise Exception("Respuesta no es JSON válida")
if "data" not in data:
raise Exception(f"Formato inesperado: {data}")
image_url = data["data"][0].get("url")
if not image_url:
raise Exception("No se encontró URL de imagen")
results.append({
"success": True,
"image_url": image_url
})
except Exception as e:
results.append({
"success": False,
"error": str(e)
})
return results
async def run_specialized_agent(agent_key: str, task: str, context: dict = None) -> dict:
if agent_key not in AGENT_ROLES:
return {"error": f"Agente {agent_key} no implementado aún", "success": False}
agent = AGENT_REGISTRY.get(agent_key, {})
today = get_today_prefix()
role = AGENT_ROLES[agent_key]
full_prompt = today + role + get_chat_style() + f"\n\nTarea asignada: {task}"
if context:
full_prompt += "\n\nContexto de otros agentes:\n" + "\n".join([f"[{k.upper()}] {v[:300]}..." for k, v in context.items()])
messages = [{"role": "user", "content": full_prompt}]
try:
response = await llm.call(agent_key, messages, temperature=0.45)
result = {
"agent": agent_key,
"response": response,
"success": True,
"file_path": None,
"file_type": None,
"image_urls": [], # ahora lista para múltiples imágenes
"queries": None
}
# Generar archivo según agente
if agent_key == "writer" and len(response.strip()) > 300:
title = task[:60].strip('"') or "Informe generado"
try:
file_path = build_docx(title, response, images=None)
if file_path:
result["file_path"] = file_path.name
result["file_type"] = "docx"
except Exception as e:
result["file_error"] = str(e)
elif agent_key == "backend_dev":
match = re.search(r'EXCEL_TEMPLATE:\s*(\{.*\})', response, re.DOTALL | re.IGNORECASE)
if match:
try:
excel_data = json.loads(match.group(1))
file_path = build_excel(task, excel_data)
if file_path:
result["file_path"] = file_path.name
result["file_type"] = "xlsx"
result["response"] = response.split("EXCEL_TEMPLATE:")[0].strip()
except Exception as e:
result["file_error"] = str(e)
elif agent_key == "image_agent":
agent_config = AGENT_REGISTRY.get(agent_key, {})
provider = agent_config.get("provider")
model_name = agent_config.get("models", [None])[0]
print("USANDO PROVIDER:", provider)
print("USANDO MODELO:", model_name)
match = re.search(r'\{.*"image_queries".*\}', response, re.DOTALL)
if match:
try:
data = json.loads(match.group(0))
queries = data.get("image_queries", [])
result["queries"] = queries
# 🧠 respuesta base
result["response"] = "Ideas de imágenes generadas:\n" + "\n".join(queries)
image_urls = []
# 🔥 intento 1: provider principal (registry)
try:
if provider == "gemini":
image_urls = await generate_with_gemini(queries, model_name)
elif provider == "huggingface":
image_urls = await generate_with_hf(queries)
except Exception as e:
print("Provider principal falló:", str(e))
# 🔥 fallback automático
if not image_urls:
try:
print("Intentando fallback HF...")
image_urls = await generate_with_hf(queries)
except Exception as e:
print("Fallback falló:", str(e))
# 🔥 fallback final visual
if not image_urls:
image_urls = ["https://picsum.photos/400/300"]
result["image_urls"] = image_urls
if image_urls:
result["response"] += "\n\nImagen generada ✔"
else:
result["response"] += "\n\n⚠️ No se pudo generar imagen"
except Exception as e:
result["response"] = f"Error procesando JSON: {str(e)}"
else:
result["response"] = "No se detectaron instrucciones claras para imágenes."
return result
except Exception as e:
return {
"agent": agent_key,
"error": str(e),
"success": False
} |