Spaces:
Build error
Build error
Download headroom/cache/base.py from minhtudragon/headroom_3: direct link, hf CLI and curl.
- Browser
- Download file 9.58 kB
-
https://huggingface.co/spaces/minhtudragon/headroom_3/resolve/dc72670bce7adf8f3280b32d838359a630e3c8ab/headroom/cache/base.py
- Command line
-
hf download hf://spaces/minhtudragon/headroom_3@dc72670bce7adf8f3280b32d838359a630e3c8ab/headroom/cache/base.py
-
curl -L -o base.py https://huggingface.co/spaces/minhtudragon/headroom_3/resolve/dc72670bce7adf8f3280b32d838359a630e3c8ab/headroom/cache/base.py
9.58 kB
| """ | |
| Base types and interfaces for cache optimization. | |
| This module defines the core abstractions that all cache optimizers implement. | |
| The design allows for provider-specific implementations while maintaining a | |
| consistent interface for users. | |
| """ | |
| from __future__ import annotations | |
| from abc import ABC, abstractmethod | |
| from dataclasses import dataclass, field | |
| from datetime import datetime | |
| from enum import Enum | |
| from typing import Any, Literal, Protocol, runtime_checkable | |
| class CacheStrategy(Enum): | |
| """Cache optimization strategy.""" | |
| # Just stabilize prefix (move dates, normalize whitespace) | |
| PREFIX_STABILIZATION = "prefix_stabilization" | |
| # Insert explicit cache breakpoints (Anthropic) | |
| EXPLICIT_BREAKPOINTS = "explicit_breakpoints" | |
| # Manage separate cached content objects (Google) | |
| CACHED_CONTENT = "cached_content" | |
| # No optimization possible (provider doesn't support caching) | |
| NONE = "none" | |
| class BreakpointLocation(Enum): | |
| """Where to insert cache breakpoints.""" | |
| AFTER_SYSTEM = "after_system" | |
| AFTER_TOOLS = "after_tools" | |
| AFTER_EXAMPLES = "after_examples" | |
| CUSTOM = "custom" | |
| class CacheBreakpoint: | |
| """ | |
| Represents a cache breakpoint location. | |
| For Anthropic, this maps to cache_control blocks. | |
| For other providers, this is informational. | |
| """ | |
| # Message index where breakpoint should be inserted | |
| message_index: int | |
| # Location type | |
| location: BreakpointLocation | |
| # For content arrays, index within the content | |
| content_index: int | None = None | |
| # Token count at this breakpoint | |
| tokens_at_breakpoint: int = 0 | |
| # Reason for this breakpoint | |
| reason: str = "" | |
| class CacheConfig: | |
| """Configuration for cache optimization.""" | |
| # Whether to optimize at all | |
| enabled: bool = True | |
| # Strategy to use (auto-detected if None) | |
| strategy: CacheStrategy | None = None | |
| # Minimum tokens before caching makes sense | |
| min_cacheable_tokens: int = 1024 | |
| # Maximum number of breakpoints (Anthropic limit is 4) | |
| max_breakpoints: int = 4 | |
| # Patterns to extract and move to dynamic section | |
| date_patterns: list[str] = field( | |
| default_factory=lambda: [ | |
| r"Today is \w+ \d{1,2},? \d{4}\.?", | |
| r"Current date: \d{4}-\d{2}-\d{2}", | |
| r"The current time is .+\.", | |
| ] | |
| ) | |
| # Whether to normalize whitespace | |
| normalize_whitespace: bool = True | |
| # Collapse multiple blank lines | |
| collapse_blank_lines: bool = True | |
| # Separator between static and dynamic content | |
| dynamic_separator: str = "\n\n---\n\n" | |
| # Dynamic content detection tiers (for OpenAI prefix stabilization) | |
| # - "regex": Fast pattern matching (~0ms) - always recommended | |
| # - "ner": Named Entity Recognition via spaCy (~5-10ms) - catches names, money, etc. | |
| # - "semantic": Embedding similarity (~20-50ms) - catches volatile patterns | |
| # Default is regex-only for speed. Add tiers for better detection at cost of latency. | |
| dynamic_detection_tiers: list[Literal["regex", "ner", "semantic"]] = field( | |
| default_factory=lambda: ["regex"] | |
| ) | |
| # For semantic caching | |
| semantic_cache_enabled: bool = False | |
| semantic_similarity_threshold: float = 0.95 | |
| semantic_cache_ttl_seconds: int = 300 | |
| class CacheMetrics: | |
| """Metrics about cache optimization.""" | |
| # Prefix analysis | |
| stable_prefix_tokens: int = 0 | |
| stable_prefix_hash: str = "" | |
| # Breakpoint info | |
| breakpoints_inserted: int = 0 | |
| breakpoint_locations: list[CacheBreakpoint] = field(default_factory=list) | |
| # Cache hit estimation | |
| prefix_changed_from_previous: bool = False | |
| previous_prefix_hash: str | None = None | |
| estimated_cache_hit: bool = False | |
| # Savings estimation | |
| estimated_savings_percent: float = 0.0 | |
| cacheable_tokens: int = 0 | |
| non_cacheable_tokens: int = 0 | |
| # Provider-specific | |
| provider_cache_id: str | None = None # For Google's CachedContent | |
| cache_ttl_remaining_seconds: int | None = None | |
| class OptimizationContext: | |
| """Context for optimization request.""" | |
| # Request tracking | |
| request_id: str = "" | |
| timestamp: datetime = field(default_factory=datetime.now) | |
| # Provider info | |
| provider: str = "" | |
| model: str = "" | |
| # Query for relevance (used by semantic cache) | |
| query: str | None = None | |
| # Previous request info (for cache hit detection) | |
| previous_prefix_hash: str | None = None | |
| # Additional metadata | |
| metadata: dict[str, Any] = field(default_factory=dict) | |
| class CacheResult: | |
| """Result of cache optimization.""" | |
| # Optimized messages | |
| messages: list[dict[str, Any]] | |
| # Whether this was a semantic cache hit | |
| semantic_cache_hit: bool = False | |
| # Cached response (if semantic cache hit) | |
| cached_response: Any | None = None | |
| # Optimization metrics | |
| metrics: CacheMetrics = field(default_factory=CacheMetrics) | |
| # Tokens before/after | |
| tokens_before: int = 0 | |
| tokens_after: int = 0 | |
| # Transforms applied | |
| transforms_applied: list[str] = field(default_factory=list) | |
| # Warnings | |
| warnings: list[str] = field(default_factory=list) | |
| class CacheOptimizer(Protocol): | |
| """ | |
| Protocol for cache optimizers. | |
| All provider-specific optimizers must implement this interface. | |
| This allows for easy swapping of implementations and plugin registration. | |
| """ | |
| def name(self) -> str: | |
| """Name of this optimizer.""" | |
| ... | |
| def provider(self) -> str: | |
| """Provider this optimizer is for.""" | |
| ... | |
| def strategy(self) -> CacheStrategy: | |
| """The caching strategy this optimizer uses.""" | |
| ... | |
| def optimize( | |
| self, | |
| messages: list[dict[str, Any]], | |
| context: OptimizationContext, | |
| config: CacheConfig | None = None, | |
| ) -> CacheResult: | |
| """ | |
| Optimize messages for caching. | |
| Args: | |
| messages: The messages to optimize. | |
| context: Optimization context with request info. | |
| config: Optional configuration override. | |
| Returns: | |
| CacheResult with optimized messages and metrics. | |
| """ | |
| ... | |
| def get_metrics(self) -> CacheMetrics: | |
| """Get aggregated metrics from this optimizer.""" | |
| ... | |
| def estimate_savings( | |
| self, | |
| messages: list[dict[str, Any]], | |
| context: OptimizationContext, | |
| ) -> float: | |
| """ | |
| Estimate potential savings from optimization. | |
| Returns: | |
| Estimated savings as a percentage (0-100). | |
| """ | |
| ... | |
| class BaseCacheOptimizer(ABC): | |
| """ | |
| Abstract base class for cache optimizers. | |
| Provides common functionality for all optimizers. | |
| """ | |
| def __init__(self, config: CacheConfig | None = None): | |
| self.config = config or CacheConfig() | |
| self._metrics_history: list[CacheMetrics] = [] | |
| self._previous_prefix_hash: str | None = None | |
| def name(self) -> str: | |
| """Name of this optimizer.""" | |
| ... | |
| def provider(self) -> str: | |
| """Provider this optimizer is for.""" | |
| ... | |
| def strategy(self) -> CacheStrategy: | |
| """The caching strategy this optimizer uses.""" | |
| ... | |
| def optimize( | |
| self, | |
| messages: list[dict[str, Any]], | |
| context: OptimizationContext, | |
| config: CacheConfig | None = None, | |
| ) -> CacheResult: | |
| """Optimize messages for caching.""" | |
| ... | |
| def get_metrics(self) -> CacheMetrics: | |
| """Get aggregated metrics.""" | |
| if not self._metrics_history: | |
| return CacheMetrics() | |
| # Return most recent metrics | |
| return self._metrics_history[-1] | |
| def estimate_savings( | |
| self, | |
| messages: list[dict[str, Any]], | |
| context: OptimizationContext, | |
| ) -> float: | |
| """Estimate potential savings.""" | |
| # Default implementation - subclasses can override | |
| result = self.optimize(messages, context) | |
| return result.metrics.estimated_savings_percent | |
| def _record_metrics(self, metrics: CacheMetrics) -> None: | |
| """Record metrics for history.""" | |
| self._metrics_history.append(metrics) | |
| # Keep only last 100 entries | |
| if len(self._metrics_history) > 100: | |
| self._metrics_history = self._metrics_history[-100:] | |
| def _compute_prefix_hash(self, content: str) -> str: | |
| """Compute a short hash of content.""" | |
| import hashlib | |
| return hashlib.sha256(content.encode()).hexdigest()[:12] | |
| def _extract_system_content(self, messages: list[dict[str, Any]]) -> str: | |
| """Extract content from system messages.""" | |
| parts = [] | |
| for msg in messages: | |
| if msg.get("role") == "system": | |
| content = msg.get("content", "") | |
| if isinstance(content, str): | |
| parts.append(content) | |
| elif isinstance(content, list): | |
| # Handle content blocks | |
| for block in content: | |
| if isinstance(block, dict) and block.get("type") == "text": | |
| parts.append(block.get("text", "")) | |
| return "\n".join(parts) | |
| def _count_tokens_estimate(self, text: str) -> int: | |
| """Rough token count estimate (4 chars per token).""" | |
| return len(text) // 4 | |