echocache 1.0.0__tar.gz

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,82 @@
1
+ Metadata-Version: 2.4
2
+ Name: echocache
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for EchoCache — Dynamic Semantic Cache for LLM APIs
5
+ Home-page: https://echocache.dev
6
+ Author: EchoCache Team
7
+ Author-email: echocache322@gmail.com
8
+ Project-URL: Source, https://github.com/echo-cache/echocache-python
9
+ Project-URL: Tracker, https://github.com/echo-cache/echocache-python/issues
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
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 :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.7
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: requests>=2.25.0
23
+ Dynamic: author
24
+ Dynamic: author-email
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: project-url
30
+ Dynamic: requires-dist
31
+ Dynamic: requires-python
32
+ Dynamic: summary
33
+
34
+ # echocache
35
+
36
+ Official Python SDK for [EchoCache](https://echocache.dev) — The Enterprise Dynamic Semantic Cache for LLM APIs.
37
+
38
+ Reduce LLM API costs by **up to 80%** and accelerate response times from **~1.8s down to <25ms** using vector similarity lookups.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install echocache
44
+ ```
45
+
46
+ ## Quickstart
47
+
48
+ ```python
49
+ import os
50
+ from echocache import EchoCache
51
+
52
+ # 1. Initialize EchoCache client
53
+ echo = EchoCache(os.environ["ECHOCACHE_API_KEY"])
54
+
55
+ # 2. Wrap your LLM generator call
56
+ def generate_llm_response(prompt: str) -> str:
57
+ # Example using OpenAI Python SDK
58
+ response = openai.chat.completions.create(
59
+ model="gpt-5.6-sol",
60
+ messages=[{"role": "user", "content": prompt}]
61
+ )
62
+ return response.choices[0].message.content
63
+
64
+ # 3. Intercept request (returns cached answer on HIT, or calls generator on MISS)
65
+ prompt = "What is the capital of France?"
66
+ answer = echo.ask(prompt, generate_llm_response)
67
+
68
+ print(answer)
69
+ ```
70
+
71
+ ## Configuration Options
72
+
73
+ ```python
74
+ echo = EchoCache(
75
+ "ec_prod_your_api_key_here",
76
+ {"baseUrl": "https://echocache.dev"} # Optional: Custom self-hosted EchoCache endpoint
77
+ )
78
+ ```
79
+
80
+ ## License
81
+
82
+ MIT © EchoCache Team
@@ -0,0 +1,49 @@
1
+ # echocache
2
+
3
+ Official Python SDK for [EchoCache](https://echocache.dev) — The Enterprise Dynamic Semantic Cache for LLM APIs.
4
+
5
+ Reduce LLM API costs by **up to 80%** and accelerate response times from **~1.8s down to <25ms** using vector similarity lookups.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install echocache
11
+ ```
12
+
13
+ ## Quickstart
14
+
15
+ ```python
16
+ import os
17
+ from echocache import EchoCache
18
+
19
+ # 1. Initialize EchoCache client
20
+ echo = EchoCache(os.environ["ECHOCACHE_API_KEY"])
21
+
22
+ # 2. Wrap your LLM generator call
23
+ def generate_llm_response(prompt: str) -> str:
24
+ # Example using OpenAI Python SDK
25
+ response = openai.chat.completions.create(
26
+ model="gpt-5.6-sol",
27
+ messages=[{"role": "user", "content": prompt}]
28
+ )
29
+ return response.choices[0].message.content
30
+
31
+ # 3. Intercept request (returns cached answer on HIT, or calls generator on MISS)
32
+ prompt = "What is the capital of France?"
33
+ answer = echo.ask(prompt, generate_llm_response)
34
+
35
+ print(answer)
36
+ ```
37
+
38
+ ## Configuration Options
39
+
40
+ ```python
41
+ echo = EchoCache(
42
+ "ec_prod_your_api_key_here",
43
+ {"baseUrl": "https://echocache.dev"} # Optional: Custom self-hosted EchoCache endpoint
44
+ )
45
+ ```
46
+
47
+ ## License
48
+
49
+ MIT © EchoCache Team
@@ -0,0 +1,3 @@
1
+ from .client import EchoCache
2
+
3
+ __all__ = ["EchoCache"]
@@ -0,0 +1,49 @@
1
+ import sys
2
+ import requests
3
+
4
+ def check_cache(prompt: str, api_key: str, base_url: str, project_id: str = "proj_default") -> dict:
5
+ """
6
+ Queries the EchoCache backend for a matching cached answer.
7
+ """
8
+ response = requests.post(
9
+ f"{base_url}/api/cache",
10
+ headers={
11
+ "Content-Type": "application/json",
12
+ "Authorization": f"Bearer {api_key}",
13
+ "x-project-id": project_id
14
+ },
15
+ json={"prompt": prompt, "project_id": project_id},
16
+ timeout=5
17
+ )
18
+ if response.status_code != 200:
19
+ raise RuntimeError(f"Cache read failed (HTTP {response.status_code})")
20
+ return response.json()
21
+
22
+ def save_to_cache(prompt: str, llm_response: str, latency_ms: int, api_key: str, base_url: str, project_id: str = "proj_default") -> None:
23
+ """
24
+ Sends the generated LLM response to EchoCache to be saved for future hits.
25
+ """
26
+ try:
27
+ # Rough token estimation: ~4 characters per token
28
+ tokens_saved = (len(prompt) // 4) + (len(llm_response) // 4)
29
+ response = requests.post(
30
+ f"{base_url}/api/cache/save",
31
+ headers={
32
+ "Content-Type": "application/json",
33
+ "Authorization": f"Bearer {api_key}",
34
+ "x-project-id": project_id
35
+ },
36
+ json={
37
+ "prompt": prompt,
38
+ "llm_response": llm_response,
39
+ "status": "miss",
40
+ "latency_ms": latency_ms,
41
+ "tokens_saved": tokens_saved,
42
+ "project_id": project_id
43
+ },
44
+ timeout=10
45
+ )
46
+ if response.status_code != 200:
47
+ print(f"[EchoCache] Background sync returned HTTP {response.status_code}", file=sys.stderr)
48
+ except Exception as e:
49
+ print(f"[EchoCache] Background sync failed: {e}", file=sys.stderr)
@@ -0,0 +1,109 @@
1
+ import os
2
+ import time
3
+ import threading
4
+ from typing import Callable, Optional, Union
5
+
6
+ from .connection import parse_connection_string
7
+ from .validate import validate_key
8
+ from .cache import check_cache, save_to_cache
9
+
10
+ class EchoCache:
11
+ def __init__(self, target: Optional[Union[str, dict]] = None, options: Optional[dict] = None):
12
+ api_key = None
13
+ base_url = None
14
+ project_id = "proj_default"
15
+
16
+ if isinstance(target, str):
17
+ if target.startswith("echocache://") or target.startswith("echocaches://") or "@" in target:
18
+ parsed = parse_connection_string(target)
19
+ api_key = parsed["apiKey"]
20
+ base_url = parsed["baseUrl"]
21
+ project_id = parsed["projectId"]
22
+ elif target.startswith("ec_prod_"):
23
+ api_key = target
24
+ options = options or {}
25
+ base_url = options.get("baseUrl") or os.getenv("ECHOCACHE_BASE_URL")
26
+ project_id = options.get("projectId") or "proj_default"
27
+ if not base_url:
28
+ raise ValueError(
29
+ "Missing EchoCache baseUrl. Please pass a full connection string 'echocache://<api_key>@<host>/<project_id>' or set os.environ['ECHOCACHE_CONNECTION_STRING']."
30
+ )
31
+ else:
32
+ raise ValueError(
33
+ f"Invalid EchoCache connection argument: '{target}'. Expected connection string format: 'echocache://ec_prod_xxx@host/proj_default'"
34
+ )
35
+ elif isinstance(target, dict):
36
+ api_key = target.get("apiKey")
37
+ base_url = target.get("baseUrl") or os.getenv("ECHOCACHE_BASE_URL")
38
+ project_id = target.get("projectId") or "proj_default"
39
+ else:
40
+ env_conn = os.getenv("ECHOCACHE_CONNECTION_STRING")
41
+ if env_conn:
42
+ parsed = parse_connection_string(env_conn)
43
+ api_key = parsed["apiKey"]
44
+ base_url = parsed["baseUrl"]
45
+ project_id = parsed["projectId"]
46
+ else:
47
+ raise ValueError(
48
+ "Missing EchoCache connection string. Please pass an explicit connection string 'echocache://<api_key>@<host>/<project_id>' or set os.environ['ECHOCACHE_CONNECTION_STRING']."
49
+ )
50
+
51
+ if not api_key or not api_key.startswith("ec_prod_"):
52
+ raise ValueError("Invalid EchoCache API Key. Key must start with 'ec_prod_'")
53
+
54
+ self.api_key = api_key
55
+ self.base_url = base_url.rstrip("/")
56
+ self.project_id = project_id
57
+ self.is_validated = False
58
+
59
+ # Validation runs in a background thread to prevent constructor blocking
60
+ self._init_thread = threading.Thread(target=self._init, daemon=True)
61
+ self._init_thread.start()
62
+
63
+ def _init(self):
64
+ self.is_validated = validate_key(self.api_key, self.base_url)
65
+ if self.is_validated:
66
+ print("[EchoCache] Successfully connected to Semantic Cache.")
67
+
68
+ def ask(self, prompt: str, fallback_llm_call: Callable[[str], str]) -> str:
69
+ # Wait for the handshake thread to finish before proceeding queries
70
+ self._init_thread.join()
71
+
72
+ if not self.is_validated:
73
+ # Bypass cache checks if validation failed
74
+ return fallback_llm_call(prompt)
75
+
76
+ start_time = time.time()
77
+
78
+ try:
79
+ # 1. Check cache
80
+ cache_data = check_cache(prompt, self.api_key, self.base_url, self.project_id)
81
+
82
+ # 2. Cache HIT
83
+ if cache_data.get("cache") == "HIT":
84
+ latency_ms = int((time.time() - start_time) * 1000)
85
+ score = cache_data.get("score", "Exact")
86
+ print(f"[EchoCache] HIT! ({latency_ms}ms) - Score: {score}")
87
+ return cache_data.get("response")
88
+
89
+ # 3. Cache MISS
90
+ print("[EchoCache] MISS. Calling LLM...")
91
+ # Execute the dev's actual LLM generation
92
+ llm_start = time.time()
93
+ llm_response = fallback_llm_call(prompt)
94
+ miss_latency_ms = int((time.time() - llm_start) * 1000)
95
+
96
+ # 4. Background Sync (runs in background daemon thread)
97
+ sync_thread = threading.Thread(
98
+ target=save_to_cache,
99
+ args=(prompt, llm_response, miss_latency_ms, self.api_key, self.base_url, self.project_id),
100
+ daemon=True
101
+ )
102
+ sync_thread.start()
103
+
104
+ return llm_response
105
+
106
+ except Exception as e:
107
+ # Fallback to LLM on caching server failure
108
+ print(f"[EchoCache] Fallback triggered. Reason: {e}")
109
+ return fallback_llm_call(prompt)
@@ -0,0 +1,40 @@
1
+ import re
2
+ from typing import Dict
3
+
4
+ def parse_connection_string(connection_string: str) -> Dict[str, str]:
5
+ """
6
+ Parses an EchoCache proprietary connection string.
7
+ Format: echocache://<api_key>@<host>/<project_id>
8
+ Example: echocache://ec_prod_d880430bbc14e293b8716f472c17e867d90f995f29dcc50e@localhost:3000/proj_default
9
+ """
10
+ if not connection_string or not isinstance(connection_string, str):
11
+ raise ValueError(
12
+ "Missing EchoCache connection string. You must explicitly provide a connection string "
13
+ "(e.g. 'echocache://ec_prod_xxx@localhost:3000/proj_default') or set os.environ['ECHOCACHE_CONNECTION_STRING']."
14
+ )
15
+
16
+ s = connection_string.strip()
17
+
18
+ pattern = r"^(?:echocaches?|https?):\/\/(ec_prod_[^@]+)@([^/]+)(?:\/(.+))?$"
19
+ match = re.match(pattern, s)
20
+
21
+ if not match:
22
+ raise ValueError(
23
+ f"Invalid EchoCache connection string format: \"{connection_string}\". "
24
+ f"Expected format: echocache://<api_key>@<host>/<project_id> "
25
+ f"(e.g. 'echocache://ec_prod_d880430b...@localhost:3000/proj_default')"
26
+ )
27
+
28
+ api_key, host, raw_project_id = match.groups()
29
+ project_id = raw_project_id.strip("/") if raw_project_id else "proj_default"
30
+
31
+ is_local = host.startswith("localhost") or host.startswith("127.0.0.1") or host.startswith("0.0.0.0")
32
+ protocol = "http" if s.startswith("http://") or is_local else "https"
33
+ base_url = f"{protocol}://{host}"
34
+
35
+ return {
36
+ "apiKey": api_key,
37
+ "baseUrl": base_url,
38
+ "projectId": project_id,
39
+ "connectionString": f"echocache://{api_key}@{host}/{project_id}",
40
+ }
@@ -0,0 +1,21 @@
1
+ import sys
2
+ import requests
3
+
4
+ def validate_key(api_key: str, base_url: str) -> bool:
5
+ """
6
+ Validates the EchoCache API key against the server validate route.
7
+ Returns True if valid, False otherwise.
8
+ """
9
+ try:
10
+ response = requests.post(
11
+ f"{base_url}/api/validate",
12
+ headers={"Authorization": f"Bearer {api_key}"},
13
+ timeout=5
14
+ )
15
+ if response.status_code != 200:
16
+ print("❌ [EchoCache] Invalid or inactive API Key.", file=sys.stderr)
17
+ return False
18
+ return True
19
+ except Exception as e:
20
+ print(f"⚠️ [EchoCache] Could not reach validation server: {e}", file=sys.stderr)
21
+ return False
@@ -0,0 +1,82 @@
1
+ Metadata-Version: 2.4
2
+ Name: echocache
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for EchoCache — Dynamic Semantic Cache for LLM APIs
5
+ Home-page: https://echocache.dev
6
+ Author: EchoCache Team
7
+ Author-email: echocache322@gmail.com
8
+ Project-URL: Source, https://github.com/echo-cache/echocache-python
9
+ Project-URL: Tracker, https://github.com/echo-cache/echocache-python/issues
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
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 :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.7
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: requests>=2.25.0
23
+ Dynamic: author
24
+ Dynamic: author-email
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: project-url
30
+ Dynamic: requires-dist
31
+ Dynamic: requires-python
32
+ Dynamic: summary
33
+
34
+ # echocache
35
+
36
+ Official Python SDK for [EchoCache](https://echocache.dev) — The Enterprise Dynamic Semantic Cache for LLM APIs.
37
+
38
+ Reduce LLM API costs by **up to 80%** and accelerate response times from **~1.8s down to <25ms** using vector similarity lookups.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install echocache
44
+ ```
45
+
46
+ ## Quickstart
47
+
48
+ ```python
49
+ import os
50
+ from echocache import EchoCache
51
+
52
+ # 1. Initialize EchoCache client
53
+ echo = EchoCache(os.environ["ECHOCACHE_API_KEY"])
54
+
55
+ # 2. Wrap your LLM generator call
56
+ def generate_llm_response(prompt: str) -> str:
57
+ # Example using OpenAI Python SDK
58
+ response = openai.chat.completions.create(
59
+ model="gpt-5.6-sol",
60
+ messages=[{"role": "user", "content": prompt}]
61
+ )
62
+ return response.choices[0].message.content
63
+
64
+ # 3. Intercept request (returns cached answer on HIT, or calls generator on MISS)
65
+ prompt = "What is the capital of France?"
66
+ answer = echo.ask(prompt, generate_llm_response)
67
+
68
+ print(answer)
69
+ ```
70
+
71
+ ## Configuration Options
72
+
73
+ ```python
74
+ echo = EchoCache(
75
+ "ec_prod_your_api_key_here",
76
+ {"baseUrl": "https://echocache.dev"} # Optional: Custom self-hosted EchoCache endpoint
77
+ )
78
+ ```
79
+
80
+ ## License
81
+
82
+ MIT © EchoCache Team
@@ -0,0 +1,12 @@
1
+ README.md
2
+ setup.py
3
+ echocache/__init__.py
4
+ echocache/cache.py
5
+ echocache/client.py
6
+ echocache/connection.py
7
+ echocache/validate.py
8
+ echocache.egg-info/PKG-INFO
9
+ echocache.egg-info/SOURCES.txt
10
+ echocache.egg-info/dependency_links.txt
11
+ echocache.egg-info/requires.txt
12
+ echocache.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.25.0
@@ -0,0 +1 @@
1
+ echocache
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,38 @@
1
+ import os
2
+ from setuptools import setup, find_packages
3
+
4
+ this_directory = os.path.abspath(os.path.dirname(__file__))
5
+ with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f:
6
+ long_description = f.read()
7
+
8
+ setup(
9
+ name="echocache",
10
+ version="1.0.0",
11
+ description="Official Python SDK for EchoCache — Dynamic Semantic Cache for LLM APIs",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ author="EchoCache Team",
15
+ author_email="echocache322@gmail.com",
16
+ url="https://echocache.dev",
17
+ project_urls={
18
+ "Source": "https://github.com/echo-cache/echocache-python",
19
+ "Tracker": "https://github.com/echo-cache/echocache-python/issues",
20
+ },
21
+ packages=find_packages(),
22
+ install_requires=[
23
+ "requests>=2.25.0",
24
+ ],
25
+ python_requires=">=3.7",
26
+ classifiers=[
27
+ "Development Status :: 5 - Production/Stable",
28
+ "Intended Audience :: Developers",
29
+ "License :: OSI Approved :: MIT License",
30
+ "Programming Language :: Python :: 3",
31
+ "Programming Language :: Python :: 3.8",
32
+ "Programming Language :: Python :: 3.9",
33
+ "Programming Language :: Python :: 3.10",
34
+ "Programming Language :: Python :: 3.11",
35
+ "Programming Language :: Python :: 3.12",
36
+ "Topic :: Software Development :: Libraries :: Python Modules",
37
+ ],
38
+ )