Skip to content

Commit 2155b15

Browse files
committed
examples(semantic-cache): address review — validate env, pin moss, defensive metadata, clearer session/README notes
- Fail fast with a clear message if MOSS/OPENAI env vars are missing. - Pin moss>=1.1.1 (matches other moss-live-labs examples). - Use metadata.get('answer') defensively on cache hits. - Note the session is in-memory for the run (deterministic MISS->HIT) and document push_index() for cross-run persistence. - Clarify in the README that the shown snippet is simplified vs the runnable ask().
1 parent 4e3b2d6 commit 2155b15

3 files changed

Lines changed: 31 additions & 12 deletions

File tree

moss-live-labs/examples/semantic-cache/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ Repeat questions come back in single-digit-millisecond retrieval, on-device.
2121

2222
## How it works
2323

24-
The whole thing is `SemanticCache` in [`semantic_cache.py`](./semantic_cache.py):
24+
Here's the idea, simplified (the runnable `ask()` in
25+
[`semantic_cache.py`](./semantic_cache.py) also returns whether it was a hit and
26+
prints timing):
2527

2628
```python
2729
async def ask(self, question):

moss-live-labs/examples/semantic-cache/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
description = "Semantic cache for LLM responses, built on Moss"
55
requires-python = ">=3.10"
66
dependencies = [
7-
"moss",
7+
"moss>=1.1.1",
88
"openai>=1.0",
99
"python-dotenv>=1.0",
1010
]

moss-live-labs/examples/semantic-cache/semantic_cache.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
nearest one you've already answered, and if it's close enough, return the stored
77
answer without calling the model.
88
9-
The whole thing is the SemanticCache class below. The one knob that matters is
10-
the similarity threshold.
9+
The core is the SemanticCache class below (the `ask` method is the whole idea).
10+
The one knob that matters is the similarity threshold.
1111
"""
1212

1313
import asyncio
1414
import os
15+
import sys
1516
import time
1617

1718
from dotenv import load_dotenv
@@ -21,19 +22,29 @@
2122

2223
load_dotenv()
2324

25+
26+
def _require(name: str) -> str:
27+
value = os.getenv(name)
28+
if not value:
29+
sys.exit(f"Missing {name}. Copy .env.example to .env and fill in your keys.")
30+
return value
31+
32+
33+
# fail fast with a clear message if credentials are missing
34+
MOSS_PROJECT_ID = _require("MOSS_PROJECT_ID")
35+
MOSS_PROJECT_KEY = _require("MOSS_PROJECT_KEY")
36+
_require("OPENAI_API_KEY") # read by AsyncOpenAI from the environment
37+
2438
# cosine similarity above which a cached answer is "close enough" to reuse.
2539
# too low -> you answer questions people didn't quite ask; too high -> you miss.
2640
THRESHOLD = 0.92
2741

28-
moss = MossClient(
29-
project_id=os.getenv("MOSS_PROJECT_ID"),
30-
project_key=os.getenv("MOSS_PROJECT_KEY"),
31-
)
42+
moss = MossClient(project_id=MOSS_PROJECT_ID, project_key=MOSS_PROJECT_KEY)
3243
llm = AsyncOpenAI()
3344

3445

3546
class SemanticCache:
36-
"""A tiny store of past questions -> answers, looked up by meaning."""
47+
"""A small store of past questions -> answers, looked up by meaning."""
3748

3849
def __init__(self, store):
3950
self.store = store
@@ -42,7 +53,9 @@ async def ask(self, question: str) -> tuple[str, bool]:
4253
# 1. look for the closest question we've already answered
4354
hit = await self.store.query(question, QueryOptions(top_k=1))
4455
if hit.docs and hit.docs[0].score >= THRESHOLD:
45-
return hit.docs[0].metadata["answer"], True # cache hit — no LLM call
56+
answer = hit.docs[0].metadata.get("answer")
57+
if answer is not None:
58+
return answer, True # cache hit — no LLM call
4659

4760
# 2. miss: ask the model once
4861
resp = await llm.chat.completions.create(
@@ -59,8 +72,12 @@ async def ask(self, question: str) -> tuple[str, bool]:
5972

6073

6174
async def main():
62-
# a fresh in-memory session acts as the cache store for this run
63-
store = await moss.session("qa-cache")
75+
# A Moss session is the cache store. It holds the entries in memory for this
76+
# run, so the demo is deterministic: the first question is a MISS and its
77+
# paraphrase is a HIT. To make the cache persist across runs and processes,
78+
# call `await store.push_index()` to upload it as a cloud index (it will then
79+
# auto-load by name on the next run).
80+
store = await moss.session(index_name="qa-cache")
6481
cache = SemanticCache(store)
6582

6683
# the 2nd question means the same as the 1st, phrased differently -> cache hit

0 commit comments

Comments
 (0)