Spaces:
Build error
Build error
Commit ·
2b7049f
1
Parent(s): 9f01b57
Refactor: extract data models to headroom/proxy/models.py (Step 1/9)
Browse filesMove ProxyConfig, RequestLog, CacheEntry, RateLimitState to
headroom/proxy/models.py. Re-exported from server.py for backward
compatibility — all existing imports continue to work.
server.py: 8835 → 8643 lines (-192)
models.py: 199 lines (new)
Part of the server.py split effort to improve maintainability.
- headroom/proxy/models.py +199 -0
- headroom/proxy/server.py +4 -196
headroom/proxy/models.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Data models for the Headroom proxy.
|
| 2 |
+
|
| 3 |
+
Contains configuration and data classes used across the proxy modules.
|
| 4 |
+
Extracted from server.py to keep the codebase maintainable.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
from dataclasses import dataclass, field
|
| 10 |
+
from datetime import datetime
|
| 11 |
+
from typing import Any, Literal
|
| 12 |
+
|
| 13 |
+
# =============================================================================
|
| 14 |
+
# Data Models
|
| 15 |
+
# =============================================================================
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@dataclass
|
| 19 |
+
class RequestLog:
|
| 20 |
+
"""Complete log of a single request."""
|
| 21 |
+
|
| 22 |
+
request_id: str
|
| 23 |
+
timestamp: str
|
| 24 |
+
provider: str
|
| 25 |
+
model: str
|
| 26 |
+
|
| 27 |
+
# Tokens
|
| 28 |
+
input_tokens_original: int
|
| 29 |
+
input_tokens_optimized: int
|
| 30 |
+
output_tokens: int | None
|
| 31 |
+
tokens_saved: int
|
| 32 |
+
savings_percent: float
|
| 33 |
+
|
| 34 |
+
# Performance
|
| 35 |
+
optimization_latency_ms: float
|
| 36 |
+
total_latency_ms: float | None
|
| 37 |
+
|
| 38 |
+
# Metadata
|
| 39 |
+
tags: dict[str, str]
|
| 40 |
+
cache_hit: bool
|
| 41 |
+
transforms_applied: list[str]
|
| 42 |
+
|
| 43 |
+
# Waste signals detected in original messages
|
| 44 |
+
waste_signals: dict[str, int] | None = None
|
| 45 |
+
|
| 46 |
+
# Request/Response (optional, for debugging)
|
| 47 |
+
request_messages: list[dict] | None = None
|
| 48 |
+
response_content: str | None = None
|
| 49 |
+
error: str | None = None
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
@dataclass
|
| 53 |
+
class CacheEntry:
|
| 54 |
+
"""Cached response entry."""
|
| 55 |
+
|
| 56 |
+
response_body: bytes
|
| 57 |
+
response_headers: dict[str, str]
|
| 58 |
+
created_at: datetime
|
| 59 |
+
ttl_seconds: int
|
| 60 |
+
hit_count: int = 0
|
| 61 |
+
tokens_saved_per_hit: int = 0
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
@dataclass
|
| 65 |
+
class RateLimitState:
|
| 66 |
+
"""Token bucket rate limiter state."""
|
| 67 |
+
|
| 68 |
+
tokens: float
|
| 69 |
+
last_update: float
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@dataclass
|
| 73 |
+
class ProxyConfig:
|
| 74 |
+
"""Proxy configuration."""
|
| 75 |
+
|
| 76 |
+
# Server
|
| 77 |
+
host: str = "127.0.0.1"
|
| 78 |
+
port: int = 8787
|
| 79 |
+
anthropic_api_url: str | None = None # Custom Anthropic API URL override
|
| 80 |
+
openai_api_url: str | None = None # Custom OpenAI API URL override
|
| 81 |
+
gemini_api_url: str | None = None # Custom Gemini API URL override
|
| 82 |
+
|
| 83 |
+
# Backend: "anthropic" (direct API), "litellm-*" (via LiteLLM), or "anyllm" (via any-llm)
|
| 84 |
+
backend: str = "anthropic"
|
| 85 |
+
bedrock_region: str = "us-west-2"
|
| 86 |
+
bedrock_profile: str | None = None
|
| 87 |
+
anyllm_provider: str = "openai"
|
| 88 |
+
|
| 89 |
+
# Optimization mode
|
| 90 |
+
mode: str = "token_headroom"
|
| 91 |
+
|
| 92 |
+
# Optimization
|
| 93 |
+
optimize: bool = True
|
| 94 |
+
image_optimize: bool = True
|
| 95 |
+
min_tokens_to_crush: int = 500
|
| 96 |
+
max_items_after_crush: int = 50
|
| 97 |
+
keep_last_turns: int = 4
|
| 98 |
+
|
| 99 |
+
# CCR Tool Injection
|
| 100 |
+
ccr_inject_tool: bool = True
|
| 101 |
+
ccr_inject_system_instructions: bool = False
|
| 102 |
+
|
| 103 |
+
# CCR Response Handling
|
| 104 |
+
ccr_handle_responses: bool = True
|
| 105 |
+
ccr_max_retrieval_rounds: int = 3
|
| 106 |
+
|
| 107 |
+
# CCR Context Tracking
|
| 108 |
+
ccr_context_tracking: bool = True
|
| 109 |
+
ccr_proactive_expansion: bool = True
|
| 110 |
+
ccr_max_proactive_expansions: int = 2
|
| 111 |
+
|
| 112 |
+
# Code-aware compression
|
| 113 |
+
code_aware_enabled: bool = True
|
| 114 |
+
|
| 115 |
+
# Per-tool compression profiles
|
| 116 |
+
tool_profiles: dict[str, Any] | None = None
|
| 117 |
+
|
| 118 |
+
# Read lifecycle management
|
| 119 |
+
read_lifecycle: bool = True
|
| 120 |
+
|
| 121 |
+
# Smart content routing
|
| 122 |
+
smart_routing: bool = True
|
| 123 |
+
|
| 124 |
+
# Intelligent context management
|
| 125 |
+
intelligent_context: bool = True
|
| 126 |
+
intelligent_context_scoring: bool = True
|
| 127 |
+
intelligent_context_compress_first: bool = True
|
| 128 |
+
|
| 129 |
+
# Caching
|
| 130 |
+
cache_enabled: bool = True
|
| 131 |
+
cache_ttl_seconds: int = 3600
|
| 132 |
+
cache_max_entries: int = 1000
|
| 133 |
+
|
| 134 |
+
# Rate limiting
|
| 135 |
+
rate_limit_enabled: bool = True
|
| 136 |
+
rate_limit_requests_per_minute: int = 60
|
| 137 |
+
rate_limit_tokens_per_minute: int = 100000
|
| 138 |
+
|
| 139 |
+
# Retry
|
| 140 |
+
retry_enabled: bool = True
|
| 141 |
+
retry_max_attempts: int = 3
|
| 142 |
+
retry_base_delay_ms: int = 1000
|
| 143 |
+
retry_max_delay_ms: int = 30000
|
| 144 |
+
|
| 145 |
+
# Prefix freeze
|
| 146 |
+
prefix_freeze_enabled: bool = True
|
| 147 |
+
prefix_freeze_session_ttl: int = 600
|
| 148 |
+
|
| 149 |
+
# Cost tracking
|
| 150 |
+
cost_tracking_enabled: bool = True
|
| 151 |
+
budget_limit_usd: float | None = None
|
| 152 |
+
budget_period: Literal["hourly", "daily", "monthly"] = "daily"
|
| 153 |
+
|
| 154 |
+
# Logging
|
| 155 |
+
log_requests: bool = True
|
| 156 |
+
log_file: str | None = None
|
| 157 |
+
log_full_messages: bool = False
|
| 158 |
+
|
| 159 |
+
# Fallback
|
| 160 |
+
fallback_enabled: bool = False
|
| 161 |
+
fallback_provider: str | None = None
|
| 162 |
+
|
| 163 |
+
# Timeouts
|
| 164 |
+
request_timeout_seconds: int = 300
|
| 165 |
+
connect_timeout_seconds: int = 10
|
| 166 |
+
|
| 167 |
+
# Connection pool
|
| 168 |
+
max_connections: int = 500
|
| 169 |
+
max_keepalive_connections: int = 100
|
| 170 |
+
http2: bool = True
|
| 171 |
+
|
| 172 |
+
# Memory System
|
| 173 |
+
memory_enabled: bool = False
|
| 174 |
+
memory_backend: Literal["local", "qdrant-neo4j"] = "local"
|
| 175 |
+
memory_db_path: str = "headroom_memory.db"
|
| 176 |
+
memory_inject_tools: bool = True
|
| 177 |
+
traffic_learning_enabled: bool = False
|
| 178 |
+
memory_use_native_tool: bool = False
|
| 179 |
+
memory_inject_context: bool = True
|
| 180 |
+
memory_top_k: int = 10
|
| 181 |
+
memory_min_similarity: float = 0.3
|
| 182 |
+
memory_qdrant_host: str = "localhost"
|
| 183 |
+
memory_qdrant_port: int = 6333
|
| 184 |
+
memory_neo4j_uri: str = "neo4j://localhost:7687"
|
| 185 |
+
memory_neo4j_user: str = "neo4j"
|
| 186 |
+
memory_neo4j_password: str = "password"
|
| 187 |
+
memory_bridge_enabled: bool = False
|
| 188 |
+
memory_bridge_md_paths: list[str] = field(default_factory=list)
|
| 189 |
+
memory_bridge_md_format: str = "auto"
|
| 190 |
+
memory_bridge_auto_import: bool = False
|
| 191 |
+
memory_bridge_export_path: str = ""
|
| 192 |
+
|
| 193 |
+
# License / Usage Reporting
|
| 194 |
+
license_key: str | None = None
|
| 195 |
+
license_cloud_url: str = "https://app.headroomlabs.ai"
|
| 196 |
+
license_report_interval: int = 300
|
| 197 |
+
|
| 198 |
+
# Compression Hooks
|
| 199 |
+
hooks: Any = None
|
headroom/proxy/server.py
CHANGED
|
@@ -33,7 +33,7 @@ import random
|
|
| 33 |
import sys
|
| 34 |
import time
|
| 35 |
from collections import OrderedDict, defaultdict, deque
|
| 36 |
-
from dataclasses import asdict
|
| 37 |
from datetime import datetime, timedelta
|
| 38 |
from pathlib import Path
|
| 39 |
from typing import TYPE_CHECKING, Any, Literal
|
|
@@ -89,6 +89,9 @@ from headroom.config import (
|
|
| 89 |
from headroom.dashboard import get_dashboard_html
|
| 90 |
from headroom.providers import AnthropicProvider, OpenAIProvider
|
| 91 |
from headroom.proxy.memory_handler import MemoryConfig, MemoryHandler
|
|
|
|
|
|
|
|
|
|
| 92 |
from headroom.proxy.savings_tracker import SavingsTracker
|
| 93 |
from headroom.telemetry import get_telemetry_collector
|
| 94 |
from headroom.telemetry.toin import get_toin
|
|
@@ -604,201 +607,6 @@ MAX_RATE_LIMITER_BUCKETS = 1000
|
|
| 604 |
COMPRESSION_TIMEOUT_SECONDS = 30
|
| 605 |
|
| 606 |
|
| 607 |
-
# =============================================================================
|
| 608 |
-
# Data Models
|
| 609 |
-
# =============================================================================
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
@dataclass
|
| 613 |
-
class RequestLog:
|
| 614 |
-
"""Complete log of a single request."""
|
| 615 |
-
|
| 616 |
-
request_id: str
|
| 617 |
-
timestamp: str
|
| 618 |
-
provider: str
|
| 619 |
-
model: str
|
| 620 |
-
|
| 621 |
-
# Tokens
|
| 622 |
-
input_tokens_original: int
|
| 623 |
-
input_tokens_optimized: int
|
| 624 |
-
output_tokens: int | None
|
| 625 |
-
tokens_saved: int
|
| 626 |
-
savings_percent: float
|
| 627 |
-
|
| 628 |
-
# Performance
|
| 629 |
-
optimization_latency_ms: float
|
| 630 |
-
total_latency_ms: float | None
|
| 631 |
-
|
| 632 |
-
# Metadata
|
| 633 |
-
tags: dict[str, str]
|
| 634 |
-
cache_hit: bool
|
| 635 |
-
transforms_applied: list[str]
|
| 636 |
-
|
| 637 |
-
# Waste signals detected in original messages
|
| 638 |
-
waste_signals: dict[str, int] | None = None
|
| 639 |
-
|
| 640 |
-
# Request/Response (optional, for debugging)
|
| 641 |
-
request_messages: list[dict] | None = None
|
| 642 |
-
response_content: str | None = None
|
| 643 |
-
error: str | None = None
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
@dataclass
|
| 647 |
-
class CacheEntry:
|
| 648 |
-
"""Cached response entry."""
|
| 649 |
-
|
| 650 |
-
response_body: bytes
|
| 651 |
-
response_headers: dict[str, str]
|
| 652 |
-
created_at: datetime
|
| 653 |
-
ttl_seconds: int
|
| 654 |
-
hit_count: int = 0
|
| 655 |
-
tokens_saved_per_hit: int = 0
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
@dataclass
|
| 659 |
-
class RateLimitState:
|
| 660 |
-
"""Token bucket rate limiter state."""
|
| 661 |
-
|
| 662 |
-
tokens: float
|
| 663 |
-
last_update: float
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
@dataclass
|
| 667 |
-
class ProxyConfig:
|
| 668 |
-
"""Proxy configuration."""
|
| 669 |
-
|
| 670 |
-
# Server
|
| 671 |
-
host: str = "127.0.0.1"
|
| 672 |
-
port: int = 8787
|
| 673 |
-
anthropic_api_url: str | None = None # Custom Anthropic API URL override
|
| 674 |
-
openai_api_url: str | None = None # Custom OpenAI API URL override
|
| 675 |
-
gemini_api_url: str | None = None # Custom Gemini API URL override
|
| 676 |
-
|
| 677 |
-
# Backend: "anthropic" (direct API), "litellm-*" (via LiteLLM), or "anyllm" (via any-llm)
|
| 678 |
-
# LiteLLM backends: "litellm-bedrock", "litellm-vertex", "litellm-azure", etc.
|
| 679 |
-
# any-llm backends: "anyllm" with --anyllm-provider (openai, mistral, groq, etc.)
|
| 680 |
-
backend: str = "anthropic"
|
| 681 |
-
bedrock_region: str = "us-west-2" # AWS region for Bedrock/LiteLLM
|
| 682 |
-
bedrock_profile: str | None = None # AWS profile (optional)
|
| 683 |
-
anyllm_provider: str = "openai" # any-llm provider (openai, mistral, groq, etc.)
|
| 684 |
-
|
| 685 |
-
# Optimization mode: "token_headroom" (default) or "cost_savings"
|
| 686 |
-
# token_headroom: compress older messages for session extension
|
| 687 |
-
# cost_savings: preserve prefix cache for cost reduction
|
| 688 |
-
mode: str = "token_headroom"
|
| 689 |
-
|
| 690 |
-
# Optimization
|
| 691 |
-
optimize: bool = True
|
| 692 |
-
image_optimize: bool = True # Compress images using trained ML router
|
| 693 |
-
min_tokens_to_crush: int = 500
|
| 694 |
-
max_items_after_crush: int = 50
|
| 695 |
-
keep_last_turns: int = 4
|
| 696 |
-
|
| 697 |
-
# CCR Tool Injection
|
| 698 |
-
ccr_inject_tool: bool = True # Inject headroom_retrieve tool when compression occurs
|
| 699 |
-
ccr_inject_system_instructions: bool = False # Add instructions to system message
|
| 700 |
-
|
| 701 |
-
# CCR Response Handling (intercept and handle CCR tool calls automatically)
|
| 702 |
-
ccr_handle_responses: bool = True # Handle headroom_retrieve calls in responses
|
| 703 |
-
ccr_max_retrieval_rounds: int = 3 # Max rounds of retrieval before returning
|
| 704 |
-
|
| 705 |
-
# CCR Context Tracking (track compressed content across turns)
|
| 706 |
-
ccr_context_tracking: bool = True # Track compressed contexts for proactive expansion
|
| 707 |
-
ccr_proactive_expansion: bool = True # Proactively expand based on query relevance
|
| 708 |
-
ccr_max_proactive_expansions: int = 2 # Max contexts to proactively expand per turn
|
| 709 |
-
|
| 710 |
-
# Code-aware compression (ON by default if installed)
|
| 711 |
-
code_aware_enabled: bool = True # Enable AST-based code compression
|
| 712 |
-
|
| 713 |
-
# Per-tool compression profiles (parsed from CLI/env)
|
| 714 |
-
tool_profiles: dict[str, Any] | None = None
|
| 715 |
-
|
| 716 |
-
# Read lifecycle management (compress stale/superseded Read outputs)
|
| 717 |
-
read_lifecycle: bool = True # ON by default: stale/superseded are provably safe
|
| 718 |
-
|
| 719 |
-
# Smart content routing (routes each message to optimal compressor)
|
| 720 |
-
smart_routing: bool = True # Use ContentRouter for intelligent compression
|
| 721 |
-
|
| 722 |
-
# Intelligent context management (score-based dropping instead of age-based)
|
| 723 |
-
intelligent_context: bool = True # Use IntelligentContextManager instead of RollingWindow
|
| 724 |
-
intelligent_context_scoring: bool = True # Use multi-factor importance scoring
|
| 725 |
-
intelligent_context_compress_first: bool = True # Try deeper compression before dropping
|
| 726 |
-
|
| 727 |
-
# Caching
|
| 728 |
-
cache_enabled: bool = True
|
| 729 |
-
cache_ttl_seconds: int = 3600 # 1 hour
|
| 730 |
-
cache_max_entries: int = 1000
|
| 731 |
-
|
| 732 |
-
# Rate limiting
|
| 733 |
-
rate_limit_enabled: bool = True
|
| 734 |
-
rate_limit_requests_per_minute: int = 60
|
| 735 |
-
rate_limit_tokens_per_minute: int = 100000
|
| 736 |
-
|
| 737 |
-
# Retry
|
| 738 |
-
retry_enabled: bool = True
|
| 739 |
-
retry_max_attempts: int = 3
|
| 740 |
-
retry_base_delay_ms: int = 1000
|
| 741 |
-
retry_max_delay_ms: int = 30000
|
| 742 |
-
|
| 743 |
-
# Prefix freeze: skip compression on already-cached messages
|
| 744 |
-
prefix_freeze_enabled: bool = True # Respect provider's prefix cache
|
| 745 |
-
prefix_freeze_session_ttl: int = 600 # Session tracker TTL (seconds)
|
| 746 |
-
|
| 747 |
-
# Cost tracking
|
| 748 |
-
cost_tracking_enabled: bool = True
|
| 749 |
-
budget_limit_usd: float | None = None # None = unlimited
|
| 750 |
-
budget_period: Literal["hourly", "daily", "monthly"] = "daily"
|
| 751 |
-
|
| 752 |
-
# Logging
|
| 753 |
-
log_requests: bool = True
|
| 754 |
-
log_file: str | None = None
|
| 755 |
-
log_full_messages: bool = False # Privacy: don't log content by default
|
| 756 |
-
|
| 757 |
-
# Fallback
|
| 758 |
-
fallback_enabled: bool = False
|
| 759 |
-
fallback_provider: str | None = None # "openai" or "anthropic"
|
| 760 |
-
|
| 761 |
-
# Timeouts
|
| 762 |
-
request_timeout_seconds: int = 300
|
| 763 |
-
connect_timeout_seconds: int = 10
|
| 764 |
-
|
| 765 |
-
# Connection pool (for high concurrency with multiple agents)
|
| 766 |
-
max_connections: int = 500 # Max total connections to upstream APIs
|
| 767 |
-
max_keepalive_connections: int = 100 # Max idle connections to keep alive
|
| 768 |
-
http2: bool = True # Enable HTTP/2 multiplexing for better throughput
|
| 769 |
-
|
| 770 |
-
# Memory System
|
| 771 |
-
memory_enabled: bool = False # Enable memory integration
|
| 772 |
-
memory_backend: Literal["local", "qdrant-neo4j"] = "local" # Backend type
|
| 773 |
-
memory_db_path: str = "headroom_memory.db" # Path for local backend
|
| 774 |
-
memory_inject_tools: bool = True # Auto-inject memory tools
|
| 775 |
-
traffic_learning_enabled: bool = False # Live traffic pattern learning (--learn)
|
| 776 |
-
memory_use_native_tool: bool = False # Use Anthropic's native memory_20250818 tool
|
| 777 |
-
memory_inject_context: bool = True # Inject searched memories into context
|
| 778 |
-
memory_top_k: int = 10 # Number of memories to inject
|
| 779 |
-
memory_min_similarity: float = 0.3 # Minimum similarity threshold
|
| 780 |
-
# Qdrant+Neo4j config (only used when memory_backend="qdrant-neo4j")
|
| 781 |
-
memory_qdrant_host: str = "localhost"
|
| 782 |
-
memory_qdrant_port: int = 6333
|
| 783 |
-
memory_neo4j_uri: str = "neo4j://localhost:7687"
|
| 784 |
-
memory_neo4j_user: str = "neo4j"
|
| 785 |
-
memory_neo4j_password: str = "password"
|
| 786 |
-
# Memory Bridge (bidirectional markdown <-> Headroom sync)
|
| 787 |
-
memory_bridge_enabled: bool = False
|
| 788 |
-
memory_bridge_md_paths: list[str] = field(default_factory=list)
|
| 789 |
-
memory_bridge_md_format: str = "auto"
|
| 790 |
-
memory_bridge_auto_import: bool = False
|
| 791 |
-
memory_bridge_export_path: str = ""
|
| 792 |
-
|
| 793 |
-
# License / Usage Reporting (managed/enterprise deployments)
|
| 794 |
-
license_key: str | None = None # HEADROOM_LICENSE_KEY env var
|
| 795 |
-
license_cloud_url: str = "https://app.headroomlabs.ai"
|
| 796 |
-
license_report_interval: int = 300 # seconds (5 min)
|
| 797 |
-
|
| 798 |
-
# Compression Hooks (for SaaS and advanced customization)
|
| 799 |
-
hooks: Any = None # CompressionHooks instance, or None for default behavior
|
| 800 |
-
|
| 801 |
-
|
| 802 |
# =============================================================================
|
| 803 |
# Caching
|
| 804 |
# =============================================================================
|
|
|
|
| 33 |
import sys
|
| 34 |
import time
|
| 35 |
from collections import OrderedDict, defaultdict, deque
|
| 36 |
+
from dataclasses import asdict
|
| 37 |
from datetime import datetime, timedelta
|
| 38 |
from pathlib import Path
|
| 39 |
from typing import TYPE_CHECKING, Any, Literal
|
|
|
|
| 89 |
from headroom.dashboard import get_dashboard_html
|
| 90 |
from headroom.providers import AnthropicProvider, OpenAIProvider
|
| 91 |
from headroom.proxy.memory_handler import MemoryConfig, MemoryHandler
|
| 92 |
+
|
| 93 |
+
# Data models (extracted to headroom/proxy/models.py for maintainability)
|
| 94 |
+
from headroom.proxy.models import CacheEntry, ProxyConfig, RateLimitState, RequestLog # noqa: F401
|
| 95 |
from headroom.proxy.savings_tracker import SavingsTracker
|
| 96 |
from headroom.telemetry import get_telemetry_collector
|
| 97 |
from headroom.telemetry.toin import get_toin
|
|
|
|
| 607 |
COMPRESSION_TIMEOUT_SECONDS = 30
|
| 608 |
|
| 609 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 610 |
# =============================================================================
|
| 611 |
# Caching
|
| 612 |
# =============================================================================
|