proyecto generalizado
This commit is contained in:
@@ -26,6 +26,6 @@ EXPOSE 8501
|
||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s --retries=3 \
|
||||
CMD curl -fsS http://localhost:8501/_stcore/health || exit 1
|
||||
|
||||
CMD ["streamlit", "run", "/app/src/agentforge_dashboard/app.py", \
|
||||
CMD ["streamlit", "run", "/app/src/forja_dashboard/app.py", \
|
||||
"--server.address=0.0.0.0", "--server.port=8501", \
|
||||
"--server.headless=true", "--browser.gatherUsageStats=false"]
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
"""Entry point del dashboard Streamlit con sidebar de branding y health."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from agentforge_dashboard.client import CoreClient
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
def get_client() -> CoreClient:
|
||||
return CoreClient()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
st.set_page_config(
|
||||
page_title="AgentForge",
|
||||
page_icon="🛡️",
|
||||
layout="wide",
|
||||
initial_sidebar_state="expanded",
|
||||
)
|
||||
st.sidebar.markdown("## 🛡️ AgentForge")
|
||||
st.sidebar.caption("Plataforma de gobernanza de agentes IA")
|
||||
client = get_client()
|
||||
try:
|
||||
client.health()
|
||||
st.sidebar.success("Core API: OK")
|
||||
except Exception as exc:
|
||||
st.sidebar.error(f"Core API unreachable: {exc}")
|
||||
|
||||
st.title("🛡️ AgentForge")
|
||||
st.markdown(
|
||||
"""
|
||||
Bienvenido al panel de gobernanza de agentes IA.
|
||||
|
||||
Usa la barra lateral para navegar:
|
||||
- **Registro**: catálogo de agentes y versiones.
|
||||
- **Ejecutar**: lanzar un agente con guardrails completos.
|
||||
- **Aprobaciones**: ejecuciones pausadas a la espera de revisión humana.
|
||||
- **Historial**: trazas, violaciones y resultados.
|
||||
- **Politicas**: políticas de guardrails y diff entre versiones.
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,47 @@
|
||||
"""Entry point del dashboard Streamlit con sidebar de branding y health."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from forja_dashboard.client import CoreClient
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
def get_client() -> CoreClient:
|
||||
return CoreClient()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
st.set_page_config(
|
||||
page_title="Forja",
|
||||
page_icon="🔨",
|
||||
layout="wide",
|
||||
initial_sidebar_state="expanded",
|
||||
)
|
||||
st.sidebar.markdown("## 🔨 Forja")
|
||||
st.sidebar.caption("Forja de agentes IA — gobernanza para cualquier proyecto")
|
||||
client = get_client()
|
||||
try:
|
||||
client.health()
|
||||
st.sidebar.success("Core API: OK")
|
||||
except Exception as exc:
|
||||
st.sidebar.error(f"Core API unreachable: {exc}")
|
||||
|
||||
st.title("🔨 Forja")
|
||||
st.markdown(
|
||||
"""
|
||||
Bienvenido a la forja de agentes IA.
|
||||
|
||||
Navega por la barra lateral:
|
||||
- **Registro**: catálogo y versiones.
|
||||
- **Ejecutar**: invocar con gobierno completo.
|
||||
- **Aprobaciones** / **Historial**.
|
||||
- **Politicas**.
|
||||
- **🔨 Forjar Agente** y **🛡️ Forjar Politica**: editores gráficos (nuevo).
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+12
-1
@@ -12,7 +12,7 @@ class CoreClient:
|
||||
"""Cliente sincrono (Streamlit es sync). 2 retries con httpx Transport."""
|
||||
|
||||
def __init__(self, base_url: str | None = None, timeout: float = 30.0) -> None:
|
||||
self.base_url = base_url or os.getenv("AGENTFORGE_CORE_URL", "http://localhost:8000")
|
||||
self.base_url = base_url or os.getenv("FORJA_CORE_URL", "http://localhost:8000")
|
||||
transport = httpx.HTTPTransport(retries=2)
|
||||
self._client = httpx.Client(base_url=self.base_url, timeout=timeout, transport=transport)
|
||||
|
||||
@@ -56,6 +56,17 @@ class CoreClient:
|
||||
def list_policy_versions(self, name: str) -> list[dict]:
|
||||
return self._get(f"/policies/{name}/versions")
|
||||
|
||||
def create_agent_version(self, name: str, agent_def: dict, message: str, author: str = "dashboard") -> dict:
|
||||
body = {"agent": agent_def, "message": message, "author": author}
|
||||
return self._post(f"/agents/{name}/versions", body)
|
||||
|
||||
def create_policy_version(self, name: str, policy_def: dict, message: str, author: str = "dashboard") -> dict:
|
||||
body = {"policy": policy_def, "message": message, "author": author}
|
||||
return self._post(f"/policies/{name}/versions", body)
|
||||
|
||||
def list_validators(self) -> dict:
|
||||
return self._get("/policies/validators")
|
||||
|
||||
# ----- helpers privados -----
|
||||
|
||||
def _get(self, path: str, params: dict | None = None) -> Any:
|
||||
+3
-2
@@ -4,8 +4,8 @@ from __future__ import annotations
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from agentforge_dashboard.client import CoreClient
|
||||
from agentforge_dashboard.components.diff_view import render_unified_diff
|
||||
from forja_dashboard.client import CoreClient
|
||||
from forja_dashboard.components.diff_view import render_unified_diff
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
@@ -16,6 +16,7 @@ def get_client() -> CoreClient:
|
||||
client = get_client()
|
||||
st.title("🏛️ Registro de Agentes")
|
||||
st.caption("Catálogo central de agentes con versionado tipo Git.")
|
||||
st.info("Usa **🔨 Forjar Agente** (barra lateral) para crear o versionar gráficamente.")
|
||||
|
||||
agents = client.list_agents()
|
||||
if not agents:
|
||||
+7
-9
@@ -6,9 +6,9 @@ from pathlib import Path
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from agentforge_dashboard.client import CoreClient
|
||||
from agentforge_dashboard.components.trace_view import render_trace
|
||||
from agentforge_dashboard.components.violation_view import render_violations
|
||||
from forja_dashboard.client import CoreClient
|
||||
from forja_dashboard.components.trace_view import render_trace
|
||||
from forja_dashboard.components.violation_view import render_violations
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
@@ -27,6 +27,7 @@ if not agents:
|
||||
|
||||
names = [a["name"] for a in agents]
|
||||
agent_name = st.selectbox("Agente", names)
|
||||
agent = client.get_agent(agent_name)
|
||||
|
||||
# Cargar escenarios pregrabados si existen en el disco montado.
|
||||
examples_dir = Path("agents") / agent_name / "examples"
|
||||
@@ -40,12 +41,9 @@ with col_r:
|
||||
st.session_state["input_text"] = ef.read_text(encoding="utf-8")
|
||||
|
||||
with col_l:
|
||||
user_input = st.text_area(
|
||||
"Descripción del incidente",
|
||||
height=240,
|
||||
key="input_text",
|
||||
placeholder="Pega aquí la descripción del incidente o usa un escenario...",
|
||||
)
|
||||
label = agent.get("input_label") or "Input para el agente"
|
||||
ph = agent.get("input_placeholder") or "Escribe el input para el agente o usa un escenario..."
|
||||
user_input = st.text_area(label, height=240, key="input_text", placeholder=ph)
|
||||
|
||||
if st.button("🚀 Invocar agente", type="primary", disabled=not user_input):
|
||||
with st.spinner("Ejecutando..."):
|
||||
+1
-1
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from agentforge_dashboard.client import CoreClient
|
||||
from forja_dashboard.client import CoreClient
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
+3
-3
@@ -4,9 +4,9 @@ from __future__ import annotations
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from agentforge_dashboard.client import CoreClient
|
||||
from agentforge_dashboard.components.trace_view import render_trace
|
||||
from agentforge_dashboard.components.violation_view import render_violations
|
||||
from forja_dashboard.client import CoreClient
|
||||
from forja_dashboard.components.trace_view import render_trace
|
||||
from forja_dashboard.components.violation_view import render_violations
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
+2
-1
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from agentforge_dashboard.client import CoreClient
|
||||
from forja_dashboard.client import CoreClient
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
@@ -15,6 +15,7 @@ def get_client() -> CoreClient:
|
||||
client = get_client()
|
||||
st.title("📐 Politicas de guardrails")
|
||||
st.caption("Inventario de políticas y sus versiones (estilo Git).")
|
||||
st.info("Usa **🛡️ Forjar Politica** (barra lateral) para componer y versionar gráficamente.")
|
||||
|
||||
policies = client.list_policies()
|
||||
if not policies:
|
||||
@@ -0,0 +1,115 @@
|
||||
"""Página de editor gráfico para crear o versionar agentes (Forja)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from forja_dashboard.client import CoreClient
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
def get_client() -> CoreClient:
|
||||
return CoreClient()
|
||||
|
||||
|
||||
client = get_client()
|
||||
|
||||
st.title("🔨 Forjar Agente")
|
||||
st.caption("Editor gráfico. Crea un agente nuevo o una nueva versión de uno existente. Todo se guarda como YAML versionado.")
|
||||
|
||||
agents = client.list_agents()
|
||||
agent_names = [a["name"] for a in agents] if agents else []
|
||||
mode = st.radio("Modo", ["Nuevo agente", "Nueva versión de existente"], horizontal=True)
|
||||
|
||||
base_name = None
|
||||
if mode == "Nueva versión de existente" and agent_names:
|
||||
base_name = st.selectbox("Agente base", agent_names)
|
||||
elif mode == "Nueva versión de existente":
|
||||
st.warning("No hay agentes. Crea uno nuevo.")
|
||||
st.stop()
|
||||
|
||||
with st.form("forge_agent", clear_on_submit=False):
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
name = st.text_input("Nombre del agente", value=base_name or "mi-nuevo-agente")
|
||||
version = st.text_input("Versión (ej. v1)", value="v1")
|
||||
owner = st.text_input("Owner", value="usuario")
|
||||
state = st.selectbox("Estado", ["draft", "active", "deprecated"], index=1)
|
||||
with col2:
|
||||
purpose = st.text_area("Propósito / descripción", value="Agente gobernado por Forja", height=80)
|
||||
category = st.text_input("Categoría (opcional)", value="")
|
||||
tags_str = st.text_input("Tags (coma separadas)", value="")
|
||||
icon = st.text_input("Icono emoji (opcional)", value="🤖")
|
||||
|
||||
st.subheader("LLM")
|
||||
llm_col1, llm_col2 = st.columns(4)
|
||||
with llm_col1:
|
||||
provider = st.selectbox("Provider", ["mock", "azure", "openai"], index=0)
|
||||
with llm_col2:
|
||||
model = st.text_input("Modelo", value="gpt-4o")
|
||||
with llm_col3:
|
||||
temperature = st.number_input("Temperature", 0.0, 2.0, 0.2, 0.1)
|
||||
with llm_col4:
|
||||
max_tokens = st.number_input("Max tokens", 1, 128000, 2000)
|
||||
|
||||
st.subheader("Prompt y Schema")
|
||||
system_prompt = st.text_area("System prompt", value="Eres un asistente útil. Responde en JSON según el schema.", height=160)
|
||||
schema_text = st.text_area("Output schema (JSON)", value=json.dumps({"type": "object", "properties": {"result": {"type": "string"}}}, indent=2), height=120)
|
||||
|
||||
st.subheader("Guardrails y HITL")
|
||||
policies = client.list_policies()
|
||||
policy_names = [p["name"] for p in policies] if policies else ["default"]
|
||||
guardrails = st.multiselect("Políticas de guardrails activas", policy_names, default=policy_names[:1] if policy_names else [])
|
||||
risk = st.slider("Risk threshold para HITL", 1, 5, 4)
|
||||
|
||||
st.subheader("Metadatos de versión")
|
||||
message = st.text_input("Mensaje de versión (como commit)", value="Creación vía editor gráfico de Forja")
|
||||
author = st.text_input("Autor", value="dashboard")
|
||||
|
||||
submitted = st.form_submit_button("🔨 Forjar / Guardar versión", type="primary")
|
||||
|
||||
if submitted:
|
||||
try:
|
||||
output_schema = json.loads(schema_text)
|
||||
except Exception as e:
|
||||
st.error(f"Schema JSON inválido: {e}")
|
||||
st.stop()
|
||||
|
||||
tags = [t.strip() for t in tags_str.split(",") if t.strip()] if tags_str else []
|
||||
|
||||
agent_def = {
|
||||
"name": name,
|
||||
"version": version,
|
||||
"owner": owner,
|
||||
"purpose": purpose,
|
||||
"state": state,
|
||||
"guardrails": guardrails or ["default"],
|
||||
"llm": {
|
||||
"provider": provider,
|
||||
"model": model,
|
||||
"temperature": float(temperature),
|
||||
"max_tokens": int(max_tokens),
|
||||
},
|
||||
"system_prompt": system_prompt,
|
||||
"output_schema": output_schema,
|
||||
"risk_threshold_for_hitl": int(risk),
|
||||
"updated_at": "2026-05-23T00:00:00Z",
|
||||
"input_label": "Input para el agente",
|
||||
"input_placeholder": "Escribe aquí el input...",
|
||||
"category": category or None,
|
||||
"tags": tags,
|
||||
"icon": icon or None,
|
||||
"template": "governed_llm",
|
||||
}
|
||||
|
||||
try:
|
||||
res = client.create_agent_version(name, agent_def, message, author)
|
||||
if "api_error" in res:
|
||||
st.error(f"Error API: {res['api_error']}")
|
||||
else:
|
||||
st.success(f"Versión {version} de {name} creada correctamente. Recarga Registro para verla.")
|
||||
st.json(res)
|
||||
except Exception as exc:
|
||||
st.error(f"Error al forjar: {exc}")
|
||||
@@ -0,0 +1,82 @@
|
||||
"""Página de editor gráfico para políticas de guardrails (Forja)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from forja_dashboard.client import CoreClient
|
||||
|
||||
|
||||
@st.cache_resource
|
||||
def get_client() -> CoreClient:
|
||||
return CoreClient()
|
||||
|
||||
|
||||
client = get_client()
|
||||
|
||||
st.title("🛡️ Forjar Política")
|
||||
st.caption("Compositor gráfico de políticas. Añade validadores de entrada/salida, configura y versiona.")
|
||||
|
||||
policies = client.list_policies()
|
||||
policy_names = [p["name"] for p in policies] if policies else []
|
||||
|
||||
mode = st.radio("Modo", ["Nueva política", "Nueva versión de existente"], horizontal=True)
|
||||
base = None
|
||||
if mode == "Nueva versión de existente" and policy_names:
|
||||
base = st.selectbox("Política base", policy_names)
|
||||
|
||||
validators_meta = client.list_validators()
|
||||
input_types = validators_meta.get("input", [])
|
||||
output_types = validators_meta.get("output", [])
|
||||
|
||||
def validator_form(stage: str, types: list[str], key_prefix: str) -> list[dict]:
|
||||
st.markdown(f"**Validadores de {stage}**")
|
||||
result = []
|
||||
num = st.number_input(f"Número de validadores {stage}", 0, 12, 2, key=f"num_{key_prefix}")
|
||||
for i in range(int(num)):
|
||||
with st.expander(f"Validador {i+1} ({stage})", expanded=True):
|
||||
t = st.selectbox("Tipo", types, key=f"type_{key_prefix}_{i}")
|
||||
cfg_text = st.text_area("Config (JSON)", value="{}", key=f"cfg_{key_prefix}_{i}", height=80)
|
||||
try:
|
||||
cfg = json.loads(cfg_text)
|
||||
except Exception:
|
||||
cfg = {}
|
||||
result.append({"type": t, "config": cfg})
|
||||
return result
|
||||
|
||||
with st.form("forge_policy"):
|
||||
name = st.text_input("Nombre de la política", value=base or "mi-politica")
|
||||
version = st.text_input("Versión", value="v1")
|
||||
description = st.text_area("Descripción", value="Política creada con el editor gráfico de Forja")
|
||||
on_error = st.selectbox("on_validator_error", ["fail_closed", "fail_open"], index=0)
|
||||
|
||||
st.divider()
|
||||
input_validators = validator_form("input", input_types, "in")
|
||||
output_validators = validator_form("output", output_types, "out")
|
||||
|
||||
st.subheader("Metadatos de versión")
|
||||
message = st.text_input("Mensaje", value="Creada vía UI gráfica")
|
||||
author = st.text_input("Autor", value="dashboard")
|
||||
|
||||
submitted = st.form_submit_button("🛡️ Guardar versión de política", type="primary")
|
||||
|
||||
if submitted:
|
||||
policy_def = {
|
||||
"name": name,
|
||||
"version": version,
|
||||
"description": description,
|
||||
"input_validators": input_validators,
|
||||
"output_validators": output_validators,
|
||||
"on_validator_error": on_error,
|
||||
}
|
||||
try:
|
||||
res = client.create_policy_version(name, policy_def, message, author)
|
||||
if "api_error" in res:
|
||||
st.error(res["api_error"])
|
||||
else:
|
||||
st.success(f"Política {name}@{version} guardada.")
|
||||
st.json(res)
|
||||
except Exception as e:
|
||||
st.error(str(e))
|
||||
Reference in New Issue
Block a user