flashlite 0.1.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.
- flashlite/__init__.py +169 -0
- flashlite/cache/__init__.py +14 -0
- flashlite/cache/base.py +194 -0
- flashlite/cache/disk.py +285 -0
- flashlite/cache/memory.py +157 -0
- flashlite/client.py +671 -0
- flashlite/config.py +154 -0
- flashlite/conversation/__init__.py +30 -0
- flashlite/conversation/context.py +319 -0
- flashlite/conversation/manager.py +385 -0
- flashlite/conversation/multi_agent.py +378 -0
- flashlite/core/__init__.py +13 -0
- flashlite/core/completion.py +145 -0
- flashlite/core/messages.py +130 -0
- flashlite/middleware/__init__.py +18 -0
- flashlite/middleware/base.py +90 -0
- flashlite/middleware/cache.py +121 -0
- flashlite/middleware/logging.py +159 -0
- flashlite/middleware/rate_limit.py +211 -0
- flashlite/middleware/retry.py +149 -0
- flashlite/observability/__init__.py +34 -0
- flashlite/observability/callbacks.py +155 -0
- flashlite/observability/inspect_compat.py +266 -0
- flashlite/observability/logging.py +293 -0
- flashlite/observability/metrics.py +221 -0
- flashlite/py.typed +0 -0
- flashlite/structured/__init__.py +31 -0
- flashlite/structured/outputs.py +189 -0
- flashlite/structured/schema.py +165 -0
- flashlite/templating/__init__.py +11 -0
- flashlite/templating/engine.py +217 -0
- flashlite/templating/filters.py +143 -0
- flashlite/templating/registry.py +165 -0
- flashlite/tools/__init__.py +74 -0
- flashlite/tools/definitions.py +382 -0
- flashlite/tools/execution.py +353 -0
- flashlite/types.py +233 -0
- flashlite-0.1.0.dist-info/METADATA +173 -0
- flashlite-0.1.0.dist-info/RECORD +41 -0
- flashlite-0.1.0.dist-info/WHEEL +4 -0
- flashlite-0.1.0.dist-info/licenses/LICENSE.md +21 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""In-memory LRU cache with TTL support."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from ..types import CompletionResponse
|
|
9
|
+
from .base import CacheBackend
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class MemoryCacheEntry:
|
|
14
|
+
"""An entry in the memory cache."""
|
|
15
|
+
|
|
16
|
+
response: CompletionResponse
|
|
17
|
+
created_at: float
|
|
18
|
+
ttl: float | None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MemoryCache(CacheBackend):
|
|
22
|
+
"""
|
|
23
|
+
In-memory LRU cache with optional TTL.
|
|
24
|
+
|
|
25
|
+
This cache uses an OrderedDict to maintain LRU ordering.
|
|
26
|
+
When the cache exceeds max_size, the least recently used
|
|
27
|
+
entries are evicted.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
cache = MemoryCache(max_size=1000, default_ttl=3600)
|
|
31
|
+
|
|
32
|
+
# Store a response
|
|
33
|
+
await cache.set(key, response)
|
|
34
|
+
|
|
35
|
+
# Retrieve (returns None if expired or not found)
|
|
36
|
+
cached = await cache.get(key)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
max_size: int = 1000,
|
|
42
|
+
default_ttl: float | None = None,
|
|
43
|
+
):
|
|
44
|
+
"""
|
|
45
|
+
Initialize the memory cache.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
max_size: Maximum number of entries to store
|
|
49
|
+
default_ttl: Default time-to-live in seconds (None = no expiration)
|
|
50
|
+
"""
|
|
51
|
+
self._max_size = max_size
|
|
52
|
+
self._default_ttl = default_ttl
|
|
53
|
+
self._cache: OrderedDict[str, MemoryCacheEntry] = OrderedDict()
|
|
54
|
+
self._lock = asyncio.Lock()
|
|
55
|
+
self._hits = 0
|
|
56
|
+
self._misses = 0
|
|
57
|
+
|
|
58
|
+
async def get(self, key: str) -> CompletionResponse | None:
|
|
59
|
+
"""Retrieve a cached response."""
|
|
60
|
+
async with self._lock:
|
|
61
|
+
entry = self._cache.get(key)
|
|
62
|
+
|
|
63
|
+
if entry is None:
|
|
64
|
+
self._misses += 1
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
# Check expiration
|
|
68
|
+
if entry.ttl is not None:
|
|
69
|
+
if time.time() > entry.created_at + entry.ttl:
|
|
70
|
+
# Expired - remove and return None
|
|
71
|
+
del self._cache[key]
|
|
72
|
+
self._misses += 1
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
# Move to end (most recently used)
|
|
76
|
+
self._cache.move_to_end(key)
|
|
77
|
+
self._hits += 1
|
|
78
|
+
return entry.response
|
|
79
|
+
|
|
80
|
+
async def set(
|
|
81
|
+
self,
|
|
82
|
+
key: str,
|
|
83
|
+
response: CompletionResponse,
|
|
84
|
+
ttl: float | None = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Store a response in the cache."""
|
|
87
|
+
async with self._lock:
|
|
88
|
+
# Use provided TTL or default
|
|
89
|
+
effective_ttl = ttl if ttl is not None else self._default_ttl
|
|
90
|
+
|
|
91
|
+
entry = MemoryCacheEntry(
|
|
92
|
+
response=response,
|
|
93
|
+
created_at=time.time(),
|
|
94
|
+
ttl=effective_ttl,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# If key exists, update and move to end
|
|
98
|
+
if key in self._cache:
|
|
99
|
+
self._cache[key] = entry
|
|
100
|
+
self._cache.move_to_end(key)
|
|
101
|
+
else:
|
|
102
|
+
# Add new entry
|
|
103
|
+
self._cache[key] = entry
|
|
104
|
+
|
|
105
|
+
# Evict LRU entries if over capacity
|
|
106
|
+
while len(self._cache) > self._max_size:
|
|
107
|
+
self._cache.popitem(last=False)
|
|
108
|
+
|
|
109
|
+
async def delete(self, key: str) -> bool:
|
|
110
|
+
"""Delete a cached entry."""
|
|
111
|
+
async with self._lock:
|
|
112
|
+
if key in self._cache:
|
|
113
|
+
del self._cache[key]
|
|
114
|
+
return True
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
async def clear(self) -> int:
|
|
118
|
+
"""Clear all cached entries."""
|
|
119
|
+
async with self._lock:
|
|
120
|
+
count = len(self._cache)
|
|
121
|
+
self._cache.clear()
|
|
122
|
+
self._hits = 0
|
|
123
|
+
self._misses = 0
|
|
124
|
+
return count
|
|
125
|
+
|
|
126
|
+
async def size(self) -> int:
|
|
127
|
+
"""Get the number of cached entries."""
|
|
128
|
+
async with self._lock:
|
|
129
|
+
return len(self._cache)
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def hits(self) -> int:
|
|
133
|
+
"""Number of cache hits."""
|
|
134
|
+
return self._hits
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def misses(self) -> int:
|
|
138
|
+
"""Number of cache misses."""
|
|
139
|
+
return self._misses
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def hit_rate(self) -> float:
|
|
143
|
+
"""Cache hit rate (0.0 to 1.0)."""
|
|
144
|
+
total = self._hits + self._misses
|
|
145
|
+
if total == 0:
|
|
146
|
+
return 0.0
|
|
147
|
+
return self._hits / total
|
|
148
|
+
|
|
149
|
+
def stats(self) -> dict[str, int | float]:
|
|
150
|
+
"""Get cache statistics."""
|
|
151
|
+
return {
|
|
152
|
+
"size": len(self._cache),
|
|
153
|
+
"max_size": self._max_size,
|
|
154
|
+
"hits": self._hits,
|
|
155
|
+
"misses": self._misses,
|
|
156
|
+
"hit_rate": self.hit_rate,
|
|
157
|
+
}
|