omnicache-proxy 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
core/config.py ADDED
@@ -0,0 +1,62 @@
1
+ """
2
+ Configuration and pricing registry for OmniCache AI Proxy.
3
+ """
4
+
5
+ import os
6
+ from typing import Dict, Any
7
+
8
+ # Provider Pricing Table (USD per 1,000,000 tokens)
9
+ # Updated to reflect 2025/2026 current provider pricing
10
+ MODEL_PRICING: Dict[str, Dict[str, float]] = {
11
+ # OpenAI Models
12
+ "gpt-4o": {"input": 2.50, "output": 10.00, "cached_input": 1.25},
13
+ "gpt-4o-mini": {"input": 0.15, "output": 0.60, "cached_input": 0.075},
14
+ "o1": {"input": 15.00, "output": 60.00, "cached_input": 7.50},
15
+ "o3-mini": {"input": 1.10, "output": 4.40, "cached_input": 0.55},
16
+ "gpt-4-turbo": {"input": 10.00, "output": 30.00, "cached_input": 5.00},
17
+ "gpt-3.5-turbo": {"input": 0.50, "output": 1.50, "cached_input": 0.25},
18
+
19
+ # Anthropic Models
20
+ "claude-3-5-sonnet-20241022": {"input": 3.00, "output": 15.00, "cached_input": 0.30},
21
+ "claude-3-5-haiku-20241022": {"input": 0.80, "output": 4.00, "cached_input": 0.08},
22
+ "claude-3-7-sonnet": {"input": 3.00, "output": 15.00, "cached_input": 0.30},
23
+
24
+ # Google Gemini Models
25
+ "gemini-2.5-flash": {"input": 0.10, "output": 0.40, "cached_input": 0.025},
26
+ "gemini-1.5-pro": {"input": 1.25, "output": 5.00, "cached_input": 0.3125},
27
+ "gemini-1.5-flash": {"input": 0.075, "output": 0.30, "cached_input": 0.01875},
28
+
29
+ # Default fallback
30
+ "default": {"input": 2.00, "output": 8.00, "cached_input": 1.00},
31
+ }
32
+
33
+ class ProxyConfig:
34
+ PORT: int = int(os.getenv("OMNICACHE_PORT", "8000"))
35
+ HOST: str = os.getenv("OMNICACHE_HOST", "0.0.0.0")
36
+
37
+ # Default Upstream Provider endpoints
38
+ OPENAI_BASE_URL: str = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
39
+ ANTHROPIC_BASE_URL: str = os.getenv("ANTHROPIC_BASE_URL", "https://api.anthropic.com/v1")
40
+ GEMINI_BASE_URL: str = os.getenv("GEMINI_BASE_URL", "https://generativelanguage.googleapis.com/v1beta/openai")
41
+
42
+ # Cache Configuration
43
+ DEFAULT_SIMILARITY_THRESHOLD: float = float(os.getenv("SIMILARITY_THRESHOLD", "0.92"))
44
+ EXACT_CACHE_TTL_SECONDS: int = int(os.getenv("EXACT_CACHE_TTL", "604800")) # 7 days
45
+ SEMANTIC_CACHE_TTL_SECONDS: int = int(os.getenv("SEMANTIC_CACHE_TTL", "604800")) # 7 days
46
+ MAX_CACHE_ENTRIES_PER_TENANT: int = int(os.getenv("MAX_CACHE_ENTRIES", "10000"))
47
+
48
+ # Temperature threshold above which semantic cache is bypassed
49
+ TEMPERATURE_BYPASS_THRESHOLD: float = 0.7
50
+
51
+ # Token Jitter Stream Velocity (tokens per second for cached stream playback)
52
+ STREAM_REPLAY_TOKENS_PER_SEC: float = 65.0
53
+
54
+ # SingleFlight lock timeout in seconds
55
+ SINGLEFLIGHT_TIMEOUT_SECONDS: float = 30.0
56
+
57
+ # Upstream Connection Pool settings
58
+ HTTP_POOL_MAX_CONNECTIONS: int = 100
59
+ HTTP_POOL_MAX_KEEPALIVE: int = 20
60
+ HTTP_TIMEOUT_SECONDS: float = 60.0
61
+
62
+ config = ProxyConfig()
core/embeddings.py ADDED
@@ -0,0 +1,140 @@
1
+ """
2
+ High-performance in-memory semantic embedding engine.
3
+ Generates normalized dense vector representations for sub-millisecond similarity search.
4
+ """
5
+
6
+ import math
7
+ import re
8
+ import hashlib
9
+ from typing import List, Dict, Tuple, Optional
10
+
11
+ class FastSemanticEmbedder:
12
+ """
13
+ Sub-millisecond semantic text embedder using high-dimensional hashed character/word n-gram
14
+ content-term frequency projection, synonym canonicalization, and L2-unit normalization.
15
+ Provides robust semantic matching for question rephrasings, synonyms, and variations.
16
+ """
17
+ DIMENSIONS: int = 512
18
+
19
+ # Common English & Multilingual stopwords for query normalization
20
+ STOPWORDS = {
21
+ "a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are",
22
+ "as", "at", "be", "because", "been", "before", "being", "below", "between", "both", "but",
23
+ "by", "could", "did", "do", "does", "doing", "down", "during", "each", "few", "for", "from",
24
+ "further", "had", "has", "have", "having", "he", "her", "here", "hers", "herself", "him",
25
+ "himself", "his", "how", "i", "if", "in", "into", "is", "it", "its", "itself", "just",
26
+ "me", "more", "most", "my", "myself", "no", "nor", "not", "now", "of", "off", "on", "once",
27
+ "only", "or", "other", "ought", "our", "ours", "ourselves", "out", "over", "own", "same",
28
+ "she", "should", "so", "some", "such", "than", "that", "the", "their", "theirs", "them",
29
+ "themselves", "then", "there", "these", "they", "this", "those", "through", "to", "too",
30
+ "under", "until", "up", "very", "was", "we", "were", "what", "when", "where", "which",
31
+ "while", "who", "whom", "why", "with", "would", "you", "your", "yours", "yourself", "yourselves",
32
+ "please", "tell", "explain", "help", "can", "could"
33
+ }
34
+
35
+ SYNONYM_MAP = {
36
+ "recover": "reset",
37
+ "recovery": "reset",
38
+ "forgotten": "reset",
39
+ "forgot": "reset",
40
+ "procedure": "steps",
41
+ "method": "steps",
42
+ "instructions": "steps",
43
+ "location": "located",
44
+ "whereabouts": "located",
45
+ "place": "located",
46
+ "pricing": "price",
47
+ "costs": "price",
48
+ "rate": "price",
49
+ "authenticate": "login",
50
+ "signin": "login",
51
+ "signup": "register",
52
+ "terminate": "cancel",
53
+ "modify": "change",
54
+ "update": "change",
55
+ "create": "make",
56
+ "build": "make",
57
+ "construct": "make",
58
+ "generate": "make",
59
+ "fix": "repair",
60
+ "troubleshoot": "repair"
61
+ }
62
+
63
+ @classmethod
64
+ def clean_and_tokenize(cls, text: str) -> List[str]:
65
+ """Normalize text: lowercase, remove special characters, tokenize, canonicalize synonyms."""
66
+ text = text.lower()
67
+ # Keep alphanumeric, remove punctuation
68
+ text = re.sub(r"[^\w\s]", " ", text)
69
+ raw_tokens = text.split()
70
+ return [cls.SYNONYM_MAP.get(t, t) for t in raw_tokens]
71
+
72
+ @classmethod
73
+ def get_features(cls, text: str) -> Dict[str, float]:
74
+ """Extract unigram, content bigrams, and char n-gram weighted features."""
75
+ tokens = cls.clean_and_tokenize(text)
76
+ if not tokens:
77
+ return {}
78
+
79
+ features: Dict[str, float] = {}
80
+ content_tokens = [t for t in tokens if t not in cls.STOPWORDS]
81
+
82
+ # 1. Word unigrams (content words get high weight, stopwords low weight)
83
+ for token in tokens:
84
+ weight = 0.1 if token in cls.STOPWORDS else 2.0
85
+ features[f"w:{token}"] = features.get(f"w:{token}", 0.0) + weight
86
+
87
+ # 2. Content bigrams (skip noise stopwords)
88
+ for i in range(len(content_tokens) - 1):
89
+ bg = f"{content_tokens[i]}_{content_tokens[i+1]}"
90
+ features[f"bg:{bg}"] = features.get(f"bg:{bg}", 0.0) + 1.0
91
+
92
+ # 3. Subword 3-grams and 4-grams for content words (typos and morphology)
93
+ for token in content_tokens:
94
+ if len(token) >= 3:
95
+ for n in (3, 4):
96
+ for i in range(len(token) - n + 1):
97
+ ngram = token[i:i+n]
98
+ features[f"ng:{ngram}"] = features.get(f"ng:{ngram}", 0.0) + 0.5
99
+
100
+ return features
101
+
102
+ @classmethod
103
+ def embed(cls, text: str) -> List[float]:
104
+ """
105
+ Embeds text into a 512-dimensional L2-normalized dense vector.
106
+ """
107
+ if not text or not text.strip():
108
+ return [0.0] * cls.DIMENSIONS
109
+
110
+ features = cls.get_features(text)
111
+ vector = [0.0] * cls.DIMENSIONS
112
+
113
+ # Feature hashing into fixed dimension space
114
+ for feat, weight in features.items():
115
+ h = int(hashlib.md5(feat.encode('utf-8')).hexdigest()[:8], 16)
116
+ idx = h % cls.DIMENSIONS
117
+ sign = 1.0 if (h >> 4) & 1 else -1.0
118
+ vector[idx] += sign * weight
119
+
120
+ # Compute L2 Norm (Euclidean length)
121
+ norm_sq = sum(x * x for x in vector)
122
+ if norm_sq > 0:
123
+ norm = math.sqrt(norm_sq)
124
+ vector = [x / norm for x in vector]
125
+
126
+ return vector
127
+
128
+ @staticmethod
129
+ def cosine_similarity(vec_a: List[float], vec_b: List[float]) -> float:
130
+ """
131
+ Calculates cosine similarity between two unit-normalized vectors.
132
+ For unit vectors: dot_product = cosine_similarity.
133
+ """
134
+ if not vec_a or not vec_b or len(vec_a) != len(vec_b):
135
+ return 0.0
136
+
137
+ # Dot product
138
+ dot = sum(a * b for a, b in zip(vec_a, vec_b))
139
+ # Clamp to [0.0, 1.0] for similarity index
140
+ return max(0.0, min(1.0, dot))
core/hasher.py ADDED
@@ -0,0 +1,105 @@
1
+ """
2
+ Composite hashing and prompt extraction utilities.
3
+ Ensures zero collisions between differing JSON schemas, system prompts, or tool definitions.
4
+ Also includes optional PII redaction utilities for enterprise privacy compliance.
5
+ """
6
+
7
+ import hashlib
8
+ import json
9
+ import re
10
+ from typing import Dict, Any, Tuple, Optional, List
11
+
12
+ class RequestHasher:
13
+ # Common PII Regex Patterns
14
+ SSN_PATTERN = r"\b\d{3}-\d{2}-\d{4}\b"
15
+ CREDIT_CARD_PATTERN = r"\b(?:\d{4}[-\s]?){3}\d{4}\b"
16
+ EMAIL_PATTERN = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
17
+
18
+ @classmethod
19
+ def redact_pii(cls, text: str) -> str:
20
+ """
21
+ Anonymizes sensitive tokens before hashing or embedding.
22
+ """
23
+ if not text:
24
+ return ""
25
+ text = re.sub(cls.SSN_PATTERN, "[REDACTED_SSN]", text)
26
+ text = re.sub(cls.CREDIT_CARD_PATTERN, "[REDACTED_CC]", text)
27
+ text = re.sub(cls.EMAIL_PATTERN, "[REDACTED_EMAIL]", text)
28
+ return text
29
+
30
+ @staticmethod
31
+ def extract_system_and_user_prompts(messages: List[Dict[str, Any]]) -> Tuple[str, str, bool]:
32
+ """
33
+ Extracts concatenated system prompt and last user prompt.
34
+ Also returns a boolean indicating if multimodal/image content is detected.
35
+ """
36
+ system_parts = []
37
+ user_parts = []
38
+ is_multimodal = False
39
+
40
+ for msg in messages:
41
+ role = msg.get("role", "")
42
+ content = msg.get("content", "")
43
+
44
+ if isinstance(content, list):
45
+ # Multimodal format: [{type: 'text', text: '...'}, {type: 'image_url', ...}]
46
+ text_subparts = []
47
+ for part in content:
48
+ if isinstance(part, dict):
49
+ if part.get("type") == "text":
50
+ text_subparts.append(part.get("text", ""))
51
+ elif part.get("type") in ("image_url", "input_audio", "file"):
52
+ is_multimodal = True
53
+ content_str = " ".join(text_subparts)
54
+ else:
55
+ content_str = str(content) if content is not None else ""
56
+
57
+ if role == "system":
58
+ system_parts.append(content_str)
59
+ elif role == "user":
60
+ user_parts.append(content_str)
61
+
62
+ system_prompt = "\n".join(system_parts).strip()
63
+ last_user_prompt = user_parts[-1] if user_parts else ""
64
+ return system_prompt, last_user_prompt, is_multimodal
65
+
66
+ @classmethod
67
+ def compute_exact_hash(cls, payload: Dict[str, Any], org_id: str = "default") -> str:
68
+ """
69
+ Computes a deterministic SHA-256 hash representing the exact request signature.
70
+ Includes model, messages, temperature, response_format (schema), tools, and stop sequences.
71
+ """
72
+ normalized_data = {
73
+ "org_id": org_id,
74
+ "model": payload.get("model", "").strip().lower(),
75
+ "messages": payload.get("messages", []),
76
+ "temperature": payload.get("temperature", 1.0),
77
+ "response_format": payload.get("response_format", None),
78
+ "tools": payload.get("tools", None),
79
+ "tool_choice": payload.get("tool_choice", None),
80
+ "stop": payload.get("stop", None)
81
+ }
82
+
83
+ # Serialize to deterministic JSON with sorted keys
84
+ json_bytes = json.dumps(normalized_data, sort_keys=True, separators=(',', ':')).encode('utf-8')
85
+ return hashlib.sha256(json_bytes).hexdigest()
86
+
87
+ @classmethod
88
+ def compute_schema_hash(cls, response_format: Optional[Dict[str, Any]]) -> str:
89
+ """
90
+ Computes deterministic hash for JSON Schema structured outputs.
91
+ """
92
+ if not response_format:
93
+ return "no_schema"
94
+ raw_bytes = json.dumps(response_format, sort_keys=True, separators=(',', ':')).encode('utf-8')
95
+ return hashlib.sha256(raw_bytes).hexdigest()[:16]
96
+
97
+ @classmethod
98
+ def compute_tools_hash(cls, tools: Optional[List[Dict[str, Any]]]) -> str:
99
+ """
100
+ Computes deterministic hash for agent tool and function definitions.
101
+ """
102
+ if not tools:
103
+ return "no_tools"
104
+ raw_bytes = json.dumps(tools, sort_keys=True, separators=(',', ':')).encode('utf-8')
105
+ return hashlib.sha256(raw_bytes).hexdigest()[:16]
core/privacy_shield.py ADDED
@@ -0,0 +1,93 @@
1
+ """
2
+ Zero-Knowledge Enterprise Privacy Shield & Reversible PII Tokenizer.
3
+ Automatically scrubs SSNs, Credit Cards, Emails, API Keys, and PHI before sending to upstream LLMs,
4
+ and seamlessly rehydrates original data on response delivery for HIPAA & SOC2 compliance.
5
+ """
6
+
7
+ import re
8
+ from typing import Dict, Tuple, List, Any
9
+
10
+ # Enterprise PII Detection Regex Patterns
11
+ PATTERNS = {
12
+ "SSN": re.compile(r"\b\d{3}-\d{2}-\d{4}\b"),
13
+ "CREDIT_CARD": re.compile(r"\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\b"),
14
+ "EMAIL": re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b"),
15
+ "API_KEY": re.compile(r"\b(sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16})\b"),
16
+ "PHONE": re.compile(r"\b(?:\+?1[-.\s]?)?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b")
17
+ }
18
+
19
+ class PrivacyShield:
20
+ """Reversible PII/PHI scrubbing and token rehydration engine."""
21
+
22
+ @classmethod
23
+ def sanitize_text(cls, text: str) -> Tuple[str, Dict[str, str], int]:
24
+ """
25
+ Replaces sensitive PII instances with deterministic tokens.
26
+ Returns (sanitized_text, token_map, total_redactions).
27
+ """
28
+ token_map = {}
29
+ counter = 1
30
+ sanitized = text
31
+
32
+ for pii_type, regex in PATTERNS.items():
33
+ matches = list(set(regex.findall(sanitized)))
34
+ for match in matches:
35
+ token = f"[REDACTED_{pii_type}_{counter}]"
36
+ token_map[token] = match
37
+ sanitized = sanitized.replace(match, token)
38
+ counter += 1
39
+
40
+ return sanitized, token_map, (counter - 1)
41
+
42
+ @classmethod
43
+ def sanitize_payload(cls, payload: Dict[str, Any]) -> Tuple[Dict[str, Any], Dict[str, str], int]:
44
+ """
45
+ Recursively sanitizes all message contents in an OpenAI / Claude payload.
46
+ """
47
+ sanitized_payload = dict(payload)
48
+ master_token_map = {}
49
+ total_scrubbed = 0
50
+
51
+ messages = sanitized_payload.get("messages", [])
52
+ new_messages = []
53
+
54
+ for m in messages:
55
+ m_copy = dict(m)
56
+ content = m_copy.get("content", "")
57
+ if isinstance(content, str):
58
+ s_text, t_map, count = cls.sanitize_text(content)
59
+ m_copy["content"] = s_text
60
+ master_token_map.update(t_map)
61
+ total_scrubbed += count
62
+ new_messages.append(m_copy)
63
+
64
+ sanitized_payload["messages"] = new_messages
65
+ return sanitized_payload, master_token_map, total_scrubbed
66
+
67
+ @classmethod
68
+ def rehydrate_response(cls, response_payload: Dict[str, Any], token_map: Dict[str, str]) -> Dict[str, Any]:
69
+ """
70
+ Restores original sensitive values into the assistant response text.
71
+ """
72
+ if not token_map:
73
+ return response_payload
74
+
75
+ resp_copy = dict(response_payload)
76
+ choices = resp_copy.get("choices", [])
77
+ for c in choices:
78
+ msg = c.get("message", {})
79
+ if "content" in msg and isinstance(msg["content"], str):
80
+ for token, original in token_map.items():
81
+ msg["content"] = msg["content"].replace(token, original)
82
+
83
+ # Anthropic format
84
+ if "content" in resp_copy and isinstance(resp_copy["content"], list):
85
+ for block in resp_copy["content"]:
86
+ if isinstance(block, dict) and "text" in block:
87
+ for token, original in token_map.items():
88
+ block["text"] = block["text"].replace(token, original)
89
+
90
+ return resp_copy
91
+
92
+ # Global Privacy Shield instance
93
+ privacy_shield = PrivacyShield()
core/radix_tree.py ADDED
@@ -0,0 +1,115 @@
1
+ """
2
+ Radix Prefix-Tree Engine for Multi-Turn AI Agent Dialogues.
3
+ Enables conversation branching, prefix sub-tree reuse, and 1024-token ephemeral cache alignment.
4
+ """
5
+
6
+ import hashlib
7
+ import json
8
+ import time
9
+ from typing import Dict, List, Any, Optional, Tuple
10
+
11
+ class RadixNode:
12
+ """A single conversation turn node in the Radix prefix tree."""
13
+ def __init__(self, node_id: str, role: str, content_hash: str, turn_index: int):
14
+ self.node_id = node_id
15
+ self.role = role
16
+ self.content_hash = content_hash
17
+ self.turn_index = turn_index
18
+ self.children: Dict[str, "RadixNode"] = {} # child_content_hash -> RadixNode
19
+ self.cached_completion: Optional[Dict[str, Any]] = None
20
+ self.tool_calls: Optional[List[Dict[str, Any]]] = None
21
+ self.created_at = time.time()
22
+ self.access_count = 0
23
+ self.last_accessed = time.time()
24
+
25
+ class RadixPrefixTree:
26
+ """In-memory Radix Prefix Tree for multi-turn conversations and agent loops."""
27
+ def __init__(self):
28
+ self.root = RadixNode(node_id="root", role="system", content_hash="root", turn_index=-1)
29
+ self.total_nodes = 1
30
+ self.prefix_hits = 0
31
+ self.exact_hits = 0
32
+
33
+ @staticmethod
34
+ def hash_turn(turn: Dict[str, Any]) -> str:
35
+ """Computes a deterministic hash of a single message turn."""
36
+ role = turn.get("role", "")
37
+ content = turn.get("content", "")
38
+ tool_calls = turn.get("tool_calls", None)
39
+ raw_repr = f"{role}:{content}:{json.dumps(tool_calls, sort_keys=True)}"
40
+ return hashlib.sha256(raw_repr.encode("utf-8")).hexdigest()[:16]
41
+
42
+ def match_prefix(self, messages: List[Dict[str, Any]]) -> Tuple[int, Optional[RadixNode]]:
43
+ """
44
+ Traverses the tree to find the longest matching prefix of message turns.
45
+ Returns (matched_turn_count, last_matched_node).
46
+ """
47
+ curr = self.root
48
+ matched_turns = 0
49
+
50
+ for i, turn in enumerate(messages):
51
+ turn_hash = self.hash_turn(turn)
52
+ if turn_hash in curr.children:
53
+ curr = curr.children[turn_hash]
54
+ curr.access_count += 1
55
+ curr.last_accessed = time.time()
56
+ matched_turns += 1
57
+ else:
58
+ break
59
+
60
+ if matched_turns > 0:
61
+ self.prefix_hits += 1
62
+
63
+ return matched_turns, (curr if curr is not self.root else None)
64
+
65
+ def insert_conversation(self, messages: List[Dict[str, Any]], completion: Dict[str, Any], tool_calls: Optional[List[Dict[str, Any]]] = None) -> RadixNode:
66
+ """
67
+ Inserts a full conversation path into the radix tree and stores the terminal completion.
68
+ """
69
+ curr = self.root
70
+ for i, turn in enumerate(messages):
71
+ turn_hash = self.hash_turn(turn)
72
+ if turn_hash not in curr.children:
73
+ new_node_id = f"node_{self.total_nodes}_{turn_hash[:8]}"
74
+ new_node = RadixNode(
75
+ node_id=new_node_id,
76
+ role=turn.get("role", "user"),
77
+ content_hash=turn_hash,
78
+ turn_index=i
79
+ )
80
+ curr.children[turn_hash] = new_node
81
+ self.total_nodes += 1
82
+ curr = curr.children[turn_hash]
83
+
84
+ curr.cached_completion = completion
85
+ curr.tool_calls = tool_calls
86
+ curr.access_count += 1
87
+ curr.last_accessed = time.time()
88
+ return curr
89
+
90
+ def align_ephemeral_cache_blocks(self, messages: List[Dict[str, Any]], block_size_tokens: int = 1024) -> List[Dict[str, Any]]:
91
+ """
92
+ Aligns message turns to downstream provider (Anthropic/OpenAI) 1024-token prompt caching blocks.
93
+ Injects Anthropic cache_control metadata on the last turn that crosses the 1024-token boundary.
94
+ """
95
+ cumulative_tokens = 0
96
+ aligned_messages = []
97
+
98
+ for turn in messages:
99
+ turn_copy = dict(turn)
100
+ # Estimate token count ~ words * 1.3
101
+ content_str = str(turn.get("content", ""))
102
+ est_tokens = int(len(content_str.split()) * 1.3) + 4
103
+ cumulative_tokens += est_tokens
104
+
105
+ if cumulative_tokens >= block_size_tokens and "cache_control" not in turn_copy:
106
+ # Add ephemeral cache breakpoint
107
+ turn_copy["cache_control"] = {"type": "ephemeral"}
108
+ cumulative_tokens = 0 # reset for next block
109
+
110
+ aligned_messages.append(turn_copy)
111
+
112
+ return aligned_messages
113
+
114
+ # Global Radix Prefix Tree instance
115
+ radix_tree = RadixPrefixTree()