chopratejas commited on
Commit
2b7049f
·
1 Parent(s): 9f01b57

Refactor: extract data models to headroom/proxy/models.py (Step 1/9)

Browse files

Move 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.

Files changed (2) hide show
  1. headroom/proxy/models.py +199 -0
  2. 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, dataclass, field
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
  # =============================================================================