Skip to content

Commit ddf6e94

Browse files
committed
Initial commit
0 parents  commit ddf6e94

File tree

5 files changed

+366
-0
lines changed

5 files changed

+366
-0
lines changed

.env.EXAMPLE

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
OPENAI_API_KEY=sk-proj-<...>
2+
OPENAI_MODEL=gpt-4o
3+
4+
LANGCACHE_SERVICE_KEY=wy4ECQMIW<...>KKmDb
5+
LANGCACHE_CACHE_ID=eb438<...>
6+
LANGCACHE_BASE_URL=https://gcp-us-east4.langcache.redis.io

.gitignore

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
### Python template
2+
# Byte-compiled / optimized / DLL files
3+
__pycache__/
4+
*.py[cod]
5+
*$py.class
6+
7+
# C extensions
8+
*.so
9+
.env
10+
11+
# Distribution / packaging
12+
.Python
13+
build/
14+
develop-eggs/
15+
dist/
16+
downloads/
17+
eggs/
18+
.eggs/
19+
lib/
20+
lib64/
21+
parts/
22+
sdist/
23+
var/
24+
wheels/
25+
share/python-wheels/
26+
*.egg-info/
27+
.installed.cfg
28+
*.egg
29+
MANIFEST
30+
31+
# PyInstaller
32+
# Usually these files are written by a python script from a template
33+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
34+
*.manifest
35+
*.spec
36+
37+
# Installer logs
38+
pip-log.txt
39+
pip-delete-this-directory.txt
40+
41+
# Unit test / coverage reports
42+
htmlcov/
43+
.tox/
44+
.nox/
45+
.coverage
46+
.coverage.*
47+
.cache
48+
nosetests.xml
49+
coverage.xml
50+
*.cover
51+
*.py,cover
52+
.hypothesis/
53+
.pytest_cache/
54+
cover/
55+
56+
# Translations
57+
*.mo
58+
*.pot
59+
60+
# Django stuff:
61+
*.log
62+
local_settings.py
63+
db.sqlite3
64+
db.sqlite3-journal
65+
66+
# Flask stuff:
67+
instance/
68+
.webassets-cache
69+
70+
# Scrapy stuff:
71+
.scrapy
72+
73+
# Sphinx documentation
74+
docs/_build/
75+
76+
# PyBuilder
77+
.pybuilder/
78+
target/
79+
80+
# Jupyter Notebook
81+
.ipynb_checkpoints
82+
83+
# IPython
84+
profile_default/
85+
ipython_config.py
86+
87+
# pyenv
88+
# For a library or package, you might want to ignore these files since the code is
89+
# intended to run in multiple environments; otherwise, check them in:
90+
# .python-version
91+
92+
# pipenv
93+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
94+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
95+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
96+
# install all needed dependencies.
97+
#Pipfile.lock
98+
99+
# poetry
100+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
101+
# This is especially recommended for binary packages to ensure reproducibility, and is more
102+
# commonly ignored for libraries.
103+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
104+
#poetry.lock
105+
106+
# pdm
107+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
108+
#pdm.lock
109+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
110+
# in version control.
111+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
112+
.pdm.toml
113+
.pdm-python
114+
.pdm-build/
115+
116+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
117+
__pypackages__/
118+
119+
# Celery stuff
120+
celerybeat-schedule
121+
celerybeat.pid
122+
123+
# SageMath parsed files
124+
*.sage.py
125+
126+
# Environments
127+
.env
128+
.venv
129+
env/
130+
venv/
131+
ENV/
132+
env.bak/
133+
venv.bak/
134+
135+
# Spyder project settings
136+
.spyderproject
137+
.spyproject
138+
139+
# Rope project settings
140+
.ropeproject
141+
142+
# mkdocs documentation
143+
/site
144+
145+
# mypy
146+
.mypy_cache/
147+
.dmypy.json
148+
dmypy.json
149+
150+
# Pyre type checker
151+
.pyre/
152+
153+
# pytype static type analyzer
154+
.pytype/
155+
156+
# Cython debug symbols
157+
cython_debug/
158+
159+
# PyCharm
160+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
161+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
162+
# and can be added to the global gitignore or merged into this file. For a more nuclear
163+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
164+
#.idea/
165+

README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Redis LangCache Demo
2+
3+
A minimal Python demo showing how to use [Redis LangCache](https://redis.io/docs/latest/solutions/semantic-caching/langcache/) with OpenAI to implement semantic caching for LLM queries.
4+
This example caches responses based on semantic similarity, reducing latency and API usage costs.
5+
6+
---
7+
8+
## 📂 Project Structure
9+
10+
```
11+
.
12+
├── main.py # Main script for running the demo
13+
├── requirements.txt # Python dependencies
14+
├── .env.EXAMPLE # Example environment variable configuration
15+
└── .env # Your actual environment variables (not committed)
16+
```
17+
18+
---
19+
20+
## 🚀 Prerequisites
21+
22+
- Python **3.10+**
23+
- A Redis LangCache instance (Redis Cloud)
24+
- An OpenAI API key
25+
26+
---
27+
28+
## ⚙️ Setup
29+
30+
1. **Clone this repository**
31+
```bash
32+
git clone https://github.com/<your-repo>/gabs-redis-langcache.git
33+
cd gabs-redis-langcache
34+
```
35+
36+
2. **Create and activate a virtual environment**
37+
```bash
38+
python3 -m venv .venv
39+
source .venv/bin/activate # Mac/Linux
40+
.venv\Scripts\activate # Windows
41+
```
42+
43+
3. **Install dependencies**
44+
```bash
45+
pip install -r requirements.txt
46+
```
47+
48+
4. **Configure environment variables**
49+
- Copy `.env.EXAMPLE` to `.env`
50+
- Fill in your credentials:
51+
```env
52+
OPENAI_API_KEY=sk-proj-<your-openai-key>
53+
OPENAI_MODEL=gpt-4o
54+
55+
LANGCACHE_SERVICE_KEY=<your-langcache-service-key>
56+
LANGCACHE_CACHE_ID=<your-langcache-cache-id>
57+
LANGCACHE_BASE_URL=https://gcp-us-east4.langcache.redis.io
58+
```
59+
60+
---
61+
62+
## ▶️ Usage
63+
64+
Run the demo:
65+
66+
```bash
67+
python main.py
68+
```
69+
70+
Example interaction:
71+
72+
```
73+
LangCache Semantic Cache Chat - Type 'exit' to quit.
74+
75+
Ask something: What is Redis LangCache?
76+
[CACHE MISS]
77+
[Latency] Cache miss search took 0.023 seconds
78+
[Latency] OpenAI response took 0.882 seconds
79+
Response: Redis LangCache is a semantic caching solution...
80+
------------------------------------------------------------
81+
Ask something: Tell me about LangCache
82+
[CACHE HIT]
83+
[Latency] Cache hit in 0.002 seconds
84+
Response: Redis LangCache is a semantic caching solution...
85+
------------------------------------------------------------
86+
```
87+
88+
---
89+
90+
## 🧠 How It Works
91+
92+
1. **Search** in Redis LangCache for a semantically similar question.
93+
2. If a **cache hit** is found (above the similarity threshold), return it instantly.
94+
3. If a **cache miss** occurs:
95+
- Query OpenAI.
96+
- Store the response in Redis LangCache for future reuse.
97+
98+
---
99+
100+
## 📄 License
101+
102+
MIT

main.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import os
2+
import time
3+
from dotenv import load_dotenv
4+
from openai import OpenAI
5+
from langcache import LangCache
6+
7+
# === Load environment variables ===
8+
load_dotenv()
9+
10+
# === LangCache Configuration ===
11+
LANGCACHE_SERVICE_KEY = os.getenv("LANGCACHE_SERVICE_KEY")
12+
LANGCACHE_CACHE_ID = os.getenv("LANGCACHE_CACHE_ID")
13+
LANGCACHE_BASE_URL = os.getenv("LANGCACHE_BASE_URL", "https://gcp-us-east4.langcache.redis.io")
14+
15+
# === OpenAI Configuration ===
16+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
17+
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o")
18+
openai_client = OpenAI(api_key=OPENAI_API_KEY)
19+
20+
21+
def call_openai_llm(prompt: str) -> str:
22+
try:
23+
response = openai_client.chat.completions.create(
24+
model=OPENAI_MODEL,
25+
messages=[{"role": "user", "content": prompt}],
26+
temperature=0.7
27+
)
28+
return response.choices[0].message.content.strip()
29+
except Exception as e:
30+
return f"[ERROR] OpenAI request failed: {e}"
31+
32+
33+
def main():
34+
print("LangCache Semantic Cache Chat - Type 'exit' to quit.\n")
35+
36+
with LangCache(
37+
server_url=LANGCACHE_BASE_URL,
38+
cache_id=LANGCACHE_CACHE_ID,
39+
service_key=LANGCACHE_SERVICE_KEY
40+
) as lang_cache:
41+
42+
while True:
43+
query = input("Ask something: ").strip()
44+
if query.lower() in {"exit", "quit"}:
45+
break
46+
47+
start_time = time.perf_counter()
48+
results = lang_cache.search(prompt=query, similarity_threshold=0.7)
49+
elapsed_time = time.perf_counter() - start_time
50+
51+
if results and results.data:
52+
print("[CACHE HIT]")
53+
print(f"[Latency] Cache hit in {elapsed_time:.3f} seconds")
54+
print("Response:", results.data[0].response)
55+
else:
56+
print("[CACHE MISS]")
57+
print(f"[Latency] Cache miss search took {elapsed_time:.3f} seconds")
58+
59+
start_llm = time.perf_counter()
60+
response = call_openai_llm(query)
61+
lang_cache.set(prompt=query, response=response)
62+
elapsed_llm = time.perf_counter() - start_llm
63+
64+
print(f"[Latency] OpenAI response took {elapsed_llm:.3f} seconds")
65+
print("Response:", response)
66+
67+
print("-" * 60)
68+
69+
70+
if __name__ == "__main__":
71+
main()

requirements.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
annotated-types==0.7.0
2+
anyio==4.9.0
3+
certifi==2025.7.9
4+
charset-normalizer==3.4.2
5+
distro==1.9.0
6+
dotenv==0.9.9
7+
h11==0.16.0
8+
httpcore==1.0.9
9+
httpx==0.28.1
10+
idna==3.10
11+
jiter==0.10.0
12+
langcache==0.8.0
13+
openai==1.95.0
14+
pydantic==2.11.7
15+
pydantic_core==2.33.2
16+
python-dotenv==1.1.1
17+
requests==2.32.4
18+
sniffio==1.3.1
19+
tqdm==4.67.1
20+
typing-inspection==0.4.1
21+
typing_extensions==4.14.1
22+
urllib3==2.5.0

0 commit comments

Comments
 (0)