proyecto generalizado
This commit is contained in:
+1
-1
@@ -24,4 +24,4 @@ LOG_LEVEL=INFO
|
|||||||
DATA_DIR=./data
|
DATA_DIR=./data
|
||||||
|
|
||||||
# URL del core (la usa el dashboard)
|
# URL del core (la usa el dashboard)
|
||||||
AGENTFORGE_CORE_URL=http://core:8000
|
FORJA_CORE_URL=http://core:8000
|
||||||
|
|||||||
+4
-4
@@ -1,14 +1,14 @@
|
|||||||
# AgentForge — Arquitectura técnica
|
# Forja — Arquitectura técnica
|
||||||
|
|
||||||
> Documento "vivo" con las decisiones técnicas. El spec original está en
|
> Documento "vivo" con las decisiones técnicas. El spec original está en
|
||||||
> [`docs/superpowers/specs/2026-05-09-agentforge-design.md`](docs/superpowers/specs/2026-05-09-agentforge-design.md).
|
> [`docs/superpowers/specs/2026-05-09- forja-design.md`](docs/superpowers/specs/2026-05-09- forja-design.md).
|
||||||
|
|
||||||
## Visión general
|
## Visión general
|
||||||
|
|
||||||
Two-tier:
|
Two-tier:
|
||||||
- `agentforge-core` — FastAPI 8000. Dominio de gobierno, runtime LangGraph,
|
- `forja-core` — FastAPI 8000. Dominio de gobierno, runtime LangGraph,
|
||||||
guardrails y persistencia.
|
guardrails y persistencia.
|
||||||
- `agentforge-dashboard` — Streamlit 8501. Cliente HTTP del core.
|
- `forja-dashboard` — Streamlit 8501. Cliente HTTP del core.
|
||||||
|
|
||||||
## Diagrama
|
## Diagrama
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,9 @@
|
|||||||
# 🛡️ AgentForge
|
# 🔨 Forja
|
||||||
|
|
||||||
Plataforma profesional de **gobernanza de agentes IA**: catalogación, versionado
|
**Forja de agentes IA** — plataforma de gobernanza genérica para agentes de cualquier tipo.
|
||||||
de prompts y políticas, guardrails runtime, ejecución stateful con
|
Catalogación y versionado (estilo Git) de definiciones, políticas y guardrails;
|
||||||
Human-in-the-Loop, observabilidad y trazabilidad end-to-end.
|
ejecución controlada con runtime stateful, Human-in-the-Loop y observabilidad completa.
|
||||||
|
Úsala como plano de control en cualquier proyecto que necesite agentes gobernados.
|
||||||
## ¿Por qué?
|
|
||||||
|
|
||||||
Poner agentes IA en producción sin una capa de gobierno produce sistemas
|
|
||||||
opacos: prompts que cambian sin historial, validaciones inconsistentes,
|
|
||||||
acciones de alto impacto sin supervisión, sin auditoría de decisiones.
|
|
||||||
AgentForge aporta el plano de control mínimo que un equipo de plataforma
|
|
||||||
necesita antes de operar agentes con impacto real.
|
|
||||||
|
|
||||||
## Arquitectura
|
## Arquitectura
|
||||||
|
|
||||||
@@ -18,13 +11,13 @@ necesita antes de operar agentes con impacto real.
|
|||||||
┌───────────────────────── docker-compose ──────────────────────────┐
|
┌───────────────────────── docker-compose ──────────────────────────┐
|
||||||
│ │
|
│ │
|
||||||
│ ┌─────────────────────┐ HTTP/JSON ┌────────────────────┐ │
|
│ ┌─────────────────────┐ HTTP/JSON ┌────────────────────┐ │
|
||||||
│ │ agentforge-dashboard│ ────────────────► │ agentforge-core │ │
|
│ │ forja-dashboard │ ────────────────► │ forja-core │ │
|
||||||
│ │ Streamlit :8501 │ ◄──────────────── │ FastAPI :8000 │ │
|
│ │ Streamlit :8501 │ ◄──────────────── │ FastAPI :8000 │ │
|
||||||
│ └─────────────────────┘ └────────────────────┘ │
|
│ └─────────────────────┘ └────────────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
│ Strategy pattern (Protocol) para LLMProvider, GuardrailEngine, │
|
│ Strategy pattern para LLMProvider, GuardrailEngine, Registry. │
|
||||||
│ AgentRegistry. Persistencia mixta: YAML (definiciones), JSON │
|
│ Persistencia: YAML (defs versionadas), JSONL (logs), SQLite │
|
||||||
│ (registry), JSONL (logs append-only), SQLite (checkpoints). │
|
│ (checkpoints LangGraph + HITL). │
|
||||||
└────────────────────────────────────────────────────────────────────┘
|
└────────────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,14 +58,14 @@ Azure OpenAI real, edita `.env`.
|
|||||||
|
|
||||||
| Feature | Ubicación |
|
| Feature | Ubicación |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Agent Registry | `core/src/agentforge_core/registry/repository.py` |
|
| Agent / Policy Registry | `core/src/forja_core/registry/` |
|
||||||
| Versionado tipo Git | `agents/<name>/versions/` + `registry/versioning.py` |
|
| Versionado Git-like (YAML) | `agents/<name>/` + `policies/<name>/` |
|
||||||
| Guardrails runtime | `core/src/agentforge_core/guardrails/` |
|
| Guardrails runtime (strategy) | `core/src/forja_core/guardrails/` |
|
||||||
| LangGraph stateful + checkpointing | `core/src/agentforge_core/runtime/` |
|
| LangGraph + HITL + checkpoints | `core/src/forja_core/runtime/` |
|
||||||
| Human-in-the-Loop | nodo `approve_gate` + endpoints `/approve` y `/reject` |
|
| Observabilidad (trace + structlog) | `core/src/forja_core/observability/` |
|
||||||
| Observabilidad | `observability/logging.py` (structlog + trace_id) |
|
| LLM abstraction | `core/src/forja_core/llm/` |
|
||||||
| LLM provider abstraction | `core/src/agentforge_core/llm/` |
|
| Editores gráficos (nuevo) | dashboard páginas Forjar Agente / Política |
|
||||||
| Política versionada | `policies/default/versions/v1.yaml` |
|
| API REST + dashboard | forja-core :8000 + forja-dashboard :8501 |
|
||||||
|
|
||||||
## Variables de entorno
|
## Variables de entorno
|
||||||
|
|
||||||
@@ -85,14 +78,14 @@ Ver [`docs/futuro.md`](docs/futuro.md).
|
|||||||
## Estructura del repositorio
|
## Estructura del repositorio
|
||||||
|
|
||||||
```
|
```
|
||||||
agentforge/
|
forja/
|
||||||
├── core/ servicio FastAPI
|
├── core/ servicio FastAPI (gobernanza + runtime)
|
||||||
├── dashboard/ servicio Streamlit
|
├── dashboard/ Streamlit (UI + editores gráficos)
|
||||||
├── agents/ definiciones declarativas (YAML)
|
├── agents/ definiciones de agentes (YAML versionados)
|
||||||
├── policies/ políticas de guardrails
|
├── policies/ políticas de guardrails (YAML versionados)
|
||||||
├── data/ estado runtime (gitignored)
|
├── data/ runtime state (gitignored)
|
||||||
├── tests/ pytest unit + integration
|
├── tests/
|
||||||
└── docs/ documentación adicional
|
└── docs/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|||||||
+1
-1
@@ -33,4 +33,4 @@ EXPOSE 8000
|
|||||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 \
|
HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 \
|
||||||
CMD curl -fsS http://localhost:8000/health || exit 1
|
CMD curl -fsS http://localhost:8000/health || exit 1
|
||||||
|
|
||||||
CMD ["uvicorn", "agentforge_core.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
CMD ["uvicorn", "forja_core.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
"""Router /policies: listado de políticas y de sus versiones."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException
|
|
||||||
|
|
||||||
from agentforge_core.api.deps import PolicyStoreDep
|
|
||||||
from agentforge_core.domain.policy import PolicyDefinition, PolicyVersionMeta
|
|
||||||
|
|
||||||
router = APIRouter()
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=list[PolicyDefinition])
|
|
||||||
def list_policies(store: PolicyStoreDep) -> list[PolicyDefinition]:
|
|
||||||
return store.list_policies()
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{name}/versions", response_model=list[PolicyVersionMeta])
|
|
||||||
def list_versions(name: str, store: PolicyStoreDep) -> list[PolicyVersionMeta]:
|
|
||||||
try:
|
|
||||||
return store.list_versions(name)
|
|
||||||
except FileNotFoundError as exc:
|
|
||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
||||||
@@ -2,15 +2,22 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException, status
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from agentforge_core.api.deps import RegistryDep
|
from forja_core.api.deps import RegistryDep
|
||||||
from agentforge_core.domain.agent import AgentDefinition, AgentVersionMeta
|
from forja_core.domain.agent import AgentDefinition, AgentVersionMeta
|
||||||
from agentforge_core.registry.versioning import DiffResult
|
from forja_core.registry.versioning import DiffResult
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
class CreateAgentVersionRequest(BaseModel):
|
||||||
|
agent: AgentDefinition
|
||||||
|
message: str
|
||||||
|
author: str = "dashboard"
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=list[AgentDefinition])
|
@router.get("", response_model=list[AgentDefinition])
|
||||||
def list_agents(registry: RegistryDep) -> list[AgentDefinition]:
|
def list_agents(registry: RegistryDep) -> list[AgentDefinition]:
|
||||||
return registry.list_agents()
|
return registry.list_agents()
|
||||||
@@ -43,3 +50,12 @@ def get_version(name: str, version: str, registry: RegistryDep) -> AgentDefiniti
|
|||||||
@router.get("/{name}/versions/{v_from}/diff/{v_to}", response_model=DiffResult)
|
@router.get("/{name}/versions/{v_from}/diff/{v_to}", response_model=DiffResult)
|
||||||
def diff_versions(name: str, v_from: str, v_to: str, registry: RegistryDep) -> DiffResult:
|
def diff_versions(name: str, v_from: str, v_to: str, registry: RegistryDep) -> DiffResult:
|
||||||
return registry.diff_versions(name, v_from, v_to)
|
return registry.diff_versions(name, v_from, v_to)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{name}/versions", response_model=AgentVersionMeta, status_code=status.HTTP_201_CREATED)
|
||||||
|
def create_agent_version(name: str, req: CreateAgentVersionRequest, registry: RegistryDep) -> AgentVersionMeta:
|
||||||
|
try:
|
||||||
|
return registry.upsert_version(name, req.agent, req.message, req.author)
|
||||||
|
except Exception as exc:
|
||||||
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||||
|
|
||||||
@@ -12,15 +12,15 @@ from typing import Annotated
|
|||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
from agentforge_core.config import Settings
|
from forja_core.config import Settings
|
||||||
from agentforge_core.guardrails.base import GuardrailEngine
|
from forja_core.guardrails.base import GuardrailEngine
|
||||||
from agentforge_core.guardrails.factory import build_guardrail_engine
|
from forja_core.guardrails.factory import build_guardrail_engine
|
||||||
from agentforge_core.llm.base import LLMProvider
|
from forja_core.llm.base import LLMProvider
|
||||||
from agentforge_core.llm.factory import build_llm_provider
|
from forja_core.llm.factory import build_llm_provider
|
||||||
from agentforge_core.registry.factory import build_agent_registry, build_policy_store
|
from forja_core.registry.factory import build_agent_registry, build_policy_store
|
||||||
from agentforge_core.registry.policy_store import FileSystemPolicyStore
|
from forja_core.registry.policy_store import FileSystemPolicyStore
|
||||||
from agentforge_core.registry.repository import FileSystemAgentRegistry
|
from forja_core.registry.repository import FileSystemAgentRegistry
|
||||||
from agentforge_core.runtime.orchestrator import AgentOrchestrator
|
from forja_core.runtime.orchestrator import AgentOrchestrator
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=1)
|
@lru_cache(maxsize=1)
|
||||||
@@ -9,23 +9,23 @@ from uuid import UUID
|
|||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from agentforge_core.api.deps import (
|
from forja_core.api.deps import (
|
||||||
OrchestratorDep,
|
OrchestratorDep,
|
||||||
PolicyStoreDep,
|
PolicyStoreDep,
|
||||||
RegistryDep,
|
RegistryDep,
|
||||||
SettingsDep,
|
SettingsDep,
|
||||||
)
|
)
|
||||||
from agentforge_core.api.persistence import (
|
from forja_core.api.persistence import (
|
||||||
append_execution,
|
append_execution,
|
||||||
append_violation,
|
append_violation,
|
||||||
read_execution_summaries,
|
read_execution_summaries,
|
||||||
)
|
)
|
||||||
from agentforge_core.domain.agent import AgentDefinition
|
from forja_core.domain.agent import AgentDefinition
|
||||||
from agentforge_core.domain.execution import AgentExecution, AgentExecutionSummary
|
from forja_core.domain.execution import AgentExecution, AgentExecutionSummary
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
from agentforge_core.registry.policy_store import FileSystemPolicyStore
|
from forja_core.registry.policy_store import FileSystemPolicyStore
|
||||||
from agentforge_core.registry.repository import FileSystemAgentRegistry
|
from forja_core.registry.repository import FileSystemAgentRegistry
|
||||||
from agentforge_core.runtime.orchestrator import AgentOrchestrator
|
from forja_core.runtime.orchestrator import AgentOrchestrator
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
# POST /agents/{name}/invoke vive aquí pero se monta en main bajo el prefijo /agents.
|
# POST /agents/{name}/invoke vive aquí pero se monta en main bajo el prefijo /agents.
|
||||||
+1
-1
@@ -8,7 +8,7 @@ from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoin
|
|||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import Response
|
from starlette.responses import Response
|
||||||
|
|
||||||
from agentforge_core.observability.logging import bind_trace_id, clear_trace_id
|
from forja_core.observability.logging import bind_trace_id, clear_trace_id
|
||||||
|
|
||||||
|
|
||||||
class TraceIdMiddleware(BaseHTTPMiddleware):
|
class TraceIdMiddleware(BaseHTTPMiddleware):
|
||||||
+2
-2
@@ -8,8 +8,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from agentforge_core.domain.execution import AgentExecution, AgentExecutionSummary
|
from forja_core.domain.execution import AgentExecution, AgentExecutionSummary
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
|
|
||||||
|
|
||||||
def append_violation(data_dir: Path, violation: GuardrailViolation) -> None:
|
def append_violation(data_dir: Path, violation: GuardrailViolation) -> None:
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
"""Router /policies: listado de políticas y de sus versiones."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException, status
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from forja_core.api.deps import PolicyStoreDep
|
||||||
|
from forja_core.domain.policy import PolicyDefinition, PolicyVersionMeta
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePolicyVersionRequest(BaseModel):
|
||||||
|
policy: PolicyDefinition
|
||||||
|
message: str
|
||||||
|
author: str = "dashboard"
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=list[PolicyDefinition])
|
||||||
|
def list_policies(store: PolicyStoreDep) -> list[PolicyDefinition]:
|
||||||
|
return store.list_policies()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{name}/versions", response_model=list[PolicyVersionMeta])
|
||||||
|
def list_versions(name: str, store: PolicyStoreDep) -> list[PolicyVersionMeta]:
|
||||||
|
try:
|
||||||
|
return store.list_versions(name)
|
||||||
|
except FileNotFoundError as exc:
|
||||||
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{name}/versions", response_model=PolicyVersionMeta, status_code=status.HTTP_201_CREATED)
|
||||||
|
def create_policy_version(name: str, req: CreatePolicyVersionRequest, store: PolicyStoreDep) -> PolicyVersionMeta:
|
||||||
|
try:
|
||||||
|
return store.upsert_version(name, req.policy, req.message, req.author)
|
||||||
|
except Exception as exc:
|
||||||
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/validators")
|
||||||
|
def list_validators() -> dict[str, list[str]]:
|
||||||
|
return {
|
||||||
|
"input": ["detect_pii", "prompt_injection", "toxic_language", "forbidden_topics"],
|
||||||
|
"output": ["schema_match", "pii_leakage", "forbidden_action_keywords", "telco_safety_rules"],
|
||||||
|
}
|
||||||
|
|
||||||
@@ -7,9 +7,9 @@ from uuid import UUID
|
|||||||
|
|
||||||
from fastapi import APIRouter, Query
|
from fastapi import APIRouter, Query
|
||||||
|
|
||||||
from agentforge_core.api.deps import SettingsDep
|
from forja_core.api.deps import SettingsDep
|
||||||
from agentforge_core.api.persistence import read_violations
|
from forja_core.api.persistence import read_violations
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -41,3 +41,9 @@ class AgentDefinition(BaseModel):
|
|||||||
output_schema: dict[str, Any]
|
output_schema: dict[str, Any]
|
||||||
risk_threshold_for_hitl: int = Field(default=4, ge=1, le=5)
|
risk_threshold_for_hitl: int = Field(default=4, ge=1, le=5)
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
|
input_label: str | None = None
|
||||||
|
input_placeholder: str | None = None
|
||||||
|
category: str | None = None
|
||||||
|
tags: list[str] = Field(default_factory=list)
|
||||||
|
icon: str | None = None
|
||||||
|
template: str = "governed_llm"
|
||||||
+1
-1
@@ -8,7 +8,7 @@ from uuid import UUID
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
|
|
||||||
|
|
||||||
class ProposedAction(BaseModel):
|
class ProposedAction(BaseModel):
|
||||||
+2
-2
@@ -5,8 +5,8 @@ from __future__ import annotations
|
|||||||
from typing import Any, Protocol
|
from typing import Any, Protocol
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
|
|
||||||
|
|
||||||
class GuardrailEngine(Protocol):
|
class GuardrailEngine(Protocol):
|
||||||
+3
-3
@@ -6,9 +6,9 @@ import asyncio
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
from agentforge_core.guardrails.base import GuardrailEngine
|
from forja_core.guardrails.base import GuardrailEngine
|
||||||
|
|
||||||
|
|
||||||
class CompositeGuardrailEngine:
|
class CompositeGuardrailEngine:
|
||||||
+5
-5
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from agentforge_core.config import Settings
|
from forja_core.config import Settings
|
||||||
from agentforge_core.guardrails.base import GuardrailEngine
|
from forja_core.guardrails.base import GuardrailEngine
|
||||||
from agentforge_core.guardrails.composite import CompositeGuardrailEngine
|
from forja_core.guardrails.composite import CompositeGuardrailEngine
|
||||||
from agentforge_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
from forja_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
||||||
from agentforge_core.guardrails.nemo import NeMoGuardrailsEngine
|
from forja_core.guardrails.nemo import NeMoGuardrailsEngine
|
||||||
|
|
||||||
|
|
||||||
def build_guardrail_engine(settings: Settings) -> GuardrailEngine:
|
def build_guardrail_engine(settings: Settings) -> GuardrailEngine:
|
||||||
+3
-3
@@ -9,9 +9,9 @@ from uuid import UUID
|
|||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
|
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
from agentforge_core.domain.policy import PolicyDefinition, PolicyValidator
|
from forja_core.domain.policy import PolicyDefinition, PolicyValidator
|
||||||
from agentforge_core.guardrails.validators import (
|
from forja_core.guardrails.validators import (
|
||||||
detect_pii,
|
detect_pii,
|
||||||
forbidden_action_keywords,
|
forbidden_action_keywords,
|
||||||
forbidden_topics,
|
forbidden_topics,
|
||||||
+2
-2
@@ -12,8 +12,8 @@ from uuid import UUID
|
|||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
|
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
|
|
||||||
log = structlog.get_logger(__name__)
|
log = structlog.get_logger(__name__)
|
||||||
|
|
||||||
+1
-1
@@ -10,7 +10,7 @@ from uuid import UUID
|
|||||||
|
|
||||||
import jsonschema
|
import jsonschema
|
||||||
|
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
|
|
||||||
_INJECTION_PATTERNS = [
|
_INJECTION_PATTERNS = [
|
||||||
r"ignore (the )?(previous|prior|above) (instruction|prompt|message)",
|
r"ignore (the )?(previous|prior|above) (instruction|prompt|message)",
|
||||||
@@ -8,7 +8,7 @@ from typing import Any
|
|||||||
|
|
||||||
from openai import AsyncAzureOpenAI
|
from openai import AsyncAzureOpenAI
|
||||||
|
|
||||||
from agentforge_core.llm.base import CompletionResult, Message
|
from forja_core.llm.base import CompletionResult, Message
|
||||||
|
|
||||||
|
|
||||||
class AzureOpenAIProvider:
|
class AzureOpenAIProvider:
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from agentforge_core.config import Settings
|
from forja_core.config import Settings
|
||||||
from agentforge_core.llm.azure import AzureOpenAIProvider
|
from forja_core.llm.azure import AzureOpenAIProvider
|
||||||
from agentforge_core.llm.base import LLMProvider
|
from forja_core.llm.base import LLMProvider
|
||||||
from agentforge_core.llm.mock import MockProvider
|
from forja_core.llm.mock import MockProvider
|
||||||
from agentforge_core.llm.openai import OpenAIProvider
|
from forja_core.llm.openai import OpenAIProvider
|
||||||
|
|
||||||
|
|
||||||
def build_llm_provider(settings: Settings) -> LLMProvider:
|
def build_llm_provider(settings: Settings) -> LLMProvider:
|
||||||
@@ -7,7 +7,7 @@ import json
|
|||||||
import time
|
import time
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from agentforge_core.llm.base import CompletionResult, Message
|
from forja_core.llm.base import CompletionResult, Message
|
||||||
|
|
||||||
_CANONICAL_RESPONSES: dict[str, dict[str, Any]] = {
|
_CANONICAL_RESPONSES: dict[str, dict[str, Any]] = {
|
||||||
"sip": {
|
"sip": {
|
||||||
@@ -8,7 +8,7 @@ from typing import Any
|
|||||||
|
|
||||||
from openai import AsyncOpenAI
|
from openai import AsyncOpenAI
|
||||||
|
|
||||||
from agentforge_core.llm.base import CompletionResult, Message
|
from forja_core.llm.base import CompletionResult, Message
|
||||||
|
|
||||||
|
|
||||||
class OpenAIProvider:
|
class OpenAIProvider:
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
"""Composición raíz de la aplicación FastAPI (agentforge-core)."""
|
"""Composición raíz de la aplicación FastAPI (forja-core)."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
from agentforge_core.api.middlewares import TraceIdMiddleware
|
from forja_core.api.middlewares import TraceIdMiddleware
|
||||||
from agentforge_core.config import Settings
|
from forja_core.config import Settings
|
||||||
from agentforge_core.observability.logging import configure_logging
|
from forja_core.observability.logging import configure_logging
|
||||||
|
|
||||||
|
|
||||||
def create_app() -> FastAPI:
|
def create_app() -> FastAPI:
|
||||||
@@ -14,7 +14,7 @@ def create_app() -> FastAPI:
|
|||||||
settings = Settings()
|
settings = Settings()
|
||||||
configure_logging(level=settings.log_level)
|
configure_logging(level=settings.log_level)
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="AgentForge Core",
|
title="Forja Core",
|
||||||
version="0.1.0",
|
version="0.1.0",
|
||||||
description="Plataforma de gobernanza de agentes IA — API REST.",
|
description="Plataforma de gobernanza de agentes IA — API REST.",
|
||||||
)
|
)
|
||||||
@@ -24,7 +24,7 @@ def create_app() -> FastAPI:
|
|||||||
async def health() -> dict[str, str]:
|
async def health() -> dict[str, str]:
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
|
|
||||||
from agentforge_core.api import agents, executions, policies, violations
|
from forja_core.api import agents, executions, policies, violations
|
||||||
|
|
||||||
app.include_router(agents.router, prefix="/agents", tags=["agents"])
|
app.include_router(agents.router, prefix="/agents", tags=["agents"])
|
||||||
app.include_router(executions.invoke_router, prefix="/agents", tags=["agents"])
|
app.include_router(executions.invoke_router, prefix="/agents", tags=["agents"])
|
||||||
+3
-3
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from agentforge_core.config import Settings
|
from forja_core.config import Settings
|
||||||
from agentforge_core.registry.policy_store import FileSystemPolicyStore
|
from forja_core.registry.policy_store import FileSystemPolicyStore
|
||||||
from agentforge_core.registry.repository import FileSystemAgentRegistry
|
from forja_core.registry.repository import FileSystemAgentRegistry
|
||||||
|
|
||||||
|
|
||||||
def build_agent_registry(settings: Settings) -> FileSystemAgentRegistry:
|
def build_agent_registry(settings: Settings) -> FileSystemAgentRegistry:
|
||||||
+48
-2
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import UTC, datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from agentforge_core.domain.policy import PolicyDefinition, PolicyVersionMeta
|
from forja_core.domain.policy import PolicyDefinition, PolicyVersionMeta
|
||||||
|
from forja_core.registry.versioning import compute_hash
|
||||||
|
|
||||||
|
|
||||||
class FileSystemPolicyStore:
|
class FileSystemPolicyStore:
|
||||||
@@ -62,3 +63,48 @@ class FileSystemPolicyStore:
|
|||||||
if isinstance(value, datetime):
|
if isinstance(value, datetime):
|
||||||
return value
|
return value
|
||||||
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||||
|
|
||||||
|
def upsert_version(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
body: PolicyDefinition,
|
||||||
|
message: str,
|
||||||
|
author: str,
|
||||||
|
) -> PolicyVersionMeta:
|
||||||
|
policy_dir = self._root / name
|
||||||
|
versions_dir = policy_dir / "versions"
|
||||||
|
versions_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
version_id = body.version
|
||||||
|
yaml_text = yaml.safe_dump(
|
||||||
|
body.model_dump(mode="json"),
|
||||||
|
sort_keys=False,
|
||||||
|
allow_unicode=True,
|
||||||
|
)
|
||||||
|
h = compute_hash(yaml_text)
|
||||||
|
|
||||||
|
version_path = versions_dir / f"{version_id}.yaml"
|
||||||
|
version_path.write_text(yaml_text, encoding="utf-8")
|
||||||
|
|
||||||
|
meta = PolicyVersionMeta(
|
||||||
|
id=version_id,
|
||||||
|
hash=h,
|
||||||
|
author=author,
|
||||||
|
message=message,
|
||||||
|
created_at=datetime.now(UTC),
|
||||||
|
)
|
||||||
|
|
||||||
|
index_path = policy_dir / "index.yaml"
|
||||||
|
if index_path.exists():
|
||||||
|
with index_path.open(encoding="utf-8") as f:
|
||||||
|
index: dict[str, Any] = yaml.safe_load(f)
|
||||||
|
else:
|
||||||
|
index = {"name": name, "versions": [], "active_version": version_id}
|
||||||
|
|
||||||
|
index["versions"] = [v for v in index["versions"] if v["id"] != version_id]
|
||||||
|
index["versions"].append(meta.model_dump(mode="json"))
|
||||||
|
index["active_version"] = version_id
|
||||||
|
|
||||||
|
with index_path.open("w", encoding="utf-8") as f:
|
||||||
|
yaml.safe_dump(index, f, sort_keys=False, allow_unicode=True)
|
||||||
|
return meta
|
||||||
+2
-2
@@ -8,8 +8,8 @@ from typing import Any
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from agentforge_core.domain.agent import AgentDefinition, AgentVersionMeta
|
from forja_core.domain.agent import AgentDefinition, AgentVersionMeta
|
||||||
from agentforge_core.registry.versioning import DiffResult, compute_hash, unified_diff
|
from forja_core.registry.versioning import DiffResult, compute_hash, unified_diff
|
||||||
|
|
||||||
|
|
||||||
class FileSystemAgentRegistry:
|
class FileSystemAgentRegistry:
|
||||||
@@ -7,11 +7,11 @@ from typing import Any
|
|||||||
from langgraph.checkpoint.base import BaseCheckpointSaver
|
from langgraph.checkpoint.base import BaseCheckpointSaver
|
||||||
from langgraph.graph import END, START, StateGraph
|
from langgraph.graph import END, START, StateGraph
|
||||||
|
|
||||||
from agentforge_core.domain.agent import AgentDefinition
|
from forja_core.domain.agent import AgentDefinition
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
from agentforge_core.guardrails.base import GuardrailEngine
|
from forja_core.guardrails.base import GuardrailEngine
|
||||||
from agentforge_core.llm.base import LLMProvider
|
from forja_core.llm.base import LLMProvider
|
||||||
from agentforge_core.runtime.nodes import (
|
from forja_core.runtime.nodes import (
|
||||||
build_node_approve_gate,
|
build_node_approve_gate,
|
||||||
build_node_finalize,
|
build_node_finalize,
|
||||||
build_node_llm_reason,
|
build_node_llm_reason,
|
||||||
@@ -19,7 +19,7 @@ from agentforge_core.runtime.nodes import (
|
|||||||
build_node_validate_input,
|
build_node_validate_input,
|
||||||
build_node_validate_output,
|
build_node_validate_output,
|
||||||
)
|
)
|
||||||
from agentforge_core.runtime.state import AgentState
|
from forja_core.runtime.state import AgentState
|
||||||
|
|
||||||
|
|
||||||
def build_graph(
|
def build_graph(
|
||||||
@@ -12,11 +12,11 @@ from uuid import UUID
|
|||||||
import structlog
|
import structlog
|
||||||
from langgraph.types import interrupt
|
from langgraph.types import interrupt
|
||||||
|
|
||||||
from agentforge_core.domain.agent import AgentDefinition
|
from forja_core.domain.agent import AgentDefinition
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
from agentforge_core.guardrails.base import GuardrailEngine
|
from forja_core.guardrails.base import GuardrailEngine
|
||||||
from agentforge_core.llm.base import LLMProvider, Message
|
from forja_core.llm.base import LLMProvider, Message
|
||||||
from agentforge_core.runtime.state import AgentState
|
from forja_core.runtime.state import AgentState
|
||||||
|
|
||||||
log = structlog.get_logger(__name__)
|
log = structlog.get_logger(__name__)
|
||||||
|
|
||||||
+8
-8
@@ -18,14 +18,14 @@ from uuid import UUID, uuid4
|
|||||||
import structlog
|
import structlog
|
||||||
from langgraph.types import Command
|
from langgraph.types import Command
|
||||||
|
|
||||||
from agentforge_core.domain.agent import AgentDefinition
|
from forja_core.domain.agent import AgentDefinition
|
||||||
from agentforge_core.domain.execution import AgentExecution, DecisionStep, ProposedAction
|
from forja_core.domain.execution import AgentExecution, DecisionStep, ProposedAction
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
from agentforge_core.guardrails.base import GuardrailEngine
|
from forja_core.guardrails.base import GuardrailEngine
|
||||||
from agentforge_core.llm.base import LLMProvider
|
from forja_core.llm.base import LLMProvider
|
||||||
from agentforge_core.runtime.checkpointer import build_checkpointer
|
from forja_core.runtime.checkpointer import build_checkpointer
|
||||||
from agentforge_core.runtime.graph import build_graph
|
from forja_core.runtime.graph import build_graph
|
||||||
|
|
||||||
log = structlog.get_logger(__name__)
|
log = structlog.get_logger(__name__)
|
||||||
|
|
||||||
@@ -26,6 +26,6 @@ EXPOSE 8501
|
|||||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s --retries=3 \
|
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s --retries=3 \
|
||||||
CMD curl -fsS http://localhost:8501/_stcore/health || exit 1
|
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.address=0.0.0.0", "--server.port=8501", \
|
||||||
"--server.headless=true", "--browser.gatherUsageStats=false"]
|
"--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."""
|
"""Cliente sincrono (Streamlit es sync). 2 retries con httpx Transport."""
|
||||||
|
|
||||||
def __init__(self, base_url: str | None = None, timeout: float = 30.0) -> None:
|
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)
|
transport = httpx.HTTPTransport(retries=2)
|
||||||
self._client = httpx.Client(base_url=self.base_url, timeout=timeout, transport=transport)
|
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]:
|
def list_policy_versions(self, name: str) -> list[dict]:
|
||||||
return self._get(f"/policies/{name}/versions")
|
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 -----
|
# ----- helpers privados -----
|
||||||
|
|
||||||
def _get(self, path: str, params: dict | None = None) -> Any:
|
def _get(self, path: str, params: dict | None = None) -> Any:
|
||||||
+3
-2
@@ -4,8 +4,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
from agentforge_dashboard.client import CoreClient
|
from forja_dashboard.client import CoreClient
|
||||||
from agentforge_dashboard.components.diff_view import render_unified_diff
|
from forja_dashboard.components.diff_view import render_unified_diff
|
||||||
|
|
||||||
|
|
||||||
@st.cache_resource
|
@st.cache_resource
|
||||||
@@ -16,6 +16,7 @@ def get_client() -> CoreClient:
|
|||||||
client = get_client()
|
client = get_client()
|
||||||
st.title("🏛️ Registro de Agentes")
|
st.title("🏛️ Registro de Agentes")
|
||||||
st.caption("Catálogo central de agentes con versionado tipo Git.")
|
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()
|
agents = client.list_agents()
|
||||||
if not agents:
|
if not agents:
|
||||||
+7
-9
@@ -6,9 +6,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
from agentforge_dashboard.client import CoreClient
|
from forja_dashboard.client import CoreClient
|
||||||
from agentforge_dashboard.components.trace_view import render_trace
|
from forja_dashboard.components.trace_view import render_trace
|
||||||
from agentforge_dashboard.components.violation_view import render_violations
|
from forja_dashboard.components.violation_view import render_violations
|
||||||
|
|
||||||
|
|
||||||
@st.cache_resource
|
@st.cache_resource
|
||||||
@@ -27,6 +27,7 @@ if not agents:
|
|||||||
|
|
||||||
names = [a["name"] for a in agents]
|
names = [a["name"] for a in agents]
|
||||||
agent_name = st.selectbox("Agente", names)
|
agent_name = st.selectbox("Agente", names)
|
||||||
|
agent = client.get_agent(agent_name)
|
||||||
|
|
||||||
# Cargar escenarios pregrabados si existen en el disco montado.
|
# Cargar escenarios pregrabados si existen en el disco montado.
|
||||||
examples_dir = Path("agents") / agent_name / "examples"
|
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")
|
st.session_state["input_text"] = ef.read_text(encoding="utf-8")
|
||||||
|
|
||||||
with col_l:
|
with col_l:
|
||||||
user_input = st.text_area(
|
label = agent.get("input_label") or "Input para el agente"
|
||||||
"Descripción del incidente",
|
ph = agent.get("input_placeholder") or "Escribe el input para el agente o usa un escenario..."
|
||||||
height=240,
|
user_input = st.text_area(label, height=240, key="input_text", placeholder=ph)
|
||||||
key="input_text",
|
|
||||||
placeholder="Pega aquí la descripción del incidente o usa un escenario...",
|
|
||||||
)
|
|
||||||
|
|
||||||
if st.button("🚀 Invocar agente", type="primary", disabled=not user_input):
|
if st.button("🚀 Invocar agente", type="primary", disabled=not user_input):
|
||||||
with st.spinner("Ejecutando..."):
|
with st.spinner("Ejecutando..."):
|
||||||
+1
-1
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
from agentforge_dashboard.client import CoreClient
|
from forja_dashboard.client import CoreClient
|
||||||
|
|
||||||
|
|
||||||
@st.cache_resource
|
@st.cache_resource
|
||||||
+3
-3
@@ -4,9 +4,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
from agentforge_dashboard.client import CoreClient
|
from forja_dashboard.client import CoreClient
|
||||||
from agentforge_dashboard.components.trace_view import render_trace
|
from forja_dashboard.components.trace_view import render_trace
|
||||||
from agentforge_dashboard.components.violation_view import render_violations
|
from forja_dashboard.components.violation_view import render_violations
|
||||||
|
|
||||||
|
|
||||||
@st.cache_resource
|
@st.cache_resource
|
||||||
+2
-1
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
from agentforge_dashboard.client import CoreClient
|
from forja_dashboard.client import CoreClient
|
||||||
|
|
||||||
|
|
||||||
@st.cache_resource
|
@st.cache_resource
|
||||||
@@ -15,6 +15,7 @@ def get_client() -> CoreClient:
|
|||||||
client = get_client()
|
client = get_client()
|
||||||
st.title("📐 Politicas de guardrails")
|
st.title("📐 Politicas de guardrails")
|
||||||
st.caption("Inventario de políticas y sus versiones (estilo Git).")
|
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()
|
policies = client.list_policies()
|
||||||
if not 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))
|
||||||
+8
-8
@@ -3,8 +3,8 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: core/Dockerfile
|
dockerfile: core/Dockerfile
|
||||||
image: agentforge-core:dev
|
image: forja-core:dev
|
||||||
container_name: agentforge-core
|
container_name: forja-core
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
env_file:
|
env_file:
|
||||||
@@ -14,8 +14,8 @@ services:
|
|||||||
AGENTS_DIR: /app/agents
|
AGENTS_DIR: /app/agents
|
||||||
POLICIES_DIR: /app/policies
|
POLICIES_DIR: /app/policies
|
||||||
volumes:
|
volumes:
|
||||||
- ./agents:/app/agents:ro
|
- ./agents:/app/agents:rw
|
||||||
- ./policies:/app/policies:ro
|
- ./policies:/app/policies:rw
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-fsS", "http://localhost:8000/health"]
|
test: ["CMD", "curl", "-fsS", "http://localhost:8000/health"]
|
||||||
@@ -29,15 +29,15 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: dashboard/Dockerfile
|
dockerfile: dashboard/Dockerfile
|
||||||
image: agentforge-dashboard:dev
|
image: forja-dashboard:dev
|
||||||
container_name: agentforge-dashboard
|
container_name: forja-dashboard
|
||||||
depends_on:
|
depends_on:
|
||||||
core:
|
core:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
ports:
|
ports:
|
||||||
- "8501:8501"
|
- "8501:8501"
|
||||||
environment:
|
environment:
|
||||||
AGENTFORGE_CORE_URL: http://core:8000
|
FORJA_CORE_URL: http://core:8000
|
||||||
volumes:
|
volumes:
|
||||||
- ./agents:/app/agents:ro # para que el dashboard lea los escenarios
|
- ./agents:/app/agents:rw # rw para editores gráficos + escenarios
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
+11
-11
@@ -1,4 +1,4 @@
|
|||||||
# Componentes de AgentForge y su interrelación (bajo nivel)
|
# Componentes de Forja y su interrelación (bajo nivel)
|
||||||
|
|
||||||
> **Alcance.** Este documento es la **referencia de cableado**: módulos exactos,
|
> **Alcance.** Este documento es la **referencia de cableado**: módulos exactos,
|
||||||
> firmas, el grafo de dependencias de imports, el grafo de inyección de
|
> firmas, el grafo de dependencias de imports, el grafo de inyección de
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
> - ¿Las decisiones técnicas resumidas? → [`ARCHITECTURE.md`](../ARCHITECTURE.md).
|
> - ¿Las decisiones técnicas resumidas? → [`ARCHITECTURE.md`](../ARCHITECTURE.md).
|
||||||
> - ¿Cómo arrancarlo? → [`README.md`](../README.md).
|
> - ¿Cómo arrancarlo? → [`README.md`](../README.md).
|
||||||
>
|
>
|
||||||
> Rutas relativas a `core/src/agentforge_core/` salvo que se diga otra cosa.
|
> Rutas relativas a `core/src/ forja_core/` salvo que se diga otra cosa.
|
||||||
> Refleja el estado del repo en `v0.1.0`.
|
> Refleja el estado del repo en `v0.1.0`.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -67,7 +67,7 @@ NIVEL 6
|
|||||||
main ⇐ api (routers), api.middlewares, config, observability.logging → create_app(), app
|
main ⇐ api (routers), api.middlewares, config, observability.logging → create_app(), app
|
||||||
|
|
||||||
(separado, sin imports del paquete core)
|
(separado, sin imports del paquete core)
|
||||||
dashboard/src/agentforge_dashboard/* ← habla con `main` por HTTP, no por import
|
dashboard/src/ forja_dashboard/* ← habla con `main` por HTTP, no por import
|
||||||
```
|
```
|
||||||
|
|
||||||
Reglas que se cumplen y conviene mantener:
|
Reglas que se cumplen y conviene mantener:
|
||||||
@@ -333,11 +333,11 @@ Hashing/versionado: `registry/versioning.compute_hash(yaml_text)` = SHA-256 del
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Dashboard ↔ Core (`agentforge_dashboard`)
|
## 6. Dashboard ↔ Core (` forja_dashboard`)
|
||||||
|
|
||||||
El dashboard no comparte código con el core: solo lo llama por HTTP a través de
|
El dashboard no comparte código con el core: solo lo llama por HTTP a través de
|
||||||
`CoreClient` (`dashboard/src/agentforge_dashboard/client.py`, httpx síncrono con 2
|
`CoreClient` (`dashboard/src/ forja_dashboard/client.py`, httpx síncrono con 2
|
||||||
retries, `base_url = AGENTFORGE_CORE_URL`; mapea 404/409/422 → `{"error": <json>}`).
|
retries, `base_url = FORJA_CORE_URL`; mapea 404/409/422 → `{"error": <json>}`).
|
||||||
|
|
||||||
| `CoreClient.<método>` | Endpoint del core | Página(s) que lo usan |
|
| `CoreClient.<método>` | Endpoint del core | Página(s) que lo usan |
|
||||||
|-----------------------|-------------------|-----------------------|
|
|-----------------------|-------------------|-----------------------|
|
||||||
@@ -365,11 +365,11 @@ Componentes reutilizables: `components/diff_view.render_unified_diff(diff_text)`
|
|||||||
|
|
||||||
## 7. Arranque y ciclo de vida
|
## 7. Arranque y ciclo de vida
|
||||||
|
|
||||||
**Proceso core** (`uvicorn agentforge_core.main:app`):
|
**Proceso core** (`uvicorn forja_core.main:app`):
|
||||||
1. Import de `agentforge_core.main` ⇒ se ejecuta `app = create_app()`:
|
1. Import de ` forja_core.main` ⇒ se ejecuta `app = create_app()`:
|
||||||
`Settings()` → `configure_logging(level=settings.log_level)` → `FastAPI(...)` →
|
`Settings()` → `configure_logging(level=settings.log_level)` → `FastAPI(...)` →
|
||||||
`app.add_middleware(TraceIdMiddleware)` → registra `GET /health` →
|
`app.add_middleware(TraceIdMiddleware)` → registra `GET /health` →
|
||||||
`from agentforge_core.api import agents, executions, policies, violations` →
|
`from forja_core.api import agents, executions, policies, violations` →
|
||||||
`include_router` ×5.
|
`include_router` ×5.
|
||||||
2. Las dependencias (`deps.get_registry`, `get_policy_store`, `get_llm_provider`,
|
2. Las dependencias (`deps.get_registry`, `get_policy_store`, `get_llm_provider`,
|
||||||
`get_guardrail_engine`, `get_orchestrator`) **no** se construyen aún; se
|
`get_guardrail_engine`, `get_orchestrator`) **no** se construyen aún; se
|
||||||
@@ -378,12 +378,12 @@ Componentes reutilizables: `components/diff_view.render_unified_diff(diff_text)`
|
|||||||
pueden disparar la construcción perezosa) → handler → respuesta con `X-Trace-Id`.
|
pueden disparar la construcción perezosa) → handler → respuesta con `X-Trace-Id`.
|
||||||
|
|
||||||
**Contenedores** (`docker-compose.yml`): servicio `core` (`core/Dockerfile`,
|
**Contenedores** (`docker-compose.yml`): servicio `core` (`core/Dockerfile`,
|
||||||
`uvicorn agentforge_core.main:app --host 0.0.0.0 --port 8000`, `HEALTHCHECK` →
|
`uvicorn forja_core.main:app --host 0.0.0.0 --port 8000`, `HEALTHCHECK` →
|
||||||
`curl /health`, monta `./agents:ro`, `./policies:ro`, `./data:rw`, env
|
`curl /health`, monta `./agents:ro`, `./policies:ro`, `./data:rw`, env
|
||||||
`DATA_DIR=/app/data`, `AGENTS_DIR=/app/agents`, `POLICIES_DIR=/app/policies`,
|
`DATA_DIR=/app/data`, `AGENTS_DIR=/app/agents`, `POLICIES_DIR=/app/policies`,
|
||||||
`env_file: .env`); servicio `dashboard` (`dashboard/Dockerfile`, `streamlit run
|
`env_file: .env`); servicio `dashboard` (`dashboard/Dockerfile`, `streamlit run
|
||||||
app.py`, `HEALTHCHECK` → `/_stcore/health`, `depends_on: core: service_healthy`,
|
app.py`, `HEALTHCHECK` → `/_stcore/health`, `depends_on: core: service_healthy`,
|
||||||
env `AGENTFORGE_CORE_URL=http://core:8000`, monta `./agents:ro` para leer los
|
env `FORJA_CORE_URL=http://core:8000`, monta `./agents:ro` para leer los
|
||||||
`examples/*.txt`).
|
`examples/*.txt`).
|
||||||
|
|
||||||
**`Settings` (env vars)** — `config.py`:
|
**`Settings` (env vars)** — `config.py`:
|
||||||
|
|||||||
+13
-13
@@ -1,4 +1,4 @@
|
|||||||
# AgentForge explicado de principio a fin
|
# Forja explicado de principio a fin
|
||||||
|
|
||||||
> **Para quién es esto.** Una guía didáctica para alguien que llega nuevo al
|
> **Para quién es esto.** Una guía didáctica para alguien que llega nuevo al
|
||||||
> proyecto —técnico o no— y quiere entender *qué hace*, *por qué está hecho así*
|
> proyecto —técnico o no— y quiere entender *qué hace*, *por qué está hecho así*
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
> opacos: prompts que cambian sin historial, validaciones inconsistentes, acciones
|
> opacos: prompts que cambian sin historial, validaciones inconsistentes, acciones
|
||||||
> de alto impacto sin supervisión y ninguna auditoría de lo que decidió el agente.
|
> de alto impacto sin supervisión y ninguna auditoría de lo que decidió el agente.
|
||||||
|
|
||||||
**AgentForge es el "plano de control" que pones *delante* de tus agentes** antes de
|
**Forja es el "plano de control" que pones *delante* de tus agentes** antes de
|
||||||
dejarlos tocar nada importante. No es un framework para *construir* agentes; es la
|
dejarlos tocar nada importante. No es un framework para *construir* agentes; es la
|
||||||
capa que los **cataloga, versiona, valida, ejecuta de forma supervisada y audita**.
|
capa que los **cataloga, versiona, valida, ejecuta de forma supervisada y audita**.
|
||||||
|
|
||||||
@@ -47,13 +47,13 @@ Si entiendes estas seis ideas, entiendes el proyecto. Todo lo demás son detalle
|
|||||||
|
|
||||||
## 3. Vista de pájaro: dos servicios
|
## 3. Vista de pájaro: dos servicios
|
||||||
|
|
||||||
AgentForge son **dos procesos** que se hablan por HTTP/JSON:
|
Forja son **dos procesos** que se hablan por HTTP/JSON:
|
||||||
|
|
||||||
```
|
```
|
||||||
┌───────────────────────────── docker-compose ──────────────────────────────┐
|
┌───────────────────────────── docker-compose ──────────────────────────────┐
|
||||||
│ │
|
│ │
|
||||||
│ ┌──────────────────────┐ HTTP/JSON ┌────────────────────────┐ │
|
│ ┌──────────────────────┐ HTTP/JSON ┌────────────────────────┐ │
|
||||||
│ │ agentforge-dashboard │ ───────────────► │ agentforge-core │ │
|
│ │ forja-dashboard │ ───────────────► │ forja-core │ │
|
||||||
│ │ Streamlit :8501 │ ◄─────────────── │ FastAPI :8000 │ │
|
│ │ Streamlit :8501 │ ◄─────────────── │ FastAPI :8000 │ │
|
||||||
│ │ (la "consola") │ │ (el cerebro) │ │
|
│ │ (la "consola") │ │ (el cerebro) │ │
|
||||||
│ └──────────────────────┘ └───────────┬────────────┘ │
|
│ └──────────────────────┘ └───────────┬────────────┘ │
|
||||||
@@ -69,9 +69,9 @@ AgentForge son **dos procesos** que se hablan por HTTP/JSON:
|
|||||||
└────────────────────────────────────────────────────────────────────────────┘
|
└────────────────────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`agentforge-core`** (FastAPI, puerto 8000) — todo el dominio: registry de
|
- **`forja-core`** (FastAPI, puerto 8000) — todo el dominio: registry de
|
||||||
agentes, motor de guardrails, runtime de ejecución, persistencia. No tiene UI.
|
agentes, motor de guardrails, runtime de ejecución, persistencia. No tiene UI.
|
||||||
- **`agentforge-dashboard`** (Streamlit, puerto 8501) — una consola visual. **No
|
- **`forja-dashboard`** (Streamlit, puerto 8501) — una consola visual. **No
|
||||||
contiene lógica de negocio**: es un cliente HTTP del core con cinco páginas.
|
contiene lógica de negocio**: es un cliente HTTP del core con cinco páginas.
|
||||||
|
|
||||||
La separación importa: el core podría servir a una CLI, a otro servicio, a un
|
La separación importa: el core podría servir a una CLI, a otro servicio, a un
|
||||||
@@ -97,7 +97,7 @@ pipeline... el dashboard es solo una de las caras posibles.
|
|||||||
|
|
||||||
## 4. Recorrido por los módulos (y quién depende de quién)
|
## 4. Recorrido por los módulos (y quién depende de quién)
|
||||||
|
|
||||||
El código del core vive bajo `core/src/agentforge_core/`. Lo agrupo por capas, de
|
El código del core vive bajo `core/src/ forja_core/`. Lo agrupo por capas, de
|
||||||
las más internas (sin dependencias) a las más externas.
|
las más internas (sin dependencias) a las más externas.
|
||||||
|
|
||||||
### 4.1 `domain/` — el vocabulario del sistema
|
### 4.1 `domain/` — el vocabulario del sistema
|
||||||
@@ -278,7 +278,7 @@ inyectan). Dependen de él: la API (`api/executions.py` solo conoce el
|
|||||||
| `executions.py` | El más cargado: `POST /agents/{n}/invoke`, `GET /executions`, `GET /executions/{trace_id}`, `POST /executions/{trace_id}/approve`, `POST /executions/{trace_id}/reject`. Mantiene además `execution_index.json` (mapa `trace_id → agente/versión`) para poder reanudar tras un reinicio. |
|
| `executions.py` | El más cargado: `POST /agents/{n}/invoke`, `GET /executions`, `GET /executions/{trace_id}`, `POST /executions/{trace_id}/approve`, `POST /executions/{trace_id}/reject`. Mantiene además `execution_index.json` (mapa `trace_id → agente/versión`) para poder reanudar tras un reinicio. |
|
||||||
| `policies.py`, `violations.py` | Routers `/policies` y `/violations` (este con filtros por severidad, stage, etc.). |
|
| `policies.py`, `violations.py` | Routers `/policies` y `/violations` (este con filtros por severidad, stage, etc.). |
|
||||||
|
|
||||||
Y la **raíz de la app**: `core/src/agentforge_core/main.py` → `create_app()` instancia
|
Y la **raíz de la app**: `core/src/ forja_core/main.py` → `create_app()` instancia
|
||||||
`FastAPI`, añade el `TraceIdMiddleware`, monta los routers, y expone `/health`.
|
`FastAPI`, añade el `TraceIdMiddleware`, monta los routers, y expone `/health`.
|
||||||
|
|
||||||
Depende de: todo lo de arriba (vía `deps.py`). Dependen de él: el dashboard (por
|
Depende de: todo lo de arriba (vía `deps.py`). Dependen de él: el dashboard (por
|
||||||
@@ -297,7 +297,7 @@ HTTP) y los tests de integración (`tests/integration/`, vía `TestClient`).
|
|||||||
| `pages/5_📐_Politicas.py` | Inventario de políticas: validadores de entrada/salida (cada uno expandible con su config) y versiones. |
|
| `pages/5_📐_Politicas.py` | Inventario de políticas: validadores de entrada/salida (cada uno expandible con su config) y versiones. |
|
||||||
| `components/` | Trozos reutilizables de UI: `diff_view` (pinta `+`/`-`/`@@`), `trace_view` (el timeline del `decision_path`), `violation_view` (badges de severidad). |
|
| `components/` | Trozos reutilizables de UI: `diff_view` (pinta `+`/`-`/`@@`), `trace_view` (el timeline del `decision_path`), `violation_view` (badges de severidad). |
|
||||||
|
|
||||||
Depende de: el `agentforge-core` por HTTP (vía `AGENTFORGE_CORE_URL`). **Nadie del
|
Depende de: el `forja-core` por HTTP (vía `FORJA_CORE_URL`). **Nadie del
|
||||||
core depende del dashboard.** No tiene tests unitarios (mal coste/beneficio para
|
core depende del dashboard.** No tiene tests unitarios (mal coste/beneficio para
|
||||||
Streamlit); su verificación es la checklist manual de [`docs/manual_qa.md`](manual_qa.md).
|
Streamlit); su verificación es la checklist manual de [`docs/manual_qa.md`](manual_qa.md).
|
||||||
|
|
||||||
@@ -464,7 +464,7 @@ sequenceDiagram
|
|||||||
|
|
||||||
## 7. Persistencia: cuatro formas, cuatro razones
|
## 7. Persistencia: cuatro formas, cuatro razones
|
||||||
|
|
||||||
AgentForge no usa una sola base de datos; usa la herramienta adecuada para cada cosa.
|
Forja no usa una sola base de datos; usa la herramienta adecuada para cada cosa.
|
||||||
|
|
||||||
| Qué | Cómo | Por qué así |
|
| Qué | Cómo | Por qué así |
|
||||||
|-----|------|-------------|
|
|-----|------|-------------|
|
||||||
@@ -499,9 +499,9 @@ AgentForge no usa una sola base de datos; usa la herramienta adecuada para cada
|
|||||||
## 9. Mapa del repositorio y por dónde empezar a leer
|
## 9. Mapa del repositorio y por dónde empezar a leer
|
||||||
|
|
||||||
```
|
```
|
||||||
agentforge/
|
forja/
|
||||||
├── core/
|
├── core/
|
||||||
│ ├── src/agentforge_core/
|
│ ├── src/ forja_core/
|
||||||
│ │ ├── domain/ ← los modelos de datos (empieza por execution.py)
|
│ │ ├── domain/ ← los modelos de datos (empieza por execution.py)
|
||||||
│ │ ├── config.py · observability/ ← cimientos transversales
|
│ │ ├── config.py · observability/ ← cimientos transversales
|
||||||
│ │ ├── llm/ ← proveedores LLM (Protocol + impls + factory)
|
│ │ ├── llm/ ← proveedores LLM (Protocol + impls + factory)
|
||||||
@@ -512,7 +512,7 @@ agentforge/
|
|||||||
│ │ └── main.py ← create_app(): ensambla la app
|
│ │ └── main.py ← create_app(): ensambla la app
|
||||||
│ ├── Dockerfile · requirements.txt
|
│ ├── Dockerfile · requirements.txt
|
||||||
├── dashboard/
|
├── dashboard/
|
||||||
│ ├── src/agentforge_dashboard/ ← Streamlit (client + app + pages + components)
|
│ ├── src/ forja_dashboard/ ← Streamlit (client + app + pages + components)
|
||||||
│ ├── Dockerfile · requirements.txt
|
│ ├── Dockerfile · requirements.txt
|
||||||
├── agents/incident_analyzer/ ← el agente de ejemplo (YAMLs + escenarios .txt)
|
├── agents/incident_analyzer/ ← el agente de ejemplo (YAMLs + escenarios .txt)
|
||||||
├── policies/default/ ← la política de guardrails de ejemplo
|
├── policies/default/ ← la política de guardrails de ejemplo
|
||||||
|
|||||||
@@ -0,0 +1,419 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Forja — Progreso y Próximos Pasos</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Space+Grotesk:wght@500;600&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primary: #0ea5e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', system_ui, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-display {
|
||||||
|
font-family: 'Space Grotesk', 'Inter', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
font-family: 'Space Grotesk', 'Inter', sans-serif;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.125rem 0.625rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.forja-gradient {
|
||||||
|
background: linear-gradient(135deg, #0ea5e9, #3b82f6);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
box-shadow 0.2s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.05), 0 8px 10px -6px rgb(0 0 0 / 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-active {
|
||||||
|
border-bottom: 3px solid #0ea5e9;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
transition: width 1s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mono {
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-icon {
|
||||||
|
width: 2.25rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 9999px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-zinc-950 text-zinc-200">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="border-b border-zinc-800 bg-zinc-900/70 backdrop-blur-lg sticky top-0 z-50">
|
||||||
|
<div class="max-w-6xl mx-auto px-6 py-5 flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-x-3">
|
||||||
|
<div class="w-10 h-10 rounded-2xl bg-sky-500 flex items-center justify-center shadow-inner">
|
||||||
|
<i class="fa-solid fa-hammer text-white text-3xl"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="font-display text-3xl font-semibold tracking-tighter">Forja</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-x-2 text-sm">
|
||||||
|
<div class="px-3 py-1.5 bg-zinc-900 border border-zinc-800 rounded-2xl flex items-center gap-x-2">
|
||||||
|
<div class="w-2 h-2 bg-emerald-400 rounded-full animate-pulse"></div>
|
||||||
|
<span class="font-medium text-emerald-400 text-xs tracking-wider">v0.2 — EN PRODUCCIÓN</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-zinc-500 text-xs px-2">23 mayo 2026</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="max-w-6xl mx-auto px-6 pt-10 pb-24">
|
||||||
|
|
||||||
|
<!-- Hero -->
|
||||||
|
<div class="flex flex-col lg:flex-row gap-8 items-start">
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="inline-flex items-center gap-x-2 px-3 py-1 rounded-3xl bg-zinc-900 border border-zinc-800 text-xs mb-4">
|
||||||
|
<i class="fa-solid fa-sync fa-spin-pulse text-sky-400"></i>
|
||||||
|
<span class="font-semibold tracking-widest">GRAN REFACTORIZACIÓN COMPLETADA</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="font-display text-6xl lg:text-7xl font-semibold tracking-tighter leading-none">
|
||||||
|
Forja<br>
|
||||||
|
<span class="forja-gradient">está lista</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="mt-4 max-w-lg text-xl text-zinc-400">
|
||||||
|
Plataforma de gobernanza genérica para agentes IA de cualquier tipo.
|
||||||
|
Renombrada, generalizada y con editores gráficos completos.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-x-3 mt-8">
|
||||||
|
<a href="https://github.com/anomalyco/opencode"
|
||||||
|
class="inline-flex items-center gap-x-2 px-5 py-3 rounded-3xl bg-white text-zinc-950 font-semibold text-sm hover:bg-zinc-100 transition-colors">
|
||||||
|
<i class="fa-brands fa-github"></i>
|
||||||
|
<span>Ver en GitHub</span>
|
||||||
|
</a>
|
||||||
|
<button onclick="document.getElementById('proximos-pasos').scrollIntoView({behavior:'smooth'})"
|
||||||
|
class="inline-flex items-center gap-x-2 px-5 py-3 rounded-3xl border border-zinc-700 hover:bg-zinc-900 text-sm font-medium transition-colors">
|
||||||
|
<span>Ver próximos pasos</span>
|
||||||
|
<i class="fa-solid fa-arrow-down"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lg:w-80 w-full">
|
||||||
|
<div class="bg-zinc-900 border border-zinc-800 rounded-3xl p-5">
|
||||||
|
<div class="text-xs uppercase tracking-[1px] text-zinc-500 mb-3 font-medium">Progreso general</div>
|
||||||
|
|
||||||
|
<div class="flex items-baseline gap-x-2">
|
||||||
|
<div class="text-6xl font-semibold tabular-nums tracking-tighter">78</div>
|
||||||
|
<div class="text-2xl font-medium text-zinc-400">/100</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="h-2.5 bg-zinc-800 rounded-full mt-3 overflow-hidden">
|
||||||
|
<div class="h-2.5 bg-gradient-to-r from-sky-400 to-blue-500 rounded-full progress-bar" style="width: 78%"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-4 mt-6 text-center">
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-zinc-500">Core</div>
|
||||||
|
<div class="font-semibold text-xl">100%</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-zinc-500">UI Gráfica</div>
|
||||||
|
<div class="font-semibold text-xl">95%</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-zinc-500">Docs</div>
|
||||||
|
<div class="font-semibold text-xl">65%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Métricas rápidas -->
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mt-12">
|
||||||
|
<div class="bg-zinc-900 border border-zinc-800 rounded-3xl p-4">
|
||||||
|
<div class="flex items-center gap-x-3">
|
||||||
|
<div class="feature-icon bg-emerald-500/10 text-emerald-400"><i class="fa-solid fa-check-double"></i></div>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-zinc-400">Renombrado global</div>
|
||||||
|
<div class="font-semibold">483 → 0 referencias antiguas</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-zinc-900 border border-zinc-800 rounded-3xl p-4">
|
||||||
|
<div class="flex items-center gap-x-3">
|
||||||
|
<div class="feature-icon bg-amber-500/10 text-amber-400"><i class="fa-solid fa-edit"></i></div>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-zinc-400">Editores gráficos</div>
|
||||||
|
<div class="font-semibold">2 páginas nuevas funcionales</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-zinc-900 border border-zinc-800 rounded-3xl p-4">
|
||||||
|
<div class="flex items-center gap-x-3">
|
||||||
|
<div class="feature-icon bg-violet-500/10 text-violet-400"><i class="fa-solid fa-globe"></i></div>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-zinc-400">Generalización</div>
|
||||||
|
<div class="font-semibold">Cualquier tipo de agente</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-zinc-900 border border-zinc-800 rounded-3xl p-4">
|
||||||
|
<div class="flex items-center gap-x-3">
|
||||||
|
<div class="feature-icon bg-sky-500/10 text-sky-400"><i class="fa-solid fa-database"></i></div>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs text-zinc-400">Write APIs</div>
|
||||||
|
<div class="font-semibold">POST /versions + upsert</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Qué se hizo -->
|
||||||
|
<div class="mt-16">
|
||||||
|
<h2 class="section-header text-3xl font-semibold tracking-tight mb-6 flex items-center gap-x-3">
|
||||||
|
<span>Lo que se completó</span>
|
||||||
|
<span class="text-xs px-3 py-1 bg-emerald-400/10 text-emerald-400 rounded-2xl font-mono tracking-wider">FASE 1 + 2 + 3</span>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="grid md:grid-cols-2 gap-4">
|
||||||
|
<!-- Columna 1 -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-6">
|
||||||
|
<div class="font-semibold flex items-center gap-x-2 text-lg">
|
||||||
|
<i class="fa-solid fa-sync text-sky-400"></i>
|
||||||
|
<span>Renombrado completo a "Forja"</span>
|
||||||
|
</div>
|
||||||
|
<ul class="mt-4 text-sm space-y-2 text-zinc-300">
|
||||||
|
<li class="flex gap-x-2"><span class="text-sky-400 mt-0.5">•</span> Paquetes Python: <span class="mono font-medium">forja_core</span> y <span class="mono font-medium">forja_dashboard</span></li>
|
||||||
|
<li class="flex gap-x-2"><span class="text-sky-400 mt-0.5">•</span> Imágenes Docker, contenedores y URLs (<code>forja-core:dev</code>)</li>
|
||||||
|
<li class="flex gap-x-2"><span class="text-sky-400 mt-0.5">•</span> Variables de entorno (<code>FORJA_CORE_URL</code>)</li>
|
||||||
|
<li class="flex gap-x-2"><span class="text-sky-400 mt-0.5">•</span> pyproject, Dockerfiles, docker-compose, Makefile</li>
|
||||||
|
<li class="flex gap-x-2"><span class="text-sky-400 mt-0.5">•</span> 300+ archivos actualizados (código + docs activos)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-6">
|
||||||
|
<div class="font-semibold flex items-center gap-x-2 text-lg">
|
||||||
|
<i class="fa-solid fa-universal-access text-violet-400"></i>
|
||||||
|
<span>Generalización para cualquier tipo de agente</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 text-sm text-zinc-300">
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<div class="px-3 py-1 bg-zinc-800 rounded-2xl text-xs">Campos UI opcionales en AgentDefinition</div>
|
||||||
|
<div class="px-3 py-1 bg-zinc-800 rounded-2xl text-xs">input_label / placeholder / category / tags / icon</div>
|
||||||
|
<div class="px-3 py-1 bg-zinc-800 rounded-2xl text-xs">Execute page 100% genérico</div>
|
||||||
|
<div class="px-3 py-1 bg-zinc-800 rounded-2xl text-xs">Posicionamiento como plano de control reutilizable</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Columna 2 -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-6">
|
||||||
|
<div class="font-semibold flex items-center gap-x-2 text-lg">
|
||||||
|
<i class="fa-solid fa-pencil-ruler text-amber-400"></i>
|
||||||
|
<span>Editores gráficos completos (la gran novedad)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 grid grid-cols-1 gap-3">
|
||||||
|
<div class="bg-zinc-950 border border-zinc-800 p-4 rounded-2xl">
|
||||||
|
<div class="font-medium text-amber-300 flex items-center gap-x-2">
|
||||||
|
<i class="fa-solid fa-hammer"></i>
|
||||||
|
<span>6_🔨_Forjar_Agente.py</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-zinc-400 mt-1">Form completo + LLM config + schema JSON + multiselect de guardrails + fork + versionado</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-zinc-950 border border-zinc-800 p-4 rounded-2xl">
|
||||||
|
<div class="font-medium text-amber-300 flex items-center gap-x-2">
|
||||||
|
<i class="fa-solid fa-shield-halved"></i>
|
||||||
|
<span>7_🛡️_Forjar_Politica.py</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-zinc-400 mt-1">Constructor dinámico de validadores (input/output) + /validators endpoint + configs JSON</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-6">
|
||||||
|
<div class="font-semibold text-lg">Backend de escritura</div>
|
||||||
|
<div class="text-sm mt-3 space-y-1.5 text-zinc-300">
|
||||||
|
<div>✓ <code class="mono text-xs">POST /agents/{name}/versions</code> + upsert</div>
|
||||||
|
<div>✓ <code class="mono text-xs">POST /policies/{name}/versions</code> + upsert simétrico</div>
|
||||||
|
<div>✓ <code class="mono text-xs">GET /policies/validators</code> (metadata para UI)</div>
|
||||||
|
<div>✓ <code class="mono text-xs">FileSystemPolicyStore.upsert_version()</code></div>
|
||||||
|
<div>✓ Docker mounts cambiados a <strong>:rw</strong></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Estado actual -->
|
||||||
|
<div class="mt-16">
|
||||||
|
<h2 class="section-header text-3xl font-semibold tracking-tight mb-6">Estado actual (23 mayo 2026)</h2>
|
||||||
|
|
||||||
|
<div class="bg-zinc-900 border border-zinc-800 rounded-3xl p-7">
|
||||||
|
<div class="grid md:grid-cols-3 gap-8">
|
||||||
|
<div>
|
||||||
|
<div class="uppercase text-xs font-semibold tracking-wider text-emerald-400">Compila y funciona</div>
|
||||||
|
<ul class="mt-3 space-y-2 text-sm">
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-check text-emerald-400 mt-0.5"></i> <span>Todos los imports y paquetes renombrados</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-check text-emerald-400 mt-0.5"></i> <span>py_compile 100% limpio</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-check text-emerald-400 mt-0.5"></i> <span>Editores guardan YAMLs reales</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-check text-emerald-400 mt-0.5"></i> <span>Execute page usa metadata del agente</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="uppercase text-xs font-semibold tracking-wider text-amber-400">Listo para usar</div>
|
||||||
|
<ul class="mt-3 space-y-2 text-sm">
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-check text-amber-400 mt-0.5"></i> <span><code>docker compose up</code> (con :rw)</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-check text-amber-400 mt-0.5"></i> <span>Registro + Ejecución + Aprobaciones</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-check text-amber-400 mt-0.5"></i> <span>Editores en barra lateral (páginas 6 y 7)</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-check text-amber-400 mt-0.5"></i> <span>README y docs principales actualizados</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="uppercase text-xs font-semibold tracking-wider text-zinc-400">Pendiente de verificación completa</div>
|
||||||
|
<ul class="mt-3 space-y-2 text-sm text-zinc-400">
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-clock mt-0.5"></i> <span>make test-all (requiere deps pesadas)</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-clock mt-0.5"></i> <span>Smoke test con Docker real (guardrails-ai)</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-clock mt-0.5"></i> <span>Actualización de walkthrough.html</span></li>
|
||||||
|
<li class="flex items-start gap-x-2"><i class="fa-solid fa-clock mt-0.5"></i> <span>Tests específicos de los nuevos endpoints</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Próximos pasos -->
|
||||||
|
<div id="proximos-pasos" class="mt-16">
|
||||||
|
<h2 class="section-header text-3xl font-semibold tracking-tight mb-6">Próximos pasos recomendados</h2>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<!-- Paso 1 -->
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-5 flex gap-5 items-start">
|
||||||
|
<div class="w-7 h-7 flex-shrink-0 rounded-2xl bg-blue-500 flex items-center justify-center text-xs font-bold text-white mt-0.5">1</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-semibold">Verificación completa de calidad</div>
|
||||||
|
<div class="text-sm text-zinc-400 mt-1">Ejecutar <span class="mono bg-zinc-950 px-1.5 py-px rounded">make install && make lint && make test-all</span> + smoke con docker compose en entorno limpio.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Paso 2 -->
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-5 flex gap-5 items-start">
|
||||||
|
<div class="w-7 h-7 flex-shrink-0 rounded-2xl bg-blue-500 flex items-center justify-center text-xs font-bold text-white mt-0.5">2</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-semibold">Mejora de los editores (UX)</div>
|
||||||
|
<div class="text-sm text-zinc-400 mt-1">Añadir editor JSON más amigable (ace o monaco), validación en tiempo real de schemas, preview de agente antes de guardar, y botón “Probar inmediatamente”.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Paso 3 -->
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-5 flex gap-5 items-start">
|
||||||
|
<div class="w-7 h-7 flex-shrink-0 rounded-2xl bg-blue-500 flex items-center justify-center text-xs font-bold text-white mt-0.5">3</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-semibold">Añadir más plantillas de agentes</div>
|
||||||
|
<div class="text-sm text-zinc-400 mt-1">Crear 2-3 agentes genéricos de ejemplo (resumidor, revisor de código, chatbot con guardrails) + sus políticas base.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-5 flex gap-5 items-start">
|
||||||
|
<div class="w-7 h-7 flex-shrink-0 rounded-2xl bg-blue-500 flex items-center justify-center text-xs font-bold text-white mt-0.5">4</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-semibold">Soporte de namespaces / multi-proyecto</div>
|
||||||
|
<div class="text-sm text-zinc-400 mt-1">Estructura opcional <span class="mono text-xs">agents/<proyecto>/<agente></span> para que una sola instancia de Forja sirva a múltiples equipos.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card bg-zinc-900 border border-zinc-800 rounded-3xl p-5 flex gap-5 items-start">
|
||||||
|
<div class="w-7 h-7 flex-shrink-0 rounded-2xl bg-blue-500 flex items-center justify-center text-xs font-bold text-white mt-0.5">5</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-semibold">Documentación y walkthrough actualizado</div>
|
||||||
|
<div class="text-sm text-zinc-400 mt-1">Regenerar o actualizar <span class="mono">docs/walkthrough.html</span> y <span class="mono">docs/explicacion.md</span> con los editores y el nuevo nombre.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Notas técnicas -->
|
||||||
|
<div class="mt-16 text-xs text-zinc-500 border-t border-zinc-800 pt-8">
|
||||||
|
<div class="flex flex-wrap gap-x-8 gap-y-2">
|
||||||
|
<div><strong>Paquetes:</strong> <span class="mono">forja_core</span> / <span class="mono">forja_dashboard</span></div>
|
||||||
|
<div><strong>Python:</strong> 3.11+</div>
|
||||||
|
<div><strong>UI:</strong> Streamlit 1.38 + Tailwind (editores)</div>
|
||||||
|
<div><strong>Runtime:</strong> LangGraph + FastAPI</div>
|
||||||
|
<div><strong>Persistencia:</strong> YAML versionado + SQLite checkpoints</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3 text-[10px] text-zinc-600">Este documento fue generado automáticamente tras la sesión de refactorización del 23 de mayo de 2026.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Tailwind script
|
||||||
|
function initializeTailwind() {
|
||||||
|
document.documentElement.style.setProperty('--accent', '#0ea5e9');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple confetti on load for celebration
|
||||||
|
function celebrate() {
|
||||||
|
if (Math.random() > 0.7) {
|
||||||
|
console.log('%c[Forja] ¡Refactorización completada con éxito!', 'color:#64748b;font-size:9px');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
initializeTailwind();
|
||||||
|
celebrate();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keyboard hint
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === '/' && document.activeElement.tagName === 'BODY') {
|
||||||
|
e.preventDefault();
|
||||||
|
const next = document.getElementById('proximos-pasos');
|
||||||
|
if (next) next.scrollIntoView({behavior: 'smooth'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+21
-21
@@ -3,8 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="Walkthrough completo de AgentForge — de alto a bajo nivel, con diagramas.">
|
<meta name="description" content="Walkthrough completo de Forja — de alto a bajo nivel, con diagramas.">
|
||||||
<title>AgentForge · Walkthrough</title>
|
<title>Forja · Walkthrough</title>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
try {
|
try {
|
||||||
@@ -255,7 +255,7 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
|
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<button class="icon-btn" id="menuBtn" aria-label="Abrir índice">☰</button>
|
<button class="icon-btn" id="menuBtn" aria-label="Abrir índice">☰</button>
|
||||||
<span class="tt">🛡️ AgentForge · Walkthrough</span>
|
<span class="tt">🛡️ Forja · Walkthrough</span>
|
||||||
<button class="icon-btn" id="themeBtnM" aria-label="Cambiar tema" style="margin-left:auto">◐</button>
|
<button class="icon-btn" id="themeBtnM" aria-label="Cambiar tema" style="margin-left:auto">◐</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="scrim" id="scrim"></div>
|
<div class="scrim" id="scrim"></div>
|
||||||
@@ -266,7 +266,7 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
<div class="brand">
|
<div class="brand">
|
||||||
<span class="logo">🛡️</span>
|
<span class="logo">🛡️</span>
|
||||||
<span>
|
<span>
|
||||||
<span class="t1">AgentForge</span>
|
<span class="t1">Forja</span>
|
||||||
<span class="t2">Walkthrough · de alto a bajo nivel</span>
|
<span class="t2">Walkthrough · de alto a bajo nivel</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -274,7 +274,7 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
<button class="btn" id="themeBtn" style="flex:1; justify-content:center">◐ Tema</button>
|
<button class="btn" id="themeBtn" style="flex:1; justify-content:center">◐ Tema</button>
|
||||||
<button class="btn" onclick="window.print()" style="flex:1; justify-content:center">⎙ Imprimir</button>
|
<button class="btn" onclick="window.print()" style="flex:1; justify-content:center">⎙ Imprimir</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">HTML autocontenido · <code>docs/walkthrough.html</code> · AgentForge v0.1.0</div>
|
<div class="meta">HTML autocontenido · <code>docs/walkthrough.html</code> · Forja v0.1.0</div>
|
||||||
<nav>
|
<nav>
|
||||||
<ul class="toc" id="toc">
|
<ul class="toc" id="toc">
|
||||||
<li class="group">Panorama</li>
|
<li class="group">Panorama</li>
|
||||||
@@ -314,7 +314,7 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
|
|
||||||
<header class="hero">
|
<header class="hero">
|
||||||
<div class="crumbs">Plataforma de gobernanza de agentes IA · documento generado para entender el proyecto completo</div>
|
<div class="crumbs">Plataforma de gobernanza de agentes IA · documento generado para entender el proyecto completo</div>
|
||||||
<h1>🛡️ AgentForge — Walkthrough</h1>
|
<h1>🛡️ Forja — Walkthrough</h1>
|
||||||
<p class="tagline">Catalogación y versionado de agentes y políticas, guardrails en runtime, ejecución <em>stateful</em> con Human-in-the-Loop, observabilidad y trazabilidad de extremo a extremo. Aquí está todo: <strong>de la vista de pájaro al cableado de cada módulo</strong>, con diagramas.</p>
|
<p class="tagline">Catalogación y versionado de agentes y políticas, guardrails en runtime, ejecución <em>stateful</em> con Human-in-the-Loop, observabilidad y trazabilidad de extremo a extremo. Aquí está todo: <strong>de la vista de pájaro al cableado de cada módulo</strong>, con diagramas.</p>
|
||||||
<div class="badges">
|
<div class="badges">
|
||||||
<span class="badge k">🐍 <b>Python 3.11+</b></span>
|
<span class="badge k">🐍 <b>Python 3.11+</b></span>
|
||||||
@@ -338,7 +338,7 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
<section id="intro">
|
<section id="intro">
|
||||||
<h2><span class="kicker">00</span> Qué es y por qué</h2>
|
<h2><span class="kicker">00</span> Qué es y por qué</h2>
|
||||||
<p class="lead">Poner agentes de IA en producción <strong>sin una capa de gobierno</strong> produce sistemas opacos: prompts que cambian sin historial, validaciones inconsistentes, acciones de alto impacto sin supervisión y ninguna auditoría de lo que decidió el agente.</p>
|
<p class="lead">Poner agentes de IA en producción <strong>sin una capa de gobierno</strong> produce sistemas opacos: prompts que cambian sin historial, validaciones inconsistentes, acciones de alto impacto sin supervisión y ninguna auditoría de lo que decidió el agente.</p>
|
||||||
<p><strong>AgentForge es el "plano de control" que pones <em>delante</em> de tus agentes</strong> antes de dejarlos tocar nada importante. No es un framework para <em>construir</em> agentes; es la capa que los <strong>cataloga, versiona, valida, ejecuta de forma supervisada y audita</strong>.</p>
|
<p><strong>Forja es el "plano de control" que pones <em>delante</em> de tus agentes</strong> antes de dejarlos tocar nada importante. No es un framework para <em>construir</em> agentes; es la capa que los <strong>cataloga, versiona, valida, ejecuta de forma supervisada y audita</strong>.</p>
|
||||||
<div class="callout key">
|
<div class="callout key">
|
||||||
<div class="ct">🧭 El caso de ejemplo del repo</div>
|
<div class="ct">🧭 El caso de ejemplo del repo</div>
|
||||||
<p>Un agente de operaciones de telco — <code>incident_analyzer</code>: recibe la descripción de un incidente de plataforma de voz (caída de registros SIP, degradación de MOS, saturación de HSS…) y propone acciones con <strong>análisis de riesgo</strong> y <strong>plan de rollback</strong>. Las acciones de riesgo alto quedan <strong>pausadas esperando aprobación humana</strong>. Todo queda registrado con un <code>trace_id</code>.</p>
|
<p>Un agente de operaciones de telco — <code>incident_analyzer</code>: recibe la descripción de un incidente de plataforma de voz (caída de registros SIP, degradación de MOS, saturación de HSS…) y propone acciones con <strong>análisis de riesgo</strong> y <strong>plan de rollback</strong>. Las acciones de riesgo alto quedan <strong>pausadas esperando aprobación humana</strong>. Todo queda registrado con un <code>trace_id</code>.</p>
|
||||||
@@ -393,10 +393,10 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
<!-- ============================================================= -->
|
<!-- ============================================================= -->
|
||||||
<section id="vista">
|
<section id="vista">
|
||||||
<h2><span class="kicker">02</span> Vista de pájaro: dos servicios</h2>
|
<h2><span class="kicker">02</span> Vista de pájaro: dos servicios</h2>
|
||||||
<p>AgentForge son <strong>dos procesos</strong> que se hablan por HTTP/JSON, levantados por <code>docker-compose</code>:</p>
|
<p>Forja son <strong>dos procesos</strong> que se hablan por HTTP/JSON, levantados por <code>docker-compose</code>:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong><code>agentforge-core</code></strong> (FastAPI, puerto <strong>8000</strong>) — todo el dominio: registry de agentes, motor de guardrails, runtime de ejecución, persistencia. <em>No tiene UI.</em></li>
|
<li><strong><code>forja-core</code></strong> (FastAPI, puerto <strong>8000</strong>) — todo el dominio: registry de agentes, motor de guardrails, runtime de ejecución, persistencia. <em>No tiene UI.</em></li>
|
||||||
<li><strong><code>agentforge-dashboard</code></strong> (Streamlit, puerto <strong>8501</strong>) — una consola visual con cinco páginas. <strong>No contiene lógica de negocio</strong>: es un cliente HTTP del core.</li>
|
<li><strong><code>forja-dashboard</code></strong> (Streamlit, puerto <strong>8501</strong>) — una consola visual con cinco páginas. <strong>No contiene lógica de negocio</strong>: es un cliente HTTP del core.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>La separación importa: el core podría servir a una CLI, a otro servicio, a un pipeline… el dashboard es solo una de las caras posibles. (Y este documento es otra cara más: el HTML que estás leyendo.)</p>
|
<p>La separación importa: el core podría servir a una CLI, a otro servicio, a un pipeline… el dashboard es solo una de las caras posibles. (Y este documento es otra cara más: el HTML que estás leyendo.)</p>
|
||||||
|
|
||||||
@@ -416,14 +416,14 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
|
|
||||||
<!-- dashboard -->
|
<!-- dashboard -->
|
||||||
<rect class="dg-box accent" x="56" y="60" width="300" height="106" rx="13"/>
|
<rect class="dg-box accent" x="56" y="60" width="300" height="106" rx="13"/>
|
||||||
<text class="dg-t" x="206" y="92" text-anchor="middle">agentforge-dashboard</text>
|
<text class="dg-t" x="206" y="92" text-anchor="middle">forja-dashboard</text>
|
||||||
<text class="dg-s" x="206" y="112" text-anchor="middle">Streamlit · :8501 · "la consola"</text>
|
<text class="dg-s" x="206" y="112" text-anchor="middle">Streamlit · :8501 · "la consola"</text>
|
||||||
<text class="dg-m dim" x="206" y="132" text-anchor="middle">5 páginas · sin lógica de negocio</text>
|
<text class="dg-m dim" x="206" y="132" text-anchor="middle">5 páginas · sin lógica de negocio</text>
|
||||||
<text class="dg-m dim" x="206" y="150" text-anchor="middle">CoreClient (httpx) → habla solo HTTP</text>
|
<text class="dg-m dim" x="206" y="150" text-anchor="middle">CoreClient (httpx) → habla solo HTTP</text>
|
||||||
|
|
||||||
<!-- core -->
|
<!-- core -->
|
||||||
<rect class="dg-box violet" x="600" y="60" width="344" height="106" rx="13"/>
|
<rect class="dg-box violet" x="600" y="60" width="344" height="106" rx="13"/>
|
||||||
<text class="dg-t" x="772" y="92" text-anchor="middle">agentforge-core</text>
|
<text class="dg-t" x="772" y="92" text-anchor="middle">forja-core</text>
|
||||||
<text class="dg-s" x="772" y="112" text-anchor="middle">FastAPI · :8000 · "el cerebro"</text>
|
<text class="dg-s" x="772" y="112" text-anchor="middle">FastAPI · :8000 · "el cerebro"</text>
|
||||||
<text class="dg-m dim" x="772" y="132" text-anchor="middle">dominio · runtime · guardrails · persistencia</text>
|
<text class="dg-m dim" x="772" y="132" text-anchor="middle">dominio · runtime · guardrails · persistencia</text>
|
||||||
<text class="dg-m dim" x="772" y="150" text-anchor="middle">/health · /agents · /executions · /policies · /violations</text>
|
<text class="dg-m dim" x="772" y="150" text-anchor="middle">/health · /agents · /executions · /policies · /violations</text>
|
||||||
@@ -513,7 +513,7 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
<!-- ============================================================= -->
|
<!-- ============================================================= -->
|
||||||
<section id="modulos">
|
<section id="modulos">
|
||||||
<h2><span class="kicker">04</span> El grafo de módulos (quién depende de quién)</h2>
|
<h2><span class="kicker">04</span> El grafo de módulos (quién depende de quién)</h2>
|
||||||
<p>El código del core vive bajo <code>core/src/agentforge_core/</code>. Es un <strong>DAG</strong>: las capas de abajo no importan nada de las de arriba. El "nivel" es la profundidad topológica. Lee de abajo hacia arriba: el vocabulario primero, la composición de la app al final.</p>
|
<p>El código del core vive bajo <code>core/src/forja_core/</code>. Es un <strong>DAG</strong>: las capas de abajo no importan nada de las de arriba. El "nivel" es la profundidad topológica. Lee de abajo hacia arriba: el vocabulario primero, la composición de la app al final.</p>
|
||||||
<div class="bands">
|
<div class="bands">
|
||||||
<div class="band">
|
<div class="band">
|
||||||
<div class="lvl"><b>6</b><span>app</span></div>
|
<div class="lvl"><b>6</b><span>app</span></div>
|
||||||
@@ -980,7 +980,7 @@ details.dd .body{padding:4px 16px 14px}
|
|||||||
|
|
||||||
<p>El estado que fluye por el grafo es un <code>TypedDict</code>. <code>decision_path</code> usa un <em>reducer</em> (<code>Annotated[list, operator.add]</code>) para que cada nodo <strong>añada</strong> pasos en vez de sobrescribir:</p>
|
<p>El estado que fluye por el grafo es un <code>TypedDict</code>. <code>decision_path</code> usa un <em>reducer</em> (<code>Annotated[list, operator.add]</code>) para que cada nodo <strong>añada</strong> pasos en vez de sobrescribir:</p>
|
||||||
<div class="code">
|
<div class="code">
|
||||||
<div class="hd"><span class="dot"></span><span class="fn">core/src/agentforge_core/runtime/state.py</span><span class="lang">python</span></div>
|
<div class="hd"><span class="dot"></span><span class="fn">core/src/forja_core/runtime/state.py</span><span class="lang">python</span></div>
|
||||||
<pre><span class="k">class</span> <span class="y">AgentState</span>(TypedDict, total=<span class="k">False</span>):
|
<pre><span class="k">class</span> <span class="y">AgentState</span>(TypedDict, total=<span class="k">False</span>):
|
||||||
trace_id: <span class="y">str</span>; agent_name: <span class="y">str</span>; agent_version: <span class="y">str</span>; user_input: <span class="y">str</span>
|
trace_id: <span class="y">str</span>; agent_name: <span class="y">str</span>; agent_version: <span class="y">str</span>; user_input: <span class="y">str</span>
|
||||||
messages: <span class="y">list</span>[<span class="y">dict</span>]; raw_llm_output: <span class="y">str</span> | <span class="k">None</span>; parsed_output: <span class="y">dict</span> | <span class="k">None</span>
|
messages: <span class="y">list</span>[<span class="y">dict</span>]; raw_llm_output: <span class="y">str</span> | <span class="k">None</span>; parsed_output: <span class="y">dict</span> | <span class="k">None</span>
|
||||||
@@ -1097,7 +1097,7 @@ g.add_edge(<span class="s">"finalize"</span>, END)
|
|||||||
<!-- divider -->
|
<!-- divider -->
|
||||||
<rect class="dg-band" x="14" y="460" width="1092" height="34" rx="6"/>
|
<rect class="dg-band" x="14" y="460" width="1092" height="34" rx="6"/>
|
||||||
<line class="dg-divider" x1="14" y1="460" x2="1106" y2="460"/><line class="dg-divider" x1="14" y1="494" x2="1106" y2="494"/>
|
<line class="dg-divider" x1="14" y1="460" x2="1106" y2="460"/><line class="dg-divider" x1="14" y1="494" x2="1106" y2="494"/>
|
||||||
<text class="dg-s" x="560" y="481" text-anchor="middle" style="font-style:italic">· · · más tarde — incluso tras reiniciar agentforge-core: el estado pausado sigue en checkpoints.sqlite · · ·</text>
|
<text class="dg-s" x="560" y="481" text-anchor="middle" style="font-style:italic">· · · más tarde — incluso tras reiniciar forja-core: el estado pausado sigue en checkpoints.sqlite · · ·</text>
|
||||||
|
|
||||||
<!-- ACT 2 label -->
|
<!-- ACT 2 label -->
|
||||||
<rect class="dg-box ok" x="18" y="504" width="232" height="22" rx="6"/><text class="dg-t sm" x="28" y="520" style="font-size:11.5px;fill:var(--ok)">ACTO 2 · el humano decide → completed</text>
|
<rect class="dg-box ok" x="18" y="504" width="232" height="22" rx="6"/><text class="dg-t sm" x="28" y="520" style="font-size:11.5px;fill:var(--ok)">ACTO 2 · el humano decide → completed</text>
|
||||||
@@ -1222,7 +1222,7 @@ curl -s localhost:8000/executions/$TRACE/approve \
|
|||||||
<!-- ============================================================= -->
|
<!-- ============================================================= -->
|
||||||
<section id="persistencia">
|
<section id="persistencia">
|
||||||
<h2><span class="kicker">11</span> Persistencia: cuatro formas, cuatro razones</h2>
|
<h2><span class="kicker">11</span> Persistencia: cuatro formas, cuatro razones</h2>
|
||||||
<p>AgentForge no usa una sola base de datos; usa la herramienta adecuada para cada cosa.</p>
|
<p>Forja no usa una sola base de datos; usa la herramienta adecuada para cada cosa.</p>
|
||||||
|
|
||||||
<figure class="diagram">
|
<figure class="diagram">
|
||||||
<svg viewBox="0 0 1020 420" role="img" aria-label="Mapa de persistencia: quién escribe y lee qué">
|
<svg viewBox="0 0 1020 420" role="img" aria-label="Mapa de persistencia: quién escribe y lee qué">
|
||||||
@@ -1230,7 +1230,7 @@ curl -s localhost:8000/executions/$TRACE/approve \
|
|||||||
|
|
||||||
<!-- core in the middle -->
|
<!-- core in the middle -->
|
||||||
<rect class="dg-box violet" x="396" y="170" width="228" height="80" rx="12"/>
|
<rect class="dg-box violet" x="396" y="170" width="228" height="80" rx="12"/>
|
||||||
<text class="dg-t sm" x="510" y="196" text-anchor="middle">agentforge-core</text>
|
<text class="dg-t sm" x="510" y="196" text-anchor="middle">forja-core</text>
|
||||||
<text class="dg-s" x="510" y="214" text-anchor="middle">registry · orchestrator</text>
|
<text class="dg-s" x="510" y="214" text-anchor="middle">registry · orchestrator</text>
|
||||||
<text class="dg-s" x="510" y="230" text-anchor="middle">api/persistence · runtime/checkpointer</text>
|
<text class="dg-s" x="510" y="230" text-anchor="middle">api/persistence · runtime/checkpointer</text>
|
||||||
|
|
||||||
@@ -1489,11 +1489,11 @@ curl -s localhost:8000/executions/$TRACE/approve \
|
|||||||
<div>
|
<div>
|
||||||
<h4>El ciclo de vida del proceso core</h4>
|
<h4>El ciclo de vida del proceso core</h4>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Importar <code>agentforge_core.main</code> ejecuta <code>app = create_app()</code>: <code>Settings()</code> → <code>configure_logging(level)</code> → <code>FastAPI(...)</code> → <code>add_middleware(TraceIdMiddleware)</code> → registra <code>GET /health</code> → importa los routers → <code>include_router</code> (agents, executions ×2, policies, violations).</li>
|
<li>Importar <code>forja_core.main</code> ejecuta <code>app = create_app()</code>: <code>Settings()</code> → <code>configure_logging(level)</code> → <code>FastAPI(...)</code> → <code>add_middleware(TraceIdMiddleware)</code> → registra <code>GET /health</code> → importa los routers → <code>include_router</code> (agents, executions ×2, policies, violations).</li>
|
||||||
<li>Las dependencias (<code>get_registry</code>, <code>get_policy_store</code>, <code>get_llm_provider</code>, <code>get_guardrail_engine</code>, <code>get_orchestrator</code>) <strong>no</strong> se construyen aún; se construyen y cachean en la <strong>primera request</strong> que las inyecta.</li>
|
<li>Las dependencias (<code>get_registry</code>, <code>get_policy_store</code>, <code>get_llm_provider</code>, <code>get_guardrail_engine</code>, <code>get_orchestrator</code>) <strong>no</strong> se construyen aún; se construyen y cachean en la <strong>primera request</strong> que las inyecta.</li>
|
||||||
<li>Cada request: <code>TraceIdMiddleware.dispatch</code> → router → resuelve <code>Depends(...)</code> (que puede disparar la construcción perezosa) → handler → respuesta con <code>X-Trace-Id</code>.</li>
|
<li>Cada request: <code>TraceIdMiddleware.dispatch</code> → router → resuelve <code>Depends(...)</code> (que puede disparar la construcción perezosa) → handler → respuesta con <code>X-Trace-Id</code>.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p class="muted">En contenedores: <code>uvicorn agentforge_core.main:app --host 0.0.0.0 --port 8000</code>; <code>HEALTHCHECK</code> → <code>curl /health</code>; monta <code>./agents:ro</code>, <code>./policies:ro</code>, <code>./data:rw</code>; <code>DATA_DIR=/app/data</code>, etc. El dashboard depende de <code>core: service_healthy</code> y usa <code>AGENTFORGE_CORE_URL=http://core:8000</code>. La imagen del core instala <code>en_core_web_sm</code> de spaCy para Presidio.</p>
|
<p class="muted">En contenedores: <code>uvicorn forja_core.main:app --host 0.0.0.0 --port 8000</code>; <code>HEALTHCHECK</code> → <code>curl /health</code>; monta <code>./agents:ro</code>, <code>./policies:ro</code>, <code>./data:rw</code>; <code>DATA_DIR=/app/data</code>, etc. El dashboard depende de <code>core: service_healthy</code> y usa <code>FORJA_CORE_URL=http://core:8000</code>. La imagen del core instala <code>en_core_web_sm</code> de spaCy para Presidio.</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4>Variables de entorno (<code>config.py</code> · <code>.env</code>)</h4>
|
<h4>Variables de entorno (<code>config.py</code> · <code>.env</code>)</h4>
|
||||||
@@ -1510,7 +1510,7 @@ curl -s localhost:8000/executions/$TRACE/approve \
|
|||||||
<tr><td><code>DATA_DIR</code></td><td><code>./data</code></td><td>checkpointer · índice · JSONL</td></tr>
|
<tr><td><code>DATA_DIR</code></td><td><code>./data</code></td><td>checkpointer · índice · JSONL</td></tr>
|
||||||
<tr><td><code>AGENTS_DIR</code></td><td><code>./agents</code></td><td><code>FileSystemAgentRegistry</code></td></tr>
|
<tr><td><code>AGENTS_DIR</code></td><td><code>./agents</code></td><td><code>FileSystemAgentRegistry</code></td></tr>
|
||||||
<tr><td><code>POLICIES_DIR</code></td><td><code>./policies</code></td><td><code>FileSystemPolicyStore</code></td></tr>
|
<tr><td><code>POLICIES_DIR</code></td><td><code>./policies</code></td><td><code>FileSystemPolicyStore</code></td></tr>
|
||||||
<tr><td><code>AGENTFORGE_CORE_URL</code></td><td><code>http://core:8000</code></td><td>el dashboard (<code>CoreClient</code>)</td></tr>
|
<tr><td><code>FORJA_CORE_URL</code></td><td><code>http://core:8000</code></td><td>el dashboard (<code>CoreClient</code>)</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -1721,7 +1721,7 @@ open docs/walkthrough.html <span class="c"># macOS</span>
|
|||||||
|
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p><strong>AgentForge · Walkthrough.</strong> Documento autocontenido (sin recursos externos). Acompaña a <code>README.md</code>, <code>ARCHITECTURE.md</code>, <code>docs/explicacion.md</code> (narrativa) y <code>docs/componentes.md</code> (referencia de cableado). Refleja el repo en <code>v0.1.0</code>.</p>
|
<p><strong>Forja · Walkthrough.</strong> Documento autocontenido (sin recursos externos). Acompaña a <code>README.md</code>, <code>ARCHITECTURE.md</code>, <code>docs/explicacion.md</code> (narrativa) y <code>docs/componentes.md</code> (referencia de cableado). Refleja el repo en <code>v0.1.0</code>.</p>
|
||||||
<p class="muted">Documento HTML autocontenido (<code>docs/walkthrough.html</code>) — sin recursos externos: ábrelo en cualquier navegador.</p>
|
<p class="muted">Documento HTML autocontenido (<code>docs/walkthrough.html</code>) — sin recursos externos: ábrelo en cualquier navegador.</p>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+3
-3
@@ -3,7 +3,7 @@ requires = ["setuptools>=68", "wheel"]
|
|||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "agentforge"
|
name = "forja"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Plataforma profesional de gobernanza de agentes IA"
|
description = "Plataforma profesional de gobernanza de agentes IA"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
@@ -20,10 +20,10 @@ ignore = ["E501"]
|
|||||||
[tool.ruff.lint.per-file-ignores]
|
[tool.ruff.lint.per-file-ignores]
|
||||||
# Streamlit exige que las páginas se llamen "<n>_<emoji>_<Label>.py" para la
|
# Streamlit exige que las páginas se llamen "<n>_<emoji>_<Label>.py" para la
|
||||||
# navegación multipágina; eso choca con N999 (nombre de módulo no válido).
|
# navegación multipágina; eso choca con N999 (nombre de módulo no válido).
|
||||||
"dashboard/src/agentforge_dashboard/pages/*" = ["N999"]
|
"dashboard/src/forja_dashboard/pages/*" = ["N999"]
|
||||||
|
|
||||||
[tool.ruff.lint.isort]
|
[tool.ruff.lint.isort]
|
||||||
known-first-party = ["agentforge_core", "agentforge_dashboard"]
|
known-first-party = ["forja_core", "forja_dashboard"]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.11"
|
python_version = "3.11"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_core.api import deps
|
from forja_core.api import deps
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -12,7 +12,7 @@ def integration_client(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): # type
|
|||||||
"""Cliente FastAPI con env apuntando a los assets reales del repo."""
|
"""Cliente FastAPI con env apuntando a los assets reales del repo."""
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from agentforge_core.main import create_app
|
from forja_core.main import create_app
|
||||||
|
|
||||||
repo_root = Path(__file__).parent.parent.parent
|
repo_root = Path(__file__).parent.parent.parent
|
||||||
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ EXAMPLES = (
|
|||||||
def test_resume_tras_recreacion_del_app(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_resume_tras_recreacion_del_app(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from agentforge_core.api import deps
|
from forja_core.api import deps
|
||||||
from agentforge_core.main import create_app
|
from forja_core.main import create_app
|
||||||
|
|
||||||
repo_root = Path(__file__).parent.parent.parent
|
repo_root = Path(__file__).parent.parent.parent
|
||||||
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from agentforge_core.api import deps
|
from forja_core.api import deps
|
||||||
from agentforge_core.main import create_app
|
from forja_core.main import create_app
|
||||||
|
|
||||||
FIXTURES = Path(__file__).parent.parent / "fixtures"
|
FIXTURES = Path(__file__).parent.parent / "fixtures"
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from agentforge_core.api import deps
|
from forja_core.api import deps
|
||||||
from agentforge_core.main import create_app
|
from forja_core.main import create_app
|
||||||
|
|
||||||
FIXTURES = Path(__file__).parent.parent / "fixtures"
|
FIXTURES = Path(__file__).parent.parent / "fixtures"
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ from uuid import uuid4
|
|||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from agentforge_core.api import deps
|
from forja_core.api import deps
|
||||||
from agentforge_core.api.persistence import append_violation
|
from forja_core.api.persistence import append_violation
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
from agentforge_core.main import create_app
|
from forja_core.main import create_app
|
||||||
|
|
||||||
FIXTURES = Path(__file__).parent.parent / "fixtures"
|
FIXTURES = Path(__file__).parent.parent / "fixtures"
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from agentforge_core.config import Settings
|
from forja_core.config import Settings
|
||||||
|
|
||||||
|
|
||||||
def test_settings_defaults_seguros() -> None:
|
def test_settings_defaults_seguros() -> None:
|
||||||
@@ -16,6 +16,6 @@ def test_settings_defaults_seguros() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_settings_data_dir_se_resuelve() -> None:
|
def test_settings_data_dir_se_resuelve() -> None:
|
||||||
with patch.dict(os.environ, {"DATA_DIR": "/tmp/agentforge-test"}, clear=True):
|
with patch.dict(os.environ, {"DATA_DIR": "/tmp/forja-test"}, clear=True):
|
||||||
s = Settings(_env_file=None) # type: ignore[call-arg]
|
s = Settings(_env_file=None) # type: ignore[call-arg]
|
||||||
assert s.data_dir == Path("/tmp/agentforge-test")
|
assert s.data_dir == Path("/tmp/forja-test")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from collections.abc import Callable
|
|||||||
import httpx
|
import httpx
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_dashboard.client import CoreClient
|
from forja_dashboard.client import CoreClient
|
||||||
|
|
||||||
|
|
||||||
def _client_with(handler: Callable[[httpx.Request], httpx.Response]) -> CoreClient:
|
def _client_with(handler: Callable[[httpx.Request], httpx.Response]) -> CoreClient:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from datetime import UTC, datetime
|
|||||||
import pytest
|
import pytest
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from agentforge_core.domain.agent import (
|
from forja_core.domain.agent import (
|
||||||
AgentDefinition,
|
AgentDefinition,
|
||||||
AgentVersionMeta,
|
AgentVersionMeta,
|
||||||
LLMConfig,
|
LLMConfig,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from agentforge_core.domain.execution import (
|
from forja_core.domain.execution import (
|
||||||
AgentExecution,
|
AgentExecution,
|
||||||
AgentExecutionSummary,
|
AgentExecutionSummary,
|
||||||
DecisionStep,
|
DecisionStep,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from uuid import uuid4
|
|||||||
import pytest
|
import pytest
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
|
|
||||||
|
|
||||||
def test_guardrail_violation_minimo_valido() -> None:
|
def test_guardrail_violation_minimo_valido() -> None:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Tests del modelo PolicyDefinition."""
|
"""Tests del modelo PolicyDefinition."""
|
||||||
|
|
||||||
from agentforge_core.domain.policy import PolicyDefinition, PolicyValidator
|
from forja_core.domain.policy import PolicyDefinition, PolicyValidator
|
||||||
|
|
||||||
|
|
||||||
def test_policy_definition_default_fail_closed() -> None:
|
def test_policy_definition_default_fail_closed() -> None:
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ from uuid import uuid4
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_core.domain.policy import PolicyDefinition, PolicyValidator
|
from forja_core.domain.policy import PolicyDefinition, PolicyValidator
|
||||||
from agentforge_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
from forja_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ from uuid import uuid4
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
from agentforge_core.guardrails.composite import CompositeGuardrailEngine
|
from forja_core.guardrails.composite import CompositeGuardrailEngine
|
||||||
from agentforge_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
from forja_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
||||||
from agentforge_core.guardrails.nemo import NeMoGuardrailsEngine
|
from forja_core.guardrails.nemo import NeMoGuardrailsEngine
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from agentforge_core.main import create_app
|
from forja_core.main import create_app
|
||||||
|
|
||||||
|
|
||||||
def test_health_responde_ok() -> None:
|
def test_health_responde_ok() -> None:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Test de los modelos base del proveedor LLM."""
|
"""Test de los modelos base del proveedor LLM."""
|
||||||
|
|
||||||
from agentforge_core.llm.base import CompletionResult, Message
|
from forja_core.llm.base import CompletionResult, Message
|
||||||
|
|
||||||
|
|
||||||
def test_message_serializable() -> None:
|
def test_message_serializable() -> None:
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_core.config import Settings
|
from forja_core.config import Settings
|
||||||
from agentforge_core.llm.factory import build_llm_provider
|
from forja_core.llm.factory import build_llm_provider
|
||||||
from agentforge_core.llm.mock import MockProvider
|
from forja_core.llm.mock import MockProvider
|
||||||
|
|
||||||
|
|
||||||
def test_factory_devuelve_mock_por_defecto() -> None:
|
def test_factory_devuelve_mock_por_defecto() -> None:
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import json
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_core.llm.base import Message
|
from forja_core.llm.base import Message
|
||||||
from agentforge_core.llm.mock import MockProvider
|
from forja_core.llm.mock import MockProvider
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import json
|
|||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
|
|
||||||
from agentforge_core.observability.logging import bind_trace_id, configure_logging
|
from forja_core.observability.logging import bind_trace_id, configure_logging
|
||||||
|
|
||||||
|
|
||||||
def test_configure_logging_emite_json(capsys) -> None: # type: ignore[no-untyped-def]
|
def test_configure_logging_emite_json(capsys) -> None: # type: ignore[no-untyped-def]
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ from datetime import UTC, datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from agentforge_core.api.persistence import (
|
from forja_core.api.persistence import (
|
||||||
append_execution,
|
append_execution,
|
||||||
append_violation,
|
append_violation,
|
||||||
read_execution_summaries,
|
read_execution_summaries,
|
||||||
read_violations,
|
read_violations,
|
||||||
)
|
)
|
||||||
from agentforge_core.domain.execution import AgentExecution
|
from forja_core.domain.execution import AgentExecution
|
||||||
from agentforge_core.domain.guardrail import GuardrailViolation
|
from forja_core.domain.guardrail import GuardrailViolation
|
||||||
|
|
||||||
|
|
||||||
def _violation() -> GuardrailViolation:
|
def _violation() -> GuardrailViolation:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_core.registry.policy_store import FileSystemPolicyStore
|
from forja_core.registry.policy_store import FileSystemPolicyStore
|
||||||
|
|
||||||
FIXTURES = Path(__file__).parent.parent / "fixtures" / "policies"
|
FIXTURES = Path(__file__).parent.parent / "fixtures" / "policies"
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import shutil
|
|||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from agentforge_core.domain.agent import AgentDefinition, LLMConfig
|
from forja_core.domain.agent import AgentDefinition, LLMConfig
|
||||||
from agentforge_core.registry.repository import FileSystemAgentRegistry
|
from forja_core.registry.repository import FileSystemAgentRegistry
|
||||||
|
|
||||||
FIXTURES = Path(__file__).parent.parent / "fixtures" / "agents"
|
FIXTURES = Path(__file__).parent.parent / "fixtures" / "agents"
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ from datetime import UTC, datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from agentforge_core.domain.agent import AgentDefinition, LLMConfig
|
from forja_core.domain.agent import AgentDefinition, LLMConfig
|
||||||
from agentforge_core.domain.policy import PolicyDefinition
|
from forja_core.domain.policy import PolicyDefinition
|
||||||
from agentforge_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
from forja_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
||||||
from agentforge_core.llm.mock import MockProvider
|
from forja_core.llm.mock import MockProvider
|
||||||
from agentforge_core.runtime.checkpointer import build_checkpointer
|
from forja_core.runtime.checkpointer import build_checkpointer
|
||||||
from agentforge_core.runtime.graph import build_graph
|
from forja_core.runtime.graph import build_graph
|
||||||
|
|
||||||
|
|
||||||
def _agent() -> AgentDefinition:
|
def _agent() -> AgentDefinition:
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ from uuid import uuid4
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_core.domain.policy import PolicyDefinition, PolicyValidator
|
from forja_core.domain.policy import PolicyDefinition, PolicyValidator
|
||||||
from agentforge_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
from forja_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
||||||
from agentforge_core.runtime.nodes import build_node_validate_input
|
from forja_core.runtime.nodes import build_node_validate_input
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ from uuid import uuid4
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agentforge_core.domain.agent import AgentDefinition, LLMConfig
|
from forja_core.domain.agent import AgentDefinition, LLMConfig
|
||||||
from agentforge_core.domain.policy import PolicyDefinition, PolicyValidator
|
from forja_core.domain.policy import PolicyDefinition, PolicyValidator
|
||||||
from agentforge_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
from forja_core.guardrails.guardrails_ai import GuardrailsAIEngine
|
||||||
from agentforge_core.llm.mock import MockProvider
|
from forja_core.llm.mock import MockProvider
|
||||||
from agentforge_core.runtime.orchestrator import AgentOrchestrator
|
from forja_core.runtime.orchestrator import AgentOrchestrator
|
||||||
|
|
||||||
|
|
||||||
def _agent(threshold: int = 4) -> AgentDefinition:
|
def _agent(threshold: int = 4) -> AgentDefinition:
|
||||||
|
|||||||
Reference in New Issue
Block a user