mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
- Add DynamicContentDetector integration for comprehensive dynamic content detection (20+ patterns vs previous 4 date patterns) - New detection: UUIDs, API keys, JWT tokens, Unix timestamps, request/trace IDs, hex hashes (MD5/SHA1/SHA256), version numbers, high-entropy strings - Add CacheAlignerConfig options: use_dynamic_detector, detection_tiers, extra_dynamic_labels, entropy_threshold - Maintain backward compatibility with legacy date-only mode - Add 25 new comprehensive tests for Phase 1 functionality - Fix code compressor fallback test to properly mock LLMLingua availability Expected cache hit improvement: 30-50% by extracting more dynamic content
1240 lines
46 KiB
Python
1240 lines
46 KiB
Python
"""Tests for CacheAligner transform."""
|
|
|
|
import pytest
|
|
|
|
from headroom import OpenAIProvider, Tokenizer
|
|
from headroom.config import CacheAlignerConfig, CachePrefixMetrics
|
|
from headroom.transforms import CacheAligner
|
|
|
|
# Create a shared provider for tests
|
|
_provider = OpenAIProvider()
|
|
|
|
|
|
def get_tokenizer(model: str = "gpt-4o") -> Tokenizer:
|
|
"""Get a tokenizer for tests using OpenAI provider."""
|
|
token_counter = _provider.get_token_counter(model)
|
|
return Tokenizer(token_counter, model)
|
|
|
|
|
|
# ============================================================================
|
|
# Fixtures
|
|
# ============================================================================
|
|
|
|
|
|
@pytest.fixture
|
|
def tokenizer():
|
|
"""Provide a tokenizer for tests."""
|
|
return get_tokenizer()
|
|
|
|
|
|
@pytest.fixture
|
|
def default_config():
|
|
"""Default CacheAlignerConfig."""
|
|
return CacheAlignerConfig()
|
|
|
|
|
|
@pytest.fixture
|
|
def system_prompt_with_iso_date():
|
|
"""System prompt containing ISO timestamp."""
|
|
return (
|
|
"You are a helpful AI assistant. "
|
|
"The current timestamp is 2024-01-15T10:30:00. "
|
|
"Please assist the user with their requests."
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def system_prompt_with_current_date():
|
|
"""System prompt with 'Current date:' format."""
|
|
return (
|
|
"You are a knowledgeable assistant.\n"
|
|
"Current date: 2024-01-15\n"
|
|
"Help the user with research and analysis."
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def system_prompt_with_today_is():
|
|
"""System prompt with 'Today is' format."""
|
|
return (
|
|
"You are a scheduling assistant.\n"
|
|
"Today is Monday, January 15\n"
|
|
"Help users manage their calendar."
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def system_prompt_with_multiple_dates():
|
|
"""System prompt containing multiple date patterns."""
|
|
return (
|
|
"You are a time-aware assistant.\n"
|
|
"Current date: 2024-01-15\n"
|
|
"System initialized at 2024-01-15T08:00:00.\n"
|
|
"Today is Monday, January 15\n"
|
|
"Please help the user."
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def system_prompt_no_dates():
|
|
"""System prompt without any date patterns."""
|
|
return "You are a helpful assistant. Help users with their questions. Be concise and accurate."
|
|
|
|
|
|
@pytest.fixture
|
|
def system_prompt_with_whitespace_issues():
|
|
"""System prompt with various whitespace issues."""
|
|
return (
|
|
"You are a helpful assistant.\r\n"
|
|
"Help the user. \n" # Double space and trailing space
|
|
"\n"
|
|
"\n"
|
|
"\n" # Multiple blank lines
|
|
"Be concise. " # Trailing spaces
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# TestDateExtraction
|
|
# ============================================================================
|
|
|
|
|
|
class TestDateExtraction:
|
|
"""Tests for date extraction functionality."""
|
|
|
|
def test_extract_iso_date(self, tokenizer, system_prompt_with_iso_date):
|
|
"""Test extraction of ISO 8601 datetime format."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_with_iso_date},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# The ISO date should be extracted and reinserted in dynamic context
|
|
system_content = result.messages[0]["content"]
|
|
assert "2024-01-15T10:30:00" in system_content
|
|
assert "[Dynamic Context]" in system_content
|
|
|
|
def test_extract_current_date_format(self, tokenizer, system_prompt_with_current_date):
|
|
"""Test extraction of 'Current date: YYYY-MM-DD' format."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_with_current_date},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
# The date should be moved to dynamic context
|
|
assert "[Dynamic Context]" in system_content
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
def test_extract_today_is_format(self, tokenizer, system_prompt_with_today_is):
|
|
"""Test extraction of 'Today is [Day], [Month] [Date]' format."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_with_today_is},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
|
|
def test_extract_multiple_dates(self, tokenizer, system_prompt_with_multiple_dates):
|
|
"""Test extraction of multiple date patterns."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_with_multiple_dates},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
# All dates should be in the dynamic context section
|
|
assert "[Dynamic Context]" in system_content
|
|
# Multiple dates should be comma-separated in dynamic section
|
|
dynamic_section = system_content.split("[Dynamic Context]")[1]
|
|
# At least some dates should be present
|
|
assert "2024" in dynamic_section or "January" in dynamic_section
|
|
|
|
def test_no_dates_found(self, tokenizer, system_prompt_no_dates):
|
|
"""Test behavior when no date patterns are found."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_no_dates},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
|
|
# should_apply should return False when no dates found
|
|
assert not aligner.should_apply(messages, tokenizer)
|
|
|
|
# apply still works but doesn't add cache_align transform
|
|
result = aligner.apply(messages, tokenizer)
|
|
assert "cache_align" not in result.transforms_applied
|
|
assert "[Dynamic Context]" not in result.messages[0]["content"]
|
|
|
|
def test_date_patterns_configurable(self, tokenizer):
|
|
"""Test that date patterns can be customized."""
|
|
custom_patterns = [
|
|
r"Version \d+\.\d+\.\d+", # Version pattern
|
|
r"Build #\d+", # Build number
|
|
]
|
|
|
|
system_prompt = "You are an assistant.\nVersion 1.2.3\nBuild #456\nHelp users."
|
|
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(date_patterns=custom_patterns)
|
|
aligner = CacheAligner(config)
|
|
|
|
assert aligner.should_apply(messages, tokenizer)
|
|
|
|
result = aligner.apply(messages, tokenizer)
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
|
|
|
|
# ============================================================================
|
|
# TestWhitespaceNormalization
|
|
# ============================================================================
|
|
|
|
|
|
class TestWhitespaceNormalization:
|
|
"""Tests for whitespace normalization functionality."""
|
|
|
|
def test_collapse_multiple_spaces(self, tokenizer):
|
|
"""Test that multiple consecutive spaces are collapsed."""
|
|
system_prompt = "Hello world test spaces"
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hi"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(
|
|
normalize_whitespace=True,
|
|
# Add a pattern that matches to trigger processing
|
|
date_patterns=[r"Hello"],
|
|
)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# Note: The current implementation doesn't collapse inline spaces,
|
|
# only handles line-level normalization. Let's test what it does do.
|
|
system_content = result.messages[0]["content"]
|
|
# The content should be processed (not testing for specific behavior here)
|
|
assert system_content is not None
|
|
|
|
def test_collapse_blank_lines(self, tokenizer):
|
|
"""Test that multiple consecutive blank lines are collapsed."""
|
|
system_prompt = "Line 1\n\n\n\n\nLine 2"
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hi"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(
|
|
normalize_whitespace=True,
|
|
collapse_blank_lines=True,
|
|
# Need a pattern to trigger full processing
|
|
date_patterns=[r"Line \d"],
|
|
)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# Multiple blank lines should be collapsed to single
|
|
system_content = result.messages[0]["content"]
|
|
# Check that we don't have 4+ consecutive newlines
|
|
assert "\n\n\n\n" not in system_content
|
|
|
|
def test_normalize_line_endings(self, tokenizer, system_prompt_with_whitespace_issues):
|
|
"""Test CRLF to LF normalization."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_with_whitespace_issues},
|
|
{"role": "user", "content": "Hi"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(
|
|
normalize_whitespace=True,
|
|
date_patterns=[r"helpful"], # Pattern to match
|
|
)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
# CRLF should be converted to LF
|
|
assert "\r\n" not in system_content
|
|
assert "\r" not in system_content
|
|
|
|
def test_trim_trailing_whitespace(self, tokenizer):
|
|
"""Test that trailing whitespace on lines is trimmed."""
|
|
system_prompt = "Line with spaces \nAnother line "
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hi"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(
|
|
normalize_whitespace=True,
|
|
date_patterns=[r"Line"],
|
|
)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# Split content before dynamic section for testing
|
|
system_content = result.messages[0]["content"]
|
|
static_part = system_content.split("---")[0] if "---" in system_content else system_content
|
|
|
|
# Each line in the static part should not end with spaces
|
|
for line in static_part.split("\n"):
|
|
if line: # Skip empty lines
|
|
# Lines should not end with trailing spaces
|
|
assert line == line.rstrip() or not line.endswith(" ")
|
|
|
|
def test_disabled_whitespace_normalization(self, tokenizer):
|
|
"""Test that whitespace normalization can be disabled."""
|
|
system_prompt = "Line 1\r\nLine 2 \n\n\n\nLine 3"
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hi"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(
|
|
normalize_whitespace=False,
|
|
date_patterns=[r"Line \d"],
|
|
)
|
|
aligner = CacheAligner(config)
|
|
aligner.apply(messages, tokenizer)
|
|
|
|
# When normalization is disabled, CRLF should be preserved
|
|
# (though dates are still extracted and reinserted)
|
|
# The original whitespace patterns should largely be preserved
|
|
# Note: date extraction may still affect the content structure
|
|
|
|
|
|
# ============================================================================
|
|
# TestPrefixHashing
|
|
# ============================================================================
|
|
|
|
|
|
class TestPrefixHashing:
|
|
"""Tests for stable prefix hash computation."""
|
|
|
|
def test_stable_hash_same_content(self, tokenizer):
|
|
"""Test that same content produces same hash."""
|
|
system_prompt = "You are helpful. Current date: 2024-01-15"
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner1 = CacheAligner()
|
|
aligner2 = CacheAligner()
|
|
|
|
result1 = aligner1.apply(messages, tokenizer)
|
|
result2 = aligner2.apply(messages, tokenizer)
|
|
|
|
# Extract hashes from markers
|
|
hash1 = None
|
|
hash2 = None
|
|
for marker in result1.markers_inserted:
|
|
if marker.startswith("stable_prefix_hash:"):
|
|
hash1 = marker.split(":", 1)[1]
|
|
for marker in result2.markers_inserted:
|
|
if marker.startswith("stable_prefix_hash:"):
|
|
hash2 = marker.split(":", 1)[1]
|
|
|
|
assert hash1 is not None
|
|
assert hash2 is not None
|
|
assert hash1 == hash2
|
|
|
|
def test_different_hash_different_content(self, tokenizer):
|
|
"""Test that different content produces different hash."""
|
|
messages1 = [
|
|
{"role": "system", "content": "Assistant A. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
messages2 = [
|
|
{"role": "system", "content": "Assistant B. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
|
|
result1 = aligner.apply(messages1, tokenizer)
|
|
# Reset hash tracking for independent test
|
|
aligner._previous_prefix_hash = None
|
|
result2 = aligner.apply(messages2, tokenizer)
|
|
|
|
hash1 = result1.cache_metrics.stable_prefix_hash
|
|
hash2 = result2.cache_metrics.stable_prefix_hash
|
|
|
|
assert hash1 != hash2
|
|
|
|
def test_hash_excludes_dynamic_tail(self, tokenizer):
|
|
"""Test that hash is computed before dynamic content is added."""
|
|
system_prompt = "Static content. Current date: 2024-01-15"
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# The final content should have dynamic context
|
|
assert "[Dynamic Context]" in result.messages[0]["content"]
|
|
|
|
# But the hash should be based on static content only
|
|
# (verified by the fact that cache_metrics is populated)
|
|
assert result.cache_metrics is not None
|
|
assert result.cache_metrics.stable_prefix_hash
|
|
|
|
def test_hash_stable_across_dates(self, tokenizer):
|
|
"""Test that hash is stable when only dates change."""
|
|
# Same static content, different dates
|
|
messages_day1 = [
|
|
{"role": "system", "content": "You are helpful. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
messages_day2 = [
|
|
{"role": "system", "content": "You are helpful. Current date: 2024-01-16"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner1 = CacheAligner()
|
|
aligner2 = CacheAligner()
|
|
|
|
result1 = aligner1.apply(messages_day1, tokenizer)
|
|
result2 = aligner2.apply(messages_day2, tokenizer)
|
|
|
|
# Hashes should be the same because static content is identical
|
|
assert result1.cache_metrics.stable_prefix_hash == result2.cache_metrics.stable_prefix_hash
|
|
|
|
def test_previous_hash_tracking(self, tokenizer):
|
|
"""Test that previous hash is tracked across calls."""
|
|
messages = [
|
|
{"role": "system", "content": "Helpful assistant. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
|
|
# First call - no previous hash
|
|
result1 = aligner.apply(messages, tokenizer)
|
|
assert result1.cache_metrics.previous_hash is None
|
|
first_hash = result1.cache_metrics.stable_prefix_hash
|
|
|
|
# Second call - should have previous hash
|
|
result2 = aligner.apply(messages, tokenizer)
|
|
assert result2.cache_metrics.previous_hash == first_hash
|
|
assert result2.cache_metrics.prefix_changed is False
|
|
|
|
|
|
# ============================================================================
|
|
# TestCacheMetrics
|
|
# ============================================================================
|
|
|
|
|
|
class TestCacheMetrics:
|
|
"""Tests for cache metrics reporting."""
|
|
|
|
def test_cache_metrics_populated(self, tokenizer):
|
|
"""Test that cache metrics are fully populated."""
|
|
messages = [
|
|
{"role": "system", "content": "Assistant. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
assert result.cache_metrics is not None
|
|
assert isinstance(result.cache_metrics, CachePrefixMetrics)
|
|
assert result.cache_metrics.stable_prefix_bytes > 0
|
|
assert result.cache_metrics.stable_prefix_tokens_est > 0
|
|
assert len(result.cache_metrics.stable_prefix_hash) == 16 # Short hash
|
|
|
|
def test_prefix_changed_detection(self, tokenizer):
|
|
"""Test detection when prefix changes between requests."""
|
|
aligner = CacheAligner()
|
|
|
|
# First request
|
|
messages1 = [
|
|
{"role": "system", "content": "Version A. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
result1 = aligner.apply(messages1, tokenizer)
|
|
assert result1.cache_metrics.prefix_changed is False # First request
|
|
|
|
# Second request with different static content
|
|
messages2 = [
|
|
{"role": "system", "content": "Version B. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
result2 = aligner.apply(messages2, tokenizer)
|
|
assert result2.cache_metrics.prefix_changed is True # Content changed
|
|
|
|
def test_first_request_no_previous_hash(self, tokenizer):
|
|
"""Test that first request has no previous hash."""
|
|
messages = [
|
|
{"role": "system", "content": "Assistant. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
assert result.cache_metrics.previous_hash is None
|
|
assert result.cache_metrics.prefix_changed is False
|
|
|
|
|
|
# ============================================================================
|
|
# TestAlignmentScore
|
|
# ============================================================================
|
|
|
|
|
|
class TestAlignmentScore:
|
|
"""Tests for cache alignment score calculation."""
|
|
|
|
def test_alignment_score_perfect(self, tokenizer, system_prompt_no_dates):
|
|
"""Test perfect alignment score when no dynamic content."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_no_dates},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
score = aligner.get_alignment_score(messages)
|
|
|
|
# No dynamic patterns = perfect score
|
|
assert score == 100.0
|
|
|
|
def test_alignment_score_with_dates(self, tokenizer, system_prompt_with_multiple_dates):
|
|
"""Test alignment score decreases with date patterns."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_with_multiple_dates},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
score = aligner.get_alignment_score(messages)
|
|
|
|
# Multiple dates should decrease score significantly
|
|
assert score < 100.0
|
|
# But should still be above 0
|
|
assert score >= 0.0
|
|
|
|
def test_alignment_score_with_whitespace_issues(
|
|
self, tokenizer, system_prompt_with_whitespace_issues
|
|
):
|
|
"""Test alignment score penalizes whitespace issues."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_with_whitespace_issues},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
score = aligner.get_alignment_score(messages)
|
|
|
|
# CRLF, double spaces, and triple newlines should reduce score
|
|
assert score < 100.0
|
|
|
|
|
|
# ============================================================================
|
|
# TestApply
|
|
# ============================================================================
|
|
|
|
|
|
class TestApply:
|
|
"""Tests for the main apply method."""
|
|
|
|
def test_apply_extracts_and_reinserts_dates(self, tokenizer, system_prompt_with_iso_date):
|
|
"""Test that dates are extracted and reinserted in dynamic section."""
|
|
messages = [
|
|
{"role": "system", "content": system_prompt_with_iso_date},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
|
|
# Dynamic context marker should be present
|
|
assert "[Dynamic Context]" in system_content
|
|
|
|
# The date should be in the dynamic section
|
|
parts = system_content.split("[Dynamic Context]")
|
|
assert len(parts) == 2
|
|
dynamic_section = parts[1]
|
|
assert "2024-01-15T10:30:00" in dynamic_section
|
|
|
|
def test_apply_normalizes_whitespace(self, tokenizer):
|
|
"""Test that whitespace is normalized during apply."""
|
|
system_prompt = "Hello\r\nWorld\n\n\n\nTest "
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hi"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(
|
|
normalize_whitespace=True,
|
|
collapse_blank_lines=True,
|
|
date_patterns=[r"Hello"], # Pattern to trigger processing
|
|
)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
|
|
# CRLF should be normalized
|
|
assert "\r" not in system_content
|
|
|
|
def test_apply_markers_inserted(self, tokenizer):
|
|
"""Test that markers are properly inserted in result."""
|
|
messages = [
|
|
{"role": "system", "content": "Assistant. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# Check that stable_prefix_hash marker is inserted
|
|
hash_markers = [m for m in result.markers_inserted if m.startswith("stable_prefix_hash:")]
|
|
assert len(hash_markers) == 1
|
|
assert len(hash_markers[0].split(":")[1]) == 16
|
|
|
|
def test_should_apply_false_when_disabled(self, tokenizer):
|
|
"""Test that should_apply returns False when disabled."""
|
|
messages = [
|
|
{"role": "system", "content": "Assistant. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(enabled=False)
|
|
aligner = CacheAligner(config)
|
|
|
|
assert not aligner.should_apply(messages, tokenizer)
|
|
|
|
def test_apply_preserves_non_system_messages(self, tokenizer):
|
|
"""Test that non-system messages are not modified for date extraction."""
|
|
messages = [
|
|
{"role": "system", "content": "You are helpful."},
|
|
{"role": "user", "content": "What is the date 2024-01-15T10:30:00?"},
|
|
{"role": "assistant", "content": "That's January 15th, 2024."},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# User and assistant messages should be unchanged
|
|
# (dates in non-system messages should not be extracted)
|
|
assert result.messages[1]["content"] == messages[1]["content"]
|
|
assert result.messages[2]["content"] == messages[2]["content"]
|
|
|
|
def test_apply_returns_token_counts(self, tokenizer):
|
|
"""Test that apply returns proper token counts."""
|
|
messages = [
|
|
{"role": "system", "content": "Assistant. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
assert result.tokens_before > 0
|
|
assert result.tokens_after > 0
|
|
# Token count may change due to dynamic context addition
|
|
assert (
|
|
result.tokens_before != result.tokens_after
|
|
or result.tokens_before == result.tokens_after
|
|
)
|
|
|
|
def test_apply_deep_copies_messages(self, tokenizer):
|
|
"""Test that apply does not modify original messages."""
|
|
original_content = "Assistant. Current date: 2024-01-15"
|
|
messages = [
|
|
{"role": "system", "content": original_content},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# Original should be unchanged
|
|
assert messages[0]["content"] == original_content
|
|
# Result should be modified
|
|
assert result.messages[0]["content"] != original_content
|
|
|
|
|
|
# ============================================================================
|
|
# Integration Tests
|
|
# ============================================================================
|
|
|
|
|
|
class TestIntegration:
|
|
"""Integration tests for CacheAligner."""
|
|
|
|
def test_full_workflow(self, tokenizer):
|
|
"""Test complete workflow with realistic system prompt."""
|
|
system_prompt = """You are Claude, a helpful AI assistant created by Anthropic.
|
|
|
|
Current date: 2024-01-15
|
|
Today is Monday, January 15
|
|
|
|
Your capabilities include:
|
|
- Answering questions
|
|
- Helping with analysis
|
|
- Writing and editing text
|
|
|
|
Please be helpful, harmless, and honest."""
|
|
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "What can you help me with today?"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
|
|
# Check should_apply
|
|
assert aligner.should_apply(messages, tokenizer)
|
|
|
|
# Check alignment score before
|
|
score_before = aligner.get_alignment_score(messages)
|
|
assert score_before < 100.0 # Has dynamic content
|
|
|
|
# Apply alignment
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# Verify transforms applied
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
# Verify cache metrics
|
|
assert result.cache_metrics is not None
|
|
assert result.cache_metrics.stable_prefix_hash
|
|
|
|
# Verify dynamic context section exists
|
|
assert "[Dynamic Context]" in result.messages[0]["content"]
|
|
|
|
def test_multiple_system_messages(self, tokenizer):
|
|
"""Test handling of multiple system messages."""
|
|
messages = [
|
|
{"role": "system", "content": "Base instructions. Current date: 2024-01-15"},
|
|
{"role": "system", "content": "Additional context. Today is Monday, January 15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# Both system messages should be processed
|
|
# At least one should have dynamic context
|
|
has_dynamic_context = any(
|
|
"[Dynamic Context]" in msg.get("content", "")
|
|
for msg in result.messages
|
|
if msg.get("role") == "system"
|
|
)
|
|
assert has_dynamic_context
|
|
|
|
def test_empty_messages(self, tokenizer):
|
|
"""Test handling of empty message list."""
|
|
messages = []
|
|
|
|
aligner = CacheAligner()
|
|
|
|
# should_apply should return False
|
|
assert not aligner.should_apply(messages, tokenizer)
|
|
|
|
# apply should handle gracefully
|
|
result = aligner.apply(messages, tokenizer)
|
|
assert result.messages == []
|
|
|
|
def test_no_system_message(self, tokenizer):
|
|
"""Test handling when no system message present."""
|
|
messages = [
|
|
{"role": "user", "content": "Hello"},
|
|
{"role": "assistant", "content": "Hi there!"},
|
|
]
|
|
|
|
aligner = CacheAligner()
|
|
|
|
# should_apply should return False (no system message)
|
|
assert not aligner.should_apply(messages, tokenizer)
|
|
|
|
# apply should work but not modify anything
|
|
result = aligner.apply(messages, tokenizer)
|
|
assert "cache_align" not in result.transforms_applied
|
|
|
|
|
|
# ============================================================================
|
|
# Phase 1: DynamicContentDetector Integration Tests
|
|
# ============================================================================
|
|
|
|
|
|
class TestDynamicContentDetectorIntegration:
|
|
"""Tests for Phase 1: DynamicContentDetector integration."""
|
|
|
|
def test_uuid_detection(self, tokenizer):
|
|
"""Test extraction of UUID patterns."""
|
|
system_prompt = (
|
|
"You are a helpful assistant.\n"
|
|
"Session ID: 550e8400-e29b-41d4-a716-446655440000\n"
|
|
"Please help the user."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
assert "cache_align" in result.transforms_applied
|
|
# UUID should be in dynamic section
|
|
dynamic_section = system_content.split("[Dynamic Context]")[1]
|
|
assert "550e8400-e29b-41d4-a716-446655440000" in dynamic_section
|
|
|
|
def test_api_key_detection(self, tokenizer):
|
|
"""Test extraction of API key patterns."""
|
|
system_prompt = (
|
|
"You are an assistant with API access.\n"
|
|
"API Key: sk-abc123def456ghi789jkl012mno345pqr678\n"
|
|
"Use this to make requests."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
# API key should be extracted
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
def test_jwt_token_detection(self, tokenizer):
|
|
"""Test extraction of JWT token patterns."""
|
|
jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
|
system_prompt = f"You are an assistant.\nAuth Token: {jwt_token}\nHelp the user."
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
def test_unix_timestamp_detection(self, tokenizer):
|
|
"""Test extraction of Unix timestamp patterns."""
|
|
system_prompt = (
|
|
"You are a logging assistant.\nRequest started at: 1705312200000\nHelp analyze logs."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
|
|
def test_request_trace_id_detection(self, tokenizer):
|
|
"""Test extraction of request/trace ID patterns."""
|
|
system_prompt = (
|
|
"You are a debugging assistant.\n"
|
|
"Trace ID: req_abc123def456\n"
|
|
"Request ID: tx_987654321abc\n"
|
|
"Help debug issues."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
def test_hex_hash_md5_detection(self, tokenizer):
|
|
"""Test extraction of MD5 hash patterns (32 hex chars)."""
|
|
system_prompt = (
|
|
"You are a file assistant.\n"
|
|
"File hash: d41d8cd98f00b204e9800998ecf8427e\n"
|
|
"Help with file operations."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
dynamic_section = system_content.split("[Dynamic Context]")[1]
|
|
assert "d41d8cd98f00b204e9800998ecf8427e" in dynamic_section
|
|
|
|
def test_hex_hash_sha1_detection(self, tokenizer):
|
|
"""Test extraction of SHA1 hash patterns (40 hex chars)."""
|
|
system_prompt = (
|
|
"You are a git assistant.\n"
|
|
"Commit: da39a3ee5e6b4b0d3255bfef95601890afd80709\n"
|
|
"Help with git operations."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
dynamic_section = system_content.split("[Dynamic Context]")[1]
|
|
assert "da39a3ee5e6b4b0d3255bfef95601890afd80709" in dynamic_section
|
|
|
|
def test_hex_hash_sha256_detection(self, tokenizer):
|
|
"""Test extraction of SHA256 hash patterns (64 hex chars)."""
|
|
sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
|
system_prompt = f"You are a security assistant.\nHash: {sha256}\nHelp verify files."
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
dynamic_section = system_content.split("[Dynamic Context]")[1]
|
|
assert sha256 in dynamic_section
|
|
|
|
def test_version_number_detection(self, tokenizer):
|
|
"""Test extraction of version number patterns."""
|
|
system_prompt = (
|
|
"You are a deployment assistant.\n"
|
|
"Current version: v2.15.3\n"
|
|
"Previous version: 1.14.2\n"
|
|
"Help with deployments."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
def test_combined_dynamic_content(self, tokenizer):
|
|
"""Test extraction of multiple dynamic content types together."""
|
|
system_prompt = (
|
|
"You are a comprehensive assistant.\n"
|
|
"Session: 550e8400-e29b-41d4-a716-446655440000\n"
|
|
"Current date: 2024-01-15\n"
|
|
"Request ID: req_abc123def456\n"
|
|
"Version: v3.2.1\n"
|
|
"Commit: da39a3ee5e6b4b0d3255bfef95601890afd80709\n"
|
|
"Help the user with their tasks."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
# Multiple dynamic values should be present in dynamic section
|
|
dynamic_section = system_content.split("[Dynamic Context]")[1]
|
|
# At least some of these should be in the dynamic section
|
|
dynamic_items_found = sum(
|
|
[
|
|
"550e8400" in dynamic_section,
|
|
"2024-01-15" in dynamic_section,
|
|
"da39a3ee" in dynamic_section,
|
|
]
|
|
)
|
|
assert dynamic_items_found >= 2, "Expected multiple dynamic items in dynamic section"
|
|
|
|
def test_detection_stats_tracking(self, tokenizer):
|
|
"""Test that detection statistics are properly tracked."""
|
|
system_prompt = (
|
|
"Assistant.\n"
|
|
"UUID: 550e8400-e29b-41d4-a716-446655440000\n"
|
|
"Current date: 2024-01-15T10:30:00\n"
|
|
"Hash: d41d8cd98f00b204e9800998ecf8427e\n"
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
# Cache metrics should show detection occurred
|
|
assert result.cache_metrics is not None
|
|
assert result.cache_metrics.stable_prefix_hash
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
def test_stable_hash_with_dynamic_detector(self, tokenizer):
|
|
"""Test that hash remains stable when only dynamic content changes."""
|
|
# Same static content, different dynamic content
|
|
messages_v1 = [
|
|
{
|
|
"role": "system",
|
|
"content": "You are helpful.\nSession: 550e8400-e29b-41d4-a716-446655440000",
|
|
},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
messages_v2 = [
|
|
{
|
|
"role": "system",
|
|
"content": "You are helpful.\nSession: 661f9511-f30c-52e5-b827-557766551111",
|
|
},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner1 = CacheAligner(config)
|
|
aligner2 = CacheAligner(config)
|
|
|
|
result1 = aligner1.apply(messages_v1, tokenizer)
|
|
result2 = aligner2.apply(messages_v2, tokenizer)
|
|
|
|
# Hashes should be identical - only the UUID changed
|
|
assert result1.cache_metrics.stable_prefix_hash == result2.cache_metrics.stable_prefix_hash
|
|
|
|
|
|
class TestLegacyModeBackwardCompatibility:
|
|
"""Tests for backward compatibility with legacy date-only mode."""
|
|
|
|
def test_legacy_mode_only_detects_dates(self, tokenizer):
|
|
"""Test that legacy mode only extracts date patterns."""
|
|
system_prompt = (
|
|
"You are an assistant.\n"
|
|
"Current date: 2024-01-15\n"
|
|
"Session: 550e8400-e29b-41d4-a716-446655440000\n"
|
|
"Help users."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
# Legacy mode - should only detect dates
|
|
config = CacheAlignerConfig(use_dynamic_detector=False)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
|
|
# In legacy mode, UUID should NOT be in dynamic section (still in static)
|
|
# Split by separator to get static and dynamic parts
|
|
parts = system_content.split("---")
|
|
if len(parts) > 1:
|
|
static_part = parts[0]
|
|
# UUID should still be in static part in legacy mode
|
|
assert "550e8400-e29b-41d4-a716-446655440000" in static_part
|
|
|
|
def test_legacy_mode_uses_configured_patterns(self, tokenizer):
|
|
"""Test that legacy mode uses configured date_patterns."""
|
|
custom_patterns = [r"Build #\d+", r"Release \d+\.\d+"]
|
|
system_prompt = "Assistant.\nBuild #123\nRelease 2.5\nHelp users."
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=False, date_patterns=custom_patterns)
|
|
aligner = CacheAligner(config)
|
|
result = aligner.apply(messages, tokenizer)
|
|
|
|
system_content = result.messages[0]["content"]
|
|
assert "[Dynamic Context]" in system_content
|
|
assert "cache_align" in result.transforms_applied
|
|
|
|
def test_default_config_uses_dynamic_detector(self, tokenizer):
|
|
"""Test that default config enables dynamic detector."""
|
|
config = CacheAlignerConfig()
|
|
assert config.use_dynamic_detector is True
|
|
|
|
aligner = CacheAligner(config)
|
|
assert aligner._dynamic_detector is not None
|
|
|
|
|
|
class TestDynamicDetectorConfiguration:
|
|
"""Tests for DynamicContentDetector configuration options."""
|
|
|
|
def test_detection_tiers_default(self, tokenizer):
|
|
"""Test that default detection tier is regex only."""
|
|
config = CacheAlignerConfig()
|
|
assert config.detection_tiers == ["regex"]
|
|
|
|
def test_detection_tiers_configurable(self, tokenizer):
|
|
"""Test that detection tiers can be configured."""
|
|
config = CacheAlignerConfig(detection_tiers=["regex", "ner"])
|
|
assert "regex" in config.detection_tiers
|
|
assert "ner" in config.detection_tiers
|
|
|
|
def test_extra_dynamic_labels_empty_by_default(self, tokenizer):
|
|
"""Test that extra_dynamic_labels is empty by default."""
|
|
config = CacheAlignerConfig()
|
|
assert config.extra_dynamic_labels == []
|
|
|
|
def test_entropy_threshold_default(self, tokenizer):
|
|
"""Test that entropy threshold has correct default."""
|
|
config = CacheAlignerConfig()
|
|
assert config.entropy_threshold == 0.7
|
|
|
|
def test_entropy_threshold_configurable(self, tokenizer):
|
|
"""Test that entropy threshold can be configured."""
|
|
config = CacheAlignerConfig(entropy_threshold=0.8)
|
|
assert config.entropy_threshold == 0.8
|
|
|
|
|
|
class TestAlignmentScoreWithDynamicDetector:
|
|
"""Tests for alignment score with dynamic detector enabled."""
|
|
|
|
def test_alignment_score_penalizes_uuids(self, tokenizer):
|
|
"""Test alignment score decreases with UUID patterns."""
|
|
messages_no_uuid = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
messages_with_uuid = [
|
|
{
|
|
"role": "system",
|
|
"content": "You are a helpful assistant.\nSession: 550e8400-e29b-41d4-a716-446655440000",
|
|
},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
|
|
score_no_uuid = aligner.get_alignment_score(messages_no_uuid)
|
|
score_with_uuid = aligner.get_alignment_score(messages_with_uuid)
|
|
|
|
assert score_with_uuid < score_no_uuid
|
|
|
|
def test_alignment_score_penalizes_multiple_dynamic_patterns(self, tokenizer):
|
|
"""Test alignment score decreases significantly with many dynamic patterns."""
|
|
system_prompt = (
|
|
"Assistant.\n"
|
|
"Session: 550e8400-e29b-41d4-a716-446655440000\n"
|
|
"Request: req_abc123def456\n"
|
|
"Date: 2024-01-15T10:30:00\n"
|
|
"Hash: d41d8cd98f00b204e9800998ecf8427e\n"
|
|
"Version: v2.5.1\n"
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": system_prompt},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligner = CacheAligner(config)
|
|
|
|
score = aligner.get_alignment_score(messages)
|
|
|
|
# Many dynamic patterns should significantly reduce score
|
|
assert score < 60.0 # 5+ patterns at 10 points each = at least 50 point reduction
|
|
|
|
|
|
class TestConvenienceFunction:
|
|
"""Tests for align_for_cache convenience function."""
|
|
|
|
def test_align_for_cache_basic(self):
|
|
"""Test align_for_cache convenience function."""
|
|
from headroom.transforms.cache_aligner import align_for_cache
|
|
|
|
messages = [
|
|
{"role": "system", "content": "Assistant. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
aligned_messages, stable_hash = align_for_cache(messages)
|
|
|
|
assert "[Dynamic Context]" in aligned_messages[0]["content"]
|
|
assert len(stable_hash) == 16
|
|
|
|
def test_align_for_cache_with_config(self):
|
|
"""Test align_for_cache with custom config."""
|
|
from headroom.transforms.cache_aligner import align_for_cache
|
|
|
|
messages = [
|
|
{
|
|
"role": "system",
|
|
"content": "Assistant. Session: 550e8400-e29b-41d4-a716-446655440000",
|
|
},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=True)
|
|
aligned_messages, stable_hash = align_for_cache(messages, config)
|
|
|
|
assert "[Dynamic Context]" in aligned_messages[0]["content"]
|
|
assert len(stable_hash) == 16
|
|
|
|
def test_align_for_cache_legacy_mode(self):
|
|
"""Test align_for_cache with legacy mode."""
|
|
from headroom.transforms.cache_aligner import align_for_cache
|
|
|
|
messages = [
|
|
{"role": "system", "content": "Assistant. Current date: 2024-01-15"},
|
|
{"role": "user", "content": "Hello"},
|
|
]
|
|
|
|
config = CacheAlignerConfig(use_dynamic_detector=False)
|
|
aligned_messages, stable_hash = align_for_cache(messages, config)
|
|
|
|
assert "[Dynamic Context]" in aligned_messages[0]["content"]
|
|
assert len(stable_hash) == 16
|