headroom_3 / headroom /utils.py
chopratejas's picture
Fix all mypy type errors and flaky embedding test
bf779b5
Raw
History Blame
6.09 kB
"""Shared utilities for Headroom SDK."""
from __future__ import annotations
import hashlib
import json
import re
import uuid
from datetime import datetime
from typing import Any
# Marker format for Headroom modifications
MARKER_PREFIX = "<headroom:"
MARKER_SUFFIX = ">"
def generate_request_id() -> str:
"""Generate a unique request ID."""
return str(uuid.uuid4())
def compute_hash(data: str | bytes) -> str:
"""Compute SHA256 hash, returning hex string."""
if isinstance(data, str):
data = data.encode("utf-8")
return hashlib.sha256(data).hexdigest()
def compute_short_hash(data: str | bytes, length: int = 16) -> str:
"""Compute truncated SHA256 hash."""
return compute_hash(data)[:length]
def compute_messages_hash(messages: list[dict[str, Any]]) -> str:
"""Compute hash of messages list for deduplication."""
# Serialize deterministically
serialized = json.dumps(messages, sort_keys=True, separators=(",", ":"))
return compute_short_hash(serialized)
def compute_prefix_hash(messages: list[dict[str, Any]], prefix_count: int | None = None) -> str:
"""
Compute hash of message prefix for cache alignment.
Args:
messages: List of messages.
prefix_count: Number of messages to include (default: all system messages + 1).
Returns:
Hash of the prefix content.
"""
if not messages:
return compute_short_hash("")
if prefix_count is None:
# Default: system messages + first non-system
prefix_count = 1
for i, msg in enumerate(messages):
if msg.get("role") == "system":
prefix_count = i + 2
else:
break
prefix_messages = messages[:prefix_count]
serialized = json.dumps(prefix_messages, sort_keys=True, separators=(",", ":"))
return compute_short_hash(serialized)
def format_timestamp(dt: datetime | None = None) -> str:
"""Format datetime as ISO8601 string."""
if dt is None:
dt = datetime.utcnow()
return dt.isoformat() + "Z"
def parse_timestamp(ts: str) -> datetime:
"""Parse ISO8601 timestamp string."""
# Handle both with and without Z suffix
ts = ts.rstrip("Z")
return datetime.fromisoformat(ts)
def create_marker(marker_type: str, **kwargs: Any) -> str:
"""
Create a Headroom marker string.
Args:
marker_type: Type of marker (e.g., "tool_digest", "dropped_context").
**kwargs: Attributes to include in the marker.
Returns:
Formatted marker string.
"""
attrs = " ".join(f'{k}="{v}"' for k, v in kwargs.items())
if attrs:
return f"{MARKER_PREFIX}{marker_type} {attrs}{MARKER_SUFFIX}"
return f"{MARKER_PREFIX}{marker_type}{MARKER_SUFFIX}"
def create_tool_digest_marker(original_hash: str) -> str:
"""Create marker for crushed tool output."""
return create_marker("tool_digest", sha256=original_hash)
def create_dropped_context_marker(reason: str, count: int | None = None) -> str:
"""Create marker for dropped context."""
if count is not None:
return create_marker("dropped_context", reason=reason, count=str(count))
return create_marker("dropped_context", reason=reason)
def create_truncated_marker(original_length: int, truncated_to: int) -> str:
"""Create marker for truncated content."""
return create_marker(
"truncated",
original=str(original_length),
truncated_to=str(truncated_to),
)
def extract_markers(text: str) -> list[dict[str, Any]]:
"""
Extract Headroom markers from text.
Returns:
List of dicts with marker_type and attributes.
"""
pattern = re.compile(r"<headroom:(\w+)([^>]*)>")
markers = []
for match in pattern.finditer(text):
marker_type = match.group(1)
attrs_str = match.group(2).strip()
# Parse attributes
attrs: dict[str, str] = {}
if attrs_str:
attr_pattern = re.compile(r'(\w+)="([^"]*)"')
for attr_match in attr_pattern.finditer(attrs_str):
attrs[attr_match.group(1)] = attr_match.group(2)
markers.append({"type": marker_type, "attributes": attrs})
return markers
def safe_json_loads(text: str) -> tuple[Any | None, bool]:
"""
Safely parse JSON, returning (result, success).
Args:
text: JSON string to parse.
Returns:
Tuple of (parsed_result or None, success_bool).
"""
try:
return json.loads(text), True
except (json.JSONDecodeError, ValueError):
return None, False
def safe_json_dumps(obj: Any, **kwargs: Any) -> str:
"""
Safely serialize to JSON with defaults.
Args:
obj: Object to serialize.
**kwargs: Additional json.dumps arguments.
Returns:
JSON string.
"""
kwargs.setdefault("ensure_ascii", False)
kwargs.setdefault("separators", (",", ":")) # Compact by default
return json.dumps(obj, **kwargs)
def estimate_cost(
input_tokens: int,
output_tokens: int,
model: str,
cached_tokens: int = 0,
provider: Any = None,
) -> float | None:
"""
Estimate API cost in USD using provider.
Args:
input_tokens: Number of input tokens.
output_tokens: Number of output tokens.
model: Model name.
cached_tokens: Number of cached input tokens.
provider: Provider instance for cost estimation.
Returns:
Estimated cost in USD, or None if not available.
"""
if provider is None:
return None
result = provider.estimate_cost(input_tokens, output_tokens, model, cached_tokens)
return float(result) if result is not None else None
def format_cost(cost: float) -> str:
"""Format cost as human-readable string."""
if cost < 0.01:
return f"${cost:.4f}"
return f"${cost:.2f}"
def deep_copy_messages(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Create a deep copy of messages list."""
result: list[dict[str, Any]] = json.loads(json.dumps(messages))
return result