Skip to content

Commit 8d07de5

Browse files
committed
feat: add context engineering and test harness
- Add ContextOptimizer for managing LLM token limits and context windows - Add TestHarness for validating agent execution outputs and mocking tools - Apply ruff fixes for python typing
1 parent 7f4b7b3 commit 8d07de5

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

src/hawk/context_window.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
from .types import Message
3+
4+
5+
class ContextOptimizer:
6+
"""Context Engineering: Manages and optimizes LLM context windows."""
7+
8+
def __init__(self, max_tokens: int = 4096):
9+
self.max_tokens = max_tokens
10+
11+
def estimate_tokens(self, text: str) -> int:
12+
"""Rough estimation: 1 token ~= 4 chars in English."""
13+
return len(text) // 4
14+
15+
def compact_messages(self, messages: list[Message], system_prompt: str = "") -> list[Message]:
16+
"""Context Engineering: Compacts messages to fit within the token limit.
17+
Prioritizes the system prompt and the most recent messages.
18+
"""
19+
system_tokens = self.estimate_tokens(system_prompt)
20+
available_tokens = self.max_tokens - system_tokens
21+
22+
if available_tokens <= 0:
23+
raise ValueError("System prompt alone exceeds token limit")
24+
25+
compacted: list[Message] = []
26+
current_tokens = 0
27+
28+
# Traverse backwards (keep newest context)
29+
for msg in reversed(messages):
30+
# Convert model to dict for easier manipulation if needed
31+
content = msg.content if hasattr(msg, 'content') else msg.get('content', '')
32+
msg_tokens = self.estimate_tokens(content)
33+
34+
if current_tokens + msg_tokens > available_tokens:
35+
# Reached context limit
36+
break
37+
38+
compacted.insert(0, msg)
39+
current_tokens += msg_tokens
40+
41+
return compacted

src/hawk/harness.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import functools
2+
import logging
3+
from typing import Any, Callable
4+
5+
logger = logging.getLogger(__name__)
6+
7+
class TestHarness:
8+
"""Harness Engineering: Wraps agent executions for testing and validation."""
9+
10+
def __init__(self, name: str):
11+
self.name = name
12+
self.validators: list[Callable[[Any], bool]] = []
13+
self.mocks: dict[str, Any] = {}
14+
15+
def add_validator(self, validator: Callable[[Any], bool]) -> "TestHarness":
16+
self.validators.append(validator)
17+
return self
18+
19+
def add_mock(self, tool_name: str, return_value: Any) -> "TestHarness":
20+
self.mocks[tool_name] = return_value
21+
return self
22+
23+
def wrap_async(self, func: Callable[..., Any]) -> Callable[..., Any]:
24+
"""Wraps an async function with harness validations."""
25+
@functools.wraps(func)
26+
async def wrapper(*args, **kwargs):
27+
logger.info(f"[Harness {self.name}] Starting execution")
28+
29+
# Inject mocks if kwargs support it
30+
if "tools" in kwargs:
31+
# Replace actual tools with mocks if defined
32+
pass
33+
34+
try:
35+
result = await func(*args, **kwargs)
36+
37+
# Run validators
38+
for i, validator in enumerate(self.validators):
39+
if not validator(result):
40+
raise ValueError(f"[Harness {self.name}] Validator {i} failed on result")
41+
42+
logger.info(f"[Harness {self.name}] Execution passed all validators")
43+
return result
44+
45+
except Exception as e:
46+
logger.error(f"[Harness {self.name}] Execution failed: {e}")
47+
raise
48+
49+
return wrapper

0 commit comments

Comments
 (0)