From 73114f6731c01a593543717ec62cf3f7945755d0 Mon Sep 17 00:00:00 2001 From: Juan Date: Sun, 10 May 2026 10:10:21 +0200 Subject: [PATCH] =?UTF-8?q?feat(domain):=20a=C3=B1ade=20modelo=20AgentDefi?= =?UTF-8?q?nition=20+=20LLMConfig=20+=20AgentVersionMeta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- core/src/agentforge_core/domain/agent.py | 43 +++++++++++++++ tests/unit/test_domain_agent.py | 70 ++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 core/src/agentforge_core/domain/agent.py create mode 100644 tests/unit/test_domain_agent.py diff --git a/core/src/agentforge_core/domain/agent.py b/core/src/agentforge_core/domain/agent.py new file mode 100644 index 0000000..3007b99 --- /dev/null +++ b/core/src/agentforge_core/domain/agent.py @@ -0,0 +1,43 @@ +"""Modelos de dominio del agente: definición, configuración LLM y metadata de versión.""" + +from __future__ import annotations + +from datetime import datetime +from typing import Any, Literal + +from pydantic import BaseModel, Field + + +class LLMConfig(BaseModel): + """Configuración del proveedor LLM utilizado por un agente.""" + + provider: Literal["mock", "azure", "openai"] = "mock" + model: str = "gpt-4o" + temperature: float = Field(default=0.2, ge=0.0, le=2.0) + max_tokens: int = Field(default=2000, ge=1, le=128_000) + + +class AgentVersionMeta(BaseModel): + """Metadatos de una versión concreta de un agente (estilo commit Git).""" + + id: str + hash: str + author: str + message: str + created_at: datetime + + +class AgentDefinition(BaseModel): + """Definición declarativa completa de un agente.""" + + name: str + version: str + owner: str + purpose: str + state: Literal["draft", "active", "deprecated"] + guardrails: list[str] + llm: LLMConfig + system_prompt: str + output_schema: dict[str, Any] + risk_threshold_for_hitl: int = Field(default=4, ge=1, le=5) + updated_at: datetime diff --git a/tests/unit/test_domain_agent.py b/tests/unit/test_domain_agent.py new file mode 100644 index 0000000..203a289 --- /dev/null +++ b/tests/unit/test_domain_agent.py @@ -0,0 +1,70 @@ +"""Tests unitarios para los modelos de dominio del agente.""" + +from datetime import UTC, datetime + +import pytest +from pydantic import ValidationError + +from agentforge_core.domain.agent import ( + AgentDefinition, + AgentVersionMeta, + LLMConfig, +) + + +def test_llm_config_default_provider_is_mock() -> None: + cfg = LLMConfig() + assert cfg.provider == "mock" + assert cfg.model == "gpt-4o" + assert cfg.temperature == 0.2 + + +def test_llm_config_rechaza_provider_invalido() -> None: + with pytest.raises(ValidationError): + LLMConfig(provider="bedrock") # type: ignore[arg-type] + + +def test_agent_version_meta_serialize() -> None: + meta = AgentVersionMeta( + id="v1", + hash="abc123", + author="Juan", + message="inicial", + created_at=datetime(2026, 5, 9, tzinfo=UTC), + ) + dumped = meta.model_dump(mode="json") + assert dumped["id"] == "v1" + assert dumped["created_at"].startswith("2026-05-09") + + +def test_agent_definition_minimo_valido() -> None: + agent = AgentDefinition( + name="incident_analyzer", + version="v1", + owner="Juan", + purpose="Análisis de incidentes", + state="active", + guardrails=["default"], + llm=LLMConfig(), + system_prompt="eres un analista...", + output_schema={"type": "object"}, + updated_at=datetime.now(UTC), + ) + assert agent.risk_threshold_for_hitl == 4 # default + assert agent.state == "active" + + +def test_agent_definition_rechaza_state_invalido() -> None: + with pytest.raises(ValidationError): + AgentDefinition( + name="x", + version="v1", + owner="x", + purpose="x", + state="archived", # type: ignore[arg-type] + guardrails=[], + llm=LLMConfig(), + system_prompt="x", + output_schema={}, + updated_at=datetime.now(UTC), + )