Primera fase context
This commit is contained in:
160
tests/test_context_budget.py
Normal file
160
tests/test_context_budget.py
Normal 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)
|
||||
Reference in New Issue
Block a user