Primera fase context

This commit is contained in:
Jordan Diaz
2026-04-09 18:27:36 +00:00
parent 993e7d3000
commit 4c73d848bb
8 changed files with 424 additions and 40 deletions

View File

@@ -0,0 +1,160 @@
"""Tests para budget efectivo de contexto e integracion del ContextEngine."""
from __future__ import annotations
import asyncio
import enum
import sys
import types
import pytest
if not hasattr(enum, "StrEnum"):
class _CompatStrEnum(str, enum.Enum):
pass
enum.StrEnum = _CompatStrEnum
if "anthropic" not in sys.modules:
anthropic_stub = types.ModuleType("anthropic")
class _AsyncAnthropic:
pass
anthropic_stub.AsyncAnthropic = _AsyncAnthropic
sys.modules["anthropic"] = anthropic_stub
if "openai" not in sys.modules:
openai_stub = types.ModuleType("openai")
class _AsyncOpenAI:
pass
openai_stub.AsyncOpenAI = _AsyncOpenAI
sys.modules["openai"] = openai_stub
from src.config import Settings, settings
from src.context.engine import ContextEngine
from src.models.agent import AgentProfile
from src.models.artifacts import ArtifactSummary
from src.models.session import SessionState
from src.orchestrator.engine import OrchestratorEngine
class TestSettingsBudget:
def test_effective_budget_uses_explicit_override(self):
cfg = Settings(
context_max_tokens=120_000,
model_context_window=200_000,
model_max_output_tokens=8_192,
_env_file=None,
)
assert cfg.effective_context_budget == 120_000
def test_effective_budget_uses_model_window_when_no_override(self):
cfg = Settings(
context_max_tokens=0,
model_context_window=200_000,
model_max_output_tokens=8_000,
context_reserve_ratio=0.10,
_env_file=None,
)
assert cfg.reserve_tokens == 20_000
assert cfg.effective_context_budget == 172_000
assert cfg.effective_compaction_threshold == 137_600
class TestContextEngine:
def test_build_context_keeps_task_history_and_current_task(self):
session = SessionState(
immutable_rules=["No romper el proyecto"],
project_profile={"name": "demo"},
task_history=[
{
"task_id": "prev1",
"objective": "Crear banner",
"status": "completed",
"summary": "User: Crear banner → Agent: Banner creado",
"facts": ["Section: home"],
"key_data": {"sections": ["home"]},
"tools_used": ["create_module"],
}
],
)
session.begin_task("Actualizar hero")
agent = AgentProfile(
role="acai",
name="Acai",
system_prompt="Haz el trabajo.",
)
package = asyncio.run(ContextEngine().build_context(session=session, agent=agent))
assert "# Session History" in package.system_prompt
assert "# Current Task" in package.system_prompt
def test_build_context_includes_artifact_memory_with_task_state_agents(self):
session = SessionState(
immutable_rules=["No romper el proyecto"],
project_profile={"name": "demo"},
)
task = session.begin_task("Revisar modulo")
agent = AgentProfile(
role="acai",
name="Acai",
system_prompt="Haz el trabajo.",
context_sections=[
"immutable_rules",
"project_profile",
"task_state",
],
)
artifacts = [
ArtifactSummary(
artifact_id="art-1",
session_id=session.session_id,
task_id=task.task_id,
artifact_type="code",
title="Output of read_file",
summary="Resumen del archivo",
facts=["Status: ok"],
source_tool="read_file",
char_count=120,
)
]
package = asyncio.run(
ContextEngine().build_context(
session=session,
agent=agent,
artifacts=artifacts,
)
)
assert "## Artifacts" in package.system_prompt
assert "Resumen del archivo" in package.system_prompt
class TestTaskHistoryTrim:
def test_trim_respects_entry_limit_and_token_budget(self, monkeypatch):
monkeypatch.setattr(settings, "task_history_max_entries", 3)
monkeypatch.setattr(settings, "task_history_max_tokens", 60)
history = [
{"objective": "old", "summary": "muy antiguo", "facts": [], "tools_used": [], "key_data": {}},
{
"objective": "medio",
"summary": "contenido " * 20,
"facts": [],
"tools_used": [],
"key_data": {},
},
{"objective": "nuevo", "summary": "corto", "facts": [], "tools_used": [], "key_data": {}},
{"objective": "final", "summary": "ultimo", "facts": [], "tools_used": [], "key_data": {}},
]
trimmed = OrchestratorEngine._trim_task_history(history)
assert len(trimmed) <= 3
assert trimmed[-1]["objective"] == "final"
assert all(entry["objective"] != "old" for entry in trimmed)