hypercache-kv 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.
@@ -0,0 +1,442 @@
1
+ """
2
+ hypercache.workflows — drop-in patterns for the three gains.
3
+
4
+ Each function is a thin orchestration of Hyper Cache HTTP calls. No codec
5
+ algorithm runs on the customer's machine; everything proprietary stays in
6
+ the WASM binary on Cloudflare. Customers import these helpers to get the
7
+ common patterns without writing the plumbing.
8
+
9
+ Patterns:
10
+ Pipeline(name) — ALL-IN: cache + chain + stats in one context
11
+ cached_completion(prompt, compute) — skip repeated LLM calls
12
+ audit_chain() — prove what happened, step by step
13
+ wrap_openai(client) — drop-in cached OpenAI client
14
+ wrap_anthropic(client) — drop-in cached Anthropic client
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import time
20
+ from contextlib import contextmanager
21
+ from dataclasses import dataclass, field
22
+ from typing import Any, Callable, Iterator, Optional
23
+
24
+ from . import Client, Session
25
+
26
+
27
+ # ---------- Pattern 0: Pipeline — ALL-IN template ---------- #
28
+
29
+ @dataclass
30
+ class PipelineStep:
31
+ """One entry in a pipeline's chain. Records what happened at each step."""
32
+
33
+ label: str
34
+ fingerprint_hex: str
35
+ was_cache_hit: Optional[bool] = None # None = pure record, no compute
36
+ elapsed_ms: float = 0.0
37
+ bytes_processed: int = 0
38
+
39
+
40
+ @dataclass
41
+ class PipelineReport:
42
+ """End-of-pipeline summary. Useful for dashboards and audit export."""
43
+
44
+ name: str
45
+ steps: list[PipelineStep] = field(default_factory=list)
46
+ started_at: float = 0.0
47
+ ended_at: float = 0.0
48
+
49
+ @property
50
+ def n_steps(self) -> int:
51
+ return len(self.steps)
52
+
53
+ @property
54
+ def n_hits(self) -> int:
55
+ return sum(1 for s in self.steps if s.was_cache_hit is True)
56
+
57
+ @property
58
+ def n_misses(self) -> int:
59
+ return sum(1 for s in self.steps if s.was_cache_hit is False)
60
+
61
+ @property
62
+ def total_seconds(self) -> float:
63
+ return max(0.0, self.ended_at - self.started_at)
64
+
65
+ @property
66
+ def chain(self) -> list[tuple[str, str]]:
67
+ """The verifiable audit chain: ordered (label, fingerprint_hex) pairs."""
68
+ return [(s.label, s.fingerprint_hex) for s in s_iter(self.steps)]
69
+
70
+ def export_audit(self) -> list[dict]:
71
+ """JSON-serializable chain export for compliance/auditor handoff."""
72
+ return [
73
+ {
74
+ "label": s.label,
75
+ "fingerprint_hex": s.fingerprint_hex,
76
+ "cache_hit": s.was_cache_hit,
77
+ "elapsed_ms": round(s.elapsed_ms, 2),
78
+ "bytes": s.bytes_processed,
79
+ }
80
+ for s in self.steps
81
+ ]
82
+
83
+
84
+ def s_iter(steps): # tiny helper so older Pythons don't trip on walrus elsewhere
85
+ yield from steps
86
+
87
+
88
+ class Pipeline:
89
+ """Single template that delivers all three gains in one context.
90
+
91
+ Inside a Pipeline you get:
92
+ - ``cached(label, input_bytes, compute)`` — cache an expensive call (skip)
93
+ - ``record(label, bytes)`` — record any step (prove)
94
+ - ``report`` (after exit) — stats + verifiable chain
95
+
96
+ Same inputs across runs → cache hits → faster, cheaper. Every step's
97
+ fingerprint chains to the prior one → cryptographic lineage. The
98
+ ``PipelineReport`` returned at exit holds the chain you'd hand an
99
+ auditor and the stats you'd show a dashboard.
100
+
101
+ Example:
102
+ from hypercache.workflows import Pipeline
103
+ from openai import OpenAI
104
+ openai = OpenAI()
105
+
106
+ with Pipeline("translate_user_message") as p:
107
+ p.record("input", user_msg.encode("utf-8"))
108
+
109
+ translation, was_hit = p.cached(
110
+ label="gpt_translate",
111
+ input_bytes=f"Translate to French: {user_msg}".encode("utf-8"),
112
+ compute=lambda: openai.chat.completions.create(
113
+ model="gpt-4o-mini",
114
+ messages=[{"role":"user","content":f"Translate to French: {user_msg}"}]
115
+ ).choices[0].message.content
116
+ )
117
+
118
+ p.record("output", translation.encode("utf-8"))
119
+
120
+ # After the block:
121
+ print(f"{p.report.n_hits} hits, {p.report.n_misses} misses, "
122
+ f"chain length {p.report.n_steps}")
123
+ audit_doc = p.report.export_audit() # hand to compliance
124
+ """
125
+
126
+ def __init__(self, name: str, client: Optional[Client] = None):
127
+ self.name = name
128
+ self._client = client or Client()
129
+ self._session = Session(client=self._client)
130
+ self.report: PipelineReport = PipelineReport(name=name)
131
+
132
+ def __enter__(self) -> "Pipeline":
133
+ self.report.started_at = time.perf_counter()
134
+ return self
135
+
136
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
137
+ self.report.ended_at = time.perf_counter()
138
+
139
+ # ---- record: pure chain entry (no caching, just lineage) ----
140
+
141
+ def record(self, label: str, data: Any) -> PipelineStep:
142
+ """Add a step to the chain. Used for inputs, outputs, and intermediate
143
+ states you want in the audit record but aren't trying to cache."""
144
+ t0 = time.perf_counter()
145
+ result = self._session.fingerprint(data)
146
+ elapsed = (time.perf_counter() - t0) * 1000
147
+ size = len(data) if isinstance(data, (bytes, bytearray, memoryview)) else 0
148
+ step = PipelineStep(
149
+ label=label,
150
+ fingerprint_hex=result.record_hex,
151
+ was_cache_hit=None,
152
+ elapsed_ms=elapsed,
153
+ bytes_processed=size,
154
+ )
155
+ self.report.steps.append(step)
156
+ return step
157
+
158
+ # ---- cached: skip work on repeated inputs ----
159
+
160
+ def cached(
161
+ self,
162
+ label: str,
163
+ input_bytes: bytes | str,
164
+ compute: Callable[[], Any],
165
+ *,
166
+ ttl: int = 86400,
167
+ ) -> tuple[Any, bool]:
168
+ """Cache an expensive computation by its input bytes.
169
+
170
+ Same input bytes next time → cached result, no compute call. New input
171
+ → calls ``compute()``, stores its return value (as bytes via str/repr)
172
+ keyed by the input's fingerprint.
173
+
174
+ Returns:
175
+ (result, was_cache_hit)
176
+ """
177
+ t0 = time.perf_counter()
178
+ if isinstance(input_bytes, str):
179
+ input_bytes = input_bytes.encode("utf-8")
180
+
181
+ lookup = self._client.cache_lookup(input_bytes)
182
+ # Advance the chain even on hit so lineage doesn't skip cached steps.
183
+ # We do this by replaying the lookup's fingerprint into the session.
184
+ self._session._prev = bytes.fromhex(lookup.fingerprint_hex)
185
+
186
+ if lookup.hit and lookup.value is not None:
187
+ elapsed = (time.perf_counter() - t0) * 1000
188
+ cached_text = lookup.value.decode("utf-8", errors="replace")
189
+ step = PipelineStep(
190
+ label=label,
191
+ fingerprint_hex=lookup.fingerprint_hex,
192
+ was_cache_hit=True,
193
+ elapsed_ms=elapsed,
194
+ bytes_processed=len(input_bytes),
195
+ )
196
+ self.report.steps.append(step)
197
+ return cached_text, True
198
+
199
+ # Miss — compute and store.
200
+ result = compute()
201
+ result_bytes = (
202
+ result.encode("utf-8") if isinstance(result, str)
203
+ else result if isinstance(result, (bytes, bytearray))
204
+ else str(result).encode("utf-8")
205
+ )
206
+ self._client.cache_put(lookup.fingerprint_hex, result_bytes, ttl=ttl)
207
+ elapsed = (time.perf_counter() - t0) * 1000
208
+ step = PipelineStep(
209
+ label=label,
210
+ fingerprint_hex=lookup.fingerprint_hex,
211
+ was_cache_hit=False,
212
+ elapsed_ms=elapsed,
213
+ bytes_processed=len(input_bytes),
214
+ )
215
+ self.report.steps.append(step)
216
+ return result, False
217
+
218
+
219
+ # ---------- Pattern 1: cached_completion ---------- #
220
+
221
+ def cached_completion(
222
+ prompt: str,
223
+ compute: Callable[[str], str],
224
+ *,
225
+ ttl: int = 86400,
226
+ client: Optional[Client] = None,
227
+ ) -> tuple[str, bool]:
228
+ """Run an LLM call with transparent caching.
229
+
230
+ Same prompt in → cached response back. New prompt → calls ``compute(prompt)``,
231
+ stores the response, returns it.
232
+
233
+ Args:
234
+ prompt: the input string sent to your LLM.
235
+ compute: a function that takes the prompt and returns the LLM response.
236
+ Called only on cache miss.
237
+ ttl: seconds to keep the cached response (default 24h).
238
+ client: optional Client; constructed from HYPERCACHE_KEY env var if omitted.
239
+
240
+ Returns:
241
+ (response_text, was_cache_hit)
242
+
243
+ Example:
244
+ from openai import OpenAI
245
+ from hypercache.workflows import cached_completion
246
+
247
+ openai_client = OpenAI()
248
+
249
+ def call_gpt(prompt: str) -> str:
250
+ return openai_client.chat.completions.create(
251
+ model="gpt-4o-mini",
252
+ messages=[{"role": "user", "content": prompt}],
253
+ ).choices[0].message.content
254
+
255
+ text, was_hit = cached_completion("Translate hello to French", call_gpt)
256
+ """
257
+ if client is None:
258
+ client = Client()
259
+ prompt_bytes = prompt.encode("utf-8")
260
+ lookup = client.cache_lookup(prompt_bytes)
261
+ if lookup.hit and lookup.value is not None:
262
+ return lookup.value.decode("utf-8"), True
263
+
264
+ response = compute(prompt)
265
+ client.cache_put(lookup.fingerprint_hex, response.encode("utf-8"), ttl=ttl)
266
+ return response, False
267
+
268
+
269
+ # ---------- Pattern 2: audit_chain ---------- #
270
+
271
+ @contextmanager
272
+ def audit_chain(client: Optional[Client] = None) -> Iterator[Session]:
273
+ """Open a chain-aware session that records every fingerprinted step with
274
+ automatic ``prev`` linkage.
275
+
276
+ Use as a context manager so each AI computation gets its own lineage.
277
+
278
+ Example:
279
+ from hypercache.workflows import audit_chain
280
+
281
+ with audit_chain() as chain:
282
+ r1 = chain.fingerprint(input_bytes) # step 1
283
+ r2 = chain.fingerprint(model_output) # step 2, linked to r1
284
+ r3 = chain.fingerprint(reviewer_note) # step 3, linked to r2
285
+ chain_records = [r1.record_hex, r2.record_hex, r3.record_hex]
286
+
287
+ # Hand chain_records + the original input bytes to an auditor.
288
+ # They re-run the fingerprints via the same API and verify the chain.
289
+ """
290
+ sess = Session(client=client)
291
+ try:
292
+ yield sess
293
+ finally:
294
+ # Nothing to clean up — chain records are already on the wire.
295
+ pass
296
+
297
+
298
+ # ---------- Pattern 3: provider wrappers ---------- #
299
+
300
+ def wrap_openai(openai_client: Any, *, ttl: int = 86400, client: Optional[Client] = None) -> Any:
301
+ """Return a wrapper around an OpenAI client whose ``chat.completions.create``
302
+ method caches by request body. Same request body → cached response.
303
+
304
+ The wrapper exposes the same interface as the OpenAI client for completions;
305
+ other methods are passed through unchanged.
306
+
307
+ Example:
308
+ from openai import OpenAI
309
+ from hypercache.workflows import wrap_openai
310
+
311
+ client = wrap_openai(OpenAI())
312
+ resp = client.chat.completions.create(
313
+ model="gpt-4o-mini",
314
+ messages=[{"role": "user", "content": "Hello"}],
315
+ )
316
+ """
317
+ return _CachedOpenAI(openai_client, ttl=ttl, hc_client=client or Client())
318
+
319
+
320
+ def wrap_anthropic(anthropic_client: Any, *, ttl: int = 86400, client: Optional[Client] = None) -> Any:
321
+ """Same as wrap_openai but for the Anthropic SDK's ``messages.create`` method."""
322
+ return _CachedAnthropic(anthropic_client, ttl=ttl, hc_client=client or Client())
323
+
324
+
325
+ # ---------- Internal implementations ---------- #
326
+
327
+ import json
328
+
329
+
330
+ class _CachedOpenAI:
331
+ def __init__(self, openai_client: Any, *, ttl: int, hc_client: Client):
332
+ self._openai = openai_client
333
+ self._ttl = ttl
334
+ self._hc = hc_client
335
+ self.chat = _CachedOpenAIChat(self)
336
+
337
+ def __getattr__(self, name: str) -> Any:
338
+ # Pass-through for everything we don't wrap.
339
+ return getattr(self._openai, name)
340
+
341
+
342
+ class _CachedOpenAIChat:
343
+ def __init__(self, parent: _CachedOpenAI):
344
+ self._parent = parent
345
+ self.completions = _CachedOpenAICompletions(parent)
346
+
347
+ def __getattr__(self, name: str) -> Any:
348
+ return getattr(self._parent._openai.chat, name)
349
+
350
+
351
+ class _CachedOpenAICompletions:
352
+ def __init__(self, parent: _CachedOpenAI):
353
+ self._parent = parent
354
+
355
+ def create(self, **kwargs: Any) -> Any:
356
+ # Streaming requests bypass caching (we'd need to buffer the stream).
357
+ if kwargs.get("stream"):
358
+ return self._parent._openai.chat.completions.create(**kwargs)
359
+
360
+ # Build a stable cache key from the request body.
361
+ # Sort keys so semantically-equal requests produce the same fingerprint.
362
+ body = json.dumps(kwargs, sort_keys=True, default=str).encode("utf-8")
363
+ lookup = self._parent._hc.cache_lookup(body)
364
+ if lookup.hit and lookup.value is not None:
365
+ # Cached — return the deserialized completion.
366
+ cached_json = json.loads(lookup.value.decode("utf-8"))
367
+ return _DictResponse(cached_json)
368
+
369
+ # Miss — call OpenAI, cache the response.
370
+ resp = self._parent._openai.chat.completions.create(**kwargs)
371
+ # Serialize using model_dump if available (Pydantic v2), else dict().
372
+ if hasattr(resp, "model_dump"):
373
+ resp_json = resp.model_dump()
374
+ elif hasattr(resp, "dict"):
375
+ resp_json = resp.dict()
376
+ else:
377
+ resp_json = dict(resp)
378
+ self._parent._hc.cache_put(
379
+ lookup.fingerprint_hex,
380
+ json.dumps(resp_json).encode("utf-8"),
381
+ ttl=self._parent._ttl,
382
+ )
383
+ return resp
384
+
385
+
386
+ class _CachedAnthropic:
387
+ def __init__(self, anthropic_client: Any, *, ttl: int, hc_client: Client):
388
+ self._anthropic = anthropic_client
389
+ self._ttl = ttl
390
+ self._hc = hc_client
391
+ self.messages = _CachedAnthropicMessages(self)
392
+
393
+ def __getattr__(self, name: str) -> Any:
394
+ return getattr(self._anthropic, name)
395
+
396
+
397
+ class _CachedAnthropicMessages:
398
+ def __init__(self, parent: _CachedAnthropic):
399
+ self._parent = parent
400
+
401
+ def create(self, **kwargs: Any) -> Any:
402
+ if kwargs.get("stream"):
403
+ return self._parent._anthropic.messages.create(**kwargs)
404
+ body = json.dumps(kwargs, sort_keys=True, default=str).encode("utf-8")
405
+ lookup = self._parent._hc.cache_lookup(body)
406
+ if lookup.hit and lookup.value is not None:
407
+ cached_json = json.loads(lookup.value.decode("utf-8"))
408
+ return _DictResponse(cached_json)
409
+ resp = self._parent._anthropic.messages.create(**kwargs)
410
+ if hasattr(resp, "model_dump"):
411
+ resp_json = resp.model_dump()
412
+ elif hasattr(resp, "dict"):
413
+ resp_json = resp.dict()
414
+ else:
415
+ resp_json = dict(resp)
416
+ self._parent._hc.cache_put(
417
+ lookup.fingerprint_hex,
418
+ json.dumps(resp_json).encode("utf-8"),
419
+ ttl=self._parent._ttl,
420
+ )
421
+ return resp
422
+
423
+
424
+ class _DictResponse:
425
+ """Light wrapper around a dict so cached responses access fields the same
426
+ way Pydantic objects do (response.choices[0].message.content style)."""
427
+
428
+ def __init__(self, data: dict):
429
+ self._data = data
430
+ for k, v in data.items():
431
+ if isinstance(v, dict):
432
+ setattr(self, k, _DictResponse(v))
433
+ elif isinstance(v, list):
434
+ setattr(self, k, [_DictResponse(i) if isinstance(i, dict) else i for i in v])
435
+ else:
436
+ setattr(self, k, v)
437
+
438
+ def __getitem__(self, key: str) -> Any:
439
+ return self._data[key]
440
+
441
+ def __repr__(self) -> str:
442
+ return f"_DictResponse({self._data!r})"
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: hypercache-kv
3
+ Version: 0.1.0
4
+ Summary: Skip repeated LLM calls, skip GPU prefill, prove what happened — a thin client for the Hyper Cache API.
5
+ Author-email: Hyper Cache <contact@hypercache.ai>
6
+ License: MIT
7
+ Project-URL: homepage, https://hypercache.ai
8
+ Project-URL: documentation, https://hypercache.ai/docs
9
+ Project-URL: repository, https://github.com/Hyper-Cache/hypercache-kv
10
+ Project-URL: issues, https://github.com/Hyper-Cache/hypercache-kv/issues
11
+ Keywords: ai,ml,llm,cache,fingerprint,audit,inference
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Provides-Extra: numpy
24
+ Requires-Dist: numpy>=1.20; extra == "numpy"
25
+ Provides-Extra: torch
26
+ Requires-Dist: torch>=2.0; extra == "torch"
27
+ Provides-Extra: test
28
+ Requires-Dist: pytest>=7; extra == "test"
29
+ Requires-Dist: numpy>=1.20; extra == "test"
30
+ Dynamic: license-file
31
+
32
+ # hypercache (Python SDK)
33
+
34
+ Thin client for the [Hyper Cache](https://hypercache.ai) API. Zero runtime dependencies (stdlib only).
35
+
36
+ Hyper Cache is one thing on Cloudflare: a small fast server-locked codec that gives any input a tamper-evident 90-byte ID, plus a content-addressed cache and chain. **This SDK is a thin HTTP wrapper** — the codec algorithm runs only inside our WASM binary on Cloudflare, never on your machine.
37
+
38
+ ## Three gains, one primitive
39
+
40
+ ```python
41
+ from hypercache.workflows import Pipeline
42
+
43
+ with Pipeline("my_pipeline") as p:
44
+ # Skip repeated LLM calls — same input next time, cached response back
45
+ answer, was_hit = p.cached(
46
+ label="gpt_call",
47
+ input_bytes=prompt.encode("utf-8"),
48
+ compute=lambda: call_openai(prompt),
49
+ )
50
+
51
+ # Prove what happened — every step gets a verifiable fingerprint
52
+ p.record("output", answer.encode("utf-8"))
53
+
54
+ print(f"{p.report.n_hits} hits / {p.report.n_misses} misses")
55
+ audit_chain = p.report.export_audit()
56
+ ```
57
+
58
+ One template, three gains: skip repeated work (cache), prove what happened (chain), stats for your dashboard (report).
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install hypercache-kv
64
+ export HYPERCACHE_KEY=hck_...
65
+ ```
66
+
67
+ Get a key at [hypercache.ai](https://hypercache.ai).
68
+
69
+ ## The three gains in detail
70
+
71
+ **1. Skip repeated LLM calls.** Same prompt → cached response in milliseconds. Measured 7.6× faster on cache hit against real Phi-3-mini calls.
72
+
73
+ ```python
74
+ from hypercache.workflows import cached_completion
75
+
76
+ text, was_hit = cached_completion(
77
+ prompt="Translate to French: Hello",
78
+ compute=lambda p: call_openai(p),
79
+ )
80
+ ```
81
+
82
+ Or wrap your OpenAI / Anthropic client directly:
83
+
84
+ ```python
85
+ from openai import OpenAI
86
+ from hypercache.workflows import wrap_openai
87
+
88
+ client = wrap_openai(OpenAI())
89
+ resp = client.chat.completions.create(model="gpt-4o-mini", messages=[...])
90
+ ```
91
+
92
+ **2. Skip repeated GPU prefill.** For self-hosted inference (vLLM, llama-cpp, SGLang, TRT-LLM) with reused system prompts or RAG contexts. Measured 21.8× faster than cold prefill on Phi-3-mini at 1199 tokens. See [docs/02_skip_gpu_prefill.md](../../docs/02_skip_gpu_prefill.md).
93
+
94
+ **3. Prove what happened.** Every fingerprint chains algebraically to the prior one. The chain is mathematically verifiable, server-locked against forgery, and exportable for compliance.
95
+
96
+ ```python
97
+ from hypercache.workflows import audit_chain
98
+
99
+ with audit_chain() as chain:
100
+ r1 = chain.fingerprint(input_bytes)
101
+ r2 = chain.fingerprint(model_output)
102
+ r3 = chain.fingerprint(reviewer_note)
103
+ ```
104
+
105
+ ## Low-level API
106
+
107
+ ```python
108
+ import hypercache
109
+
110
+ result = hypercache.cache_lookup(b"some input bytes")
111
+ if result.hit:
112
+ print(result.value)
113
+ else:
114
+ hypercache.cache_put(result.fingerprint_hex, b"my expensive output", ttl=3600)
115
+
116
+ results = hypercache.cache_lookup_batch([b"in 1", b"in 2", b"in 3"])
117
+
118
+ fp = hypercache.fingerprint(b"any bytes")
119
+ print(fp.record_hex)
120
+ ```
121
+
122
+ ## What's open / what's closed
123
+
124
+ - **Open (this SDK):** thin HTTP wrappers + workflow templates. MIT licensed.
125
+ - **Closed:** the codec algorithm itself runs only inside our WASM binary on Cloudflare. You interact with it via HTTP.
126
+
127
+ The separation is intentional: the codec's mathematical integrity (forgery resistance for audit, byte-precision for caching) requires that the algorithm cannot be replicated or modified by anyone, including customers. This SDK contains zero codec code; PRs that try to add codec algorithm code locally will be closed.
128
+
129
+ ## License
130
+
131
+ MIT. See [LICENSE](./LICENSE).
@@ -0,0 +1,7 @@
1
+ hypercache/__init__.py,sha256=UrE8lFsJMOcaiCjnU9Ucz2PTaWDP_oex0LT7u7Nl3C0,47537
2
+ hypercache/workflows.py,sha256=lm9CA-ah_7bSJgC4--1r4w823qaI6gO7DTVNTw-_U6M,15705
3
+ hypercache_kv-0.1.0.dist-info/licenses/LICENSE,sha256=4-b3MyIQADfs_PwJavwUwJVikh4GYlsi2CEIi4Dx9bs,1408
4
+ hypercache_kv-0.1.0.dist-info/METADATA,sha256=HK8SjasbQfYvWos67BhZQqXhm49FCgSbYTbIyy9Lt8w,4793
5
+ hypercache_kv-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ hypercache_kv-0.1.0.dist-info/top_level.txt,sha256=kMHlTCg6YTKrFOgpc_ZP79S4Ibby0XXiLwnyzeFzavs,11
7
+ hypercache_kv-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hyper Cache
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ Note: this license applies to the SDK source code in this repository only.
24
+ The Hyper Cache codec algorithm — which runs only inside Cloudflare Workers
25
+ as compiled WebAssembly and is accessed via HTTP — is proprietary, patent-
26
+ pending, and not licensed under MIT. This SDK is a thin HTTP wrapper and
27
+ contains zero codec algorithm code.
@@ -0,0 +1 @@
1
+ hypercache