diff --git a/core/src/agentforge_core/llm/base.py b/core/src/agentforge_core/llm/base.py new file mode 100644 index 0000000..effed53 --- /dev/null +++ b/core/src/agentforge_core/llm/base.py @@ -0,0 +1,38 @@ +"""Tipos y Protocol del proveedor LLM (Strategy pattern).""" + +from __future__ import annotations + +from typing import Any, Literal, Protocol + +from pydantic import BaseModel + + +class Message(BaseModel): + """Mensaje individual de la conversación enviada al LLM.""" + + role: Literal["system", "user", "assistant"] + content: str + + +class CompletionResult(BaseModel): + """Resultado normalizado de una llamada a un proveedor LLM.""" + + content: str + model: str + tokens_in: int + tokens_out: int + latency_ms: int + + +class LLMProvider(Protocol): + """Interfaz mínima común a todos los proveedores LLM.""" + + name: str + + async def complete( + self, + messages: list[Message], + schema: dict[str, Any] | None = None, + temperature: float = 0.2, + max_tokens: int = 2000, + ) -> CompletionResult: ... diff --git a/tests/unit/test_llm_base.py b/tests/unit/test_llm_base.py new file mode 100644 index 0000000..deefc98 --- /dev/null +++ b/tests/unit/test_llm_base.py @@ -0,0 +1,19 @@ +"""Test de los modelos base del proveedor LLM.""" + +from agentforge_core.llm.base import CompletionResult, Message + + +def test_message_serializable() -> None: + m = Message(role="user", content="hola") + assert m.model_dump()["role"] == "user" + + +def test_completion_result_campos_basicos() -> None: + r = CompletionResult( + content='{"a": 1}', + model="gpt-4o-mock", + tokens_in=10, + tokens_out=20, + latency_ms=42, + ) + assert r.tokens_in == 10