From 17ffae0cd8e4498bf60f4fa5a36521905f9fb151 Mon Sep 17 00:00:00 2001 From: Tejas Chopra Date: Sat, 9 May 2026 15:05:29 -0700 Subject: [PATCH] fix: clear CI mypy + rust test failures introduced in eaf5980 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit compression_units.py: - Replace dict-unpacking pattern with dataclasses.replace() so mypy can type-check fields. The `**base` form forced mypy to infer `dict[str, object]`, which doesn't satisfy the per-field types of UnitCompressionResult (46 arg-type errors). - Use `isinstance(candidates, Iterable)` for the transform-iteration guard. The previous `iter()` call had a `# type: ignore[arg-type]` that was misclassified — mypy actually emits `call-overload` here. live_zone_thresholds.rs: - Update the JsonArray threshold assertion from 1024 to 512 to match the new constant. eaf5980 lowered THRESHOLD_JSON_ARRAY from 1024 → 512 in live_zone.rs but missed this integration test. --- .../tests/live_zone_thresholds.rs | 8 +-- headroom/transforms/compression_units.py | 64 +++++++++---------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/crates/headroom-core/tests/live_zone_thresholds.rs b/crates/headroom-core/tests/live_zone_thresholds.rs index 946cb54a1..429db3f29 100644 --- a/crates/headroom-core/tests/live_zone_thresholds.rs +++ b/crates/headroom-core/tests/live_zone_thresholds.rs @@ -51,14 +51,14 @@ fn first_tool_result_action(out: &LiveZoneOutcome) -> BlockAction { #[test] fn below_threshold_no_compression_attempted() { - // 200 bytes of homogeneous JSON dicts — well below the 1 KiB + // 200 bytes of homogeneous JSON dicts — well below the 512 B // JsonArray threshold. The dispatcher must record // `BelowByteThreshold` and emit `NoChange`; no compressor runs. let small_array: Vec = (0..3).map(|i| json!({"id": i, "v": "x"})).collect(); let payload = serde_json::to_string(&small_array).unwrap(); assert!( - payload.len() < 1024, - "fixture must stay below the 1 KiB JsonArray threshold; got {}", + payload.len() < 512, + "fixture must stay below the 512 B JsonArray threshold; got {}", payload.len() ); @@ -80,7 +80,7 @@ fn below_threshold_no_compression_attempted() { } => { assert_eq!(content_type, "json_array"); assert_eq!(byte_count, payload.len()); - assert_eq!(threshold_bytes, 1024); + assert_eq!(threshold_bytes, 512); } other => panic!("expected BelowByteThreshold for sub-threshold JSON, got {other:?}"), } diff --git a/headroom/transforms/compression_units.py b/headroom/transforms/compression_units.py index 16a424cd5..7253fbcf1 100644 --- a/headroom/transforms/compression_units.py +++ b/headroom/transforms/compression_units.py @@ -9,7 +9,7 @@ replacements back into their native request shape. from __future__ import annotations from collections.abc import Iterable -from dataclasses import dataclass, field +from dataclasses import dataclass, field, replace from typing import Protocol from .content_router import CompressionStrategy, ContentRouter, RouterCompressionResult @@ -63,11 +63,9 @@ def find_content_router(transforms: object) -> ContentRouter | None: """Return the first ContentRouter in a pipeline or iterable.""" candidates = getattr(transforms, "transforms", transforms) - try: - iterator = iter(candidates) # type: ignore[arg-type] - except TypeError: + if not isinstance(candidates, Iterable): return None - for transform in iterator: + for transform in candidates: if isinstance(transform, ContentRouter): return transform return None @@ -86,32 +84,32 @@ def compress_unit_with_router( """ tokens_before = tokenizer.count_text(unit.text) - base = { - "original": unit.text, - "compressed": unit.text, - "modified": False, - "tokens_before": tokens_before, - "tokens_after": tokens_before, - "tokens_saved": 0, - "transforms_applied": [], - "strategy": CompressionStrategy.PASSTHROUGH.value, - "router_result": None, - } + base = UnitCompressionResult( + original=unit.text, + compressed=unit.text, + modified=False, + tokens_before=tokens_before, + tokens_after=tokens_before, + tokens_saved=0, + transforms_applied=[], + strategy=CompressionStrategy.PASSTHROUGH.value, + router_result=None, + ) if not unit.mutable: - return UnitCompressionResult(**base, reason="immutable") + return replace(base, reason="immutable") if unit.role == "user": - return UnitCompressionResult(**base, reason="protected_user_message") + return replace(base, reason="protected_user_message") if unit.role in {"system", "developer"}: - return UnitCompressionResult(**base, reason="protected_system_message") + return replace(base, reason="protected_system_message") if unit.role == "assistant" and unit.metadata.get("compress_assistant") != "true": - return UnitCompressionResult(**base, reason="protected_assistant_message") + return replace(base, reason="protected_assistant_message") if unit.cache_zone != "live": - return UnitCompressionResult(**base, reason=f"cache_zone_{unit.cache_zone}") + return replace(base, reason=f"cache_zone_{unit.cache_zone}") if len(unit.text) < unit.min_bytes: - return UnitCompressionResult(**base, reason="below_unit_floor") + return replace(base, reason="below_unit_floor") if "Retrieve more: hash=" in unit.text or "Retrieve original: hash=" in unit.text: - return UnitCompressionResult(**base, reason="already_compressed") + return replace(base, reason="already_compressed") router_result = router.compress( unit.text, @@ -122,21 +120,21 @@ def compress_unit_with_router( replacement = router_result.compressed strategy = router_result.strategy_used.value if replacement == unit.text: - return UnitCompressionResult( - **{**base, "strategy": strategy, "router_result": router_result}, + return replace( + base, + strategy=strategy, + router_result=router_result, reason="router_no_change", ) tokens_after = tokenizer.count_text(replacement) if tokens_after >= tokens_before: - return UnitCompressionResult( - **{ - **base, - "compressed": replacement, - "tokens_after": tokens_after, - "strategy": strategy, - "router_result": router_result, - }, + return replace( + base, + compressed=replacement, + tokens_after=tokens_after, + strategy=strategy, + router_result=router_result, reason="rejected_not_smaller", )