mission-control-v2 / agents /specialized.py_old
vfven's picture
Rename agents/specialized.py to agents/specialized.py_old
d440781 verified
Raw
History Blame
11.4 kB
# 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
}