Skip to content

Commit 6e89ec2

Browse files
realpython-botmartin-martinclaudebzaczynski
authored
Update ChromaDB tutorial materials for ChromaDB 1.5 and openai 3.x (#831)
* Update ChromaDB tutorial materials for ChromaDB 1.5 and openai 3.x - Pin all dependencies to the tested versions (Python 3.12+) - Use configuration={"hnsw": ...} instead of metadata hnsw:space - Fix off-by-one that dropped the last document of every batch and read the batch size from client.get_max_batch_size() - Use itertools.batched() instead of more-itertools - Load the collection from its persisted embedding function config - Polars: schema_overrides= and maintain_order=True - Move the OpenAI code to the OpenAI() client and Responses API, with the API key loaded from .env via python-dotenv - Use the spaCy large model, since the 3.8 medium model prunes vectors Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Fix Final QA findings in the ChromaDB update materials - chroma_utils.py: import batched from itertools, not more_itertools. more-itertools is not in requirements.txt, so the script raised ImportError for anyone following the README. The tutorial uses the stdlib version, which the PR description already claimed. - car_data_etl.py: rename Vehicle_Model to Vehicle_Make. Index 1 of a title like "2017 Volvo XC90 ..." is the make, and the tutorial documents the metadata key as Vehicle_Make, so the collection built from this script had a different key than the article shows. - README.md: use the house (.venv) prompts and pin the spaCy model to en_core_web_lg-3.8.0, matching the tutorial's install commands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Martin Breuss <breuss.martin@gmail.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Bartosz Zaczyński <bartosz@realpython.com> Co-authored-by: Bartosz Zaczyński <bartosz.zaczynski@gmail.com>
1 parent e6c3416 commit 6e89ec2

9 files changed

Lines changed: 64 additions & 179 deletions

File tree

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
# Embeddings and Vector Databases With ChromaDB
1+
# ChromaDB: Embeddings and Vector Databases in Python
22

3-
Supporting code for the Real Python tutorial [Embeddings and Vector Databases With ChromaDB](https://realpython.com/chromadb-vector-database/).
3+
Supporting code for the Real Python tutorial [ChromaDB: Embeddings and Vector Databases in Python](https://realpython.com/chromadb-vector-database/).
44

5-
To run the code in this tutorial, you should have `numpy`, `spacy`, `sentence-transformers`, `chromadb`, `polars`, `more-itertools`, and `openai` installed in your environment.
5+
The code was tested with Python 3.14 and the pinned versions in `requirements.txt`. You need Python 3.12 or later.
66

7-
You can install the dependencies manually, or by running:
7+
You can install the dependencies by running:
88

99
```
10-
(venv) $ python -m pip install -r requirements.txt
10+
(.venv) $ python -m pip install -r requirements.txt
11+
(.venv) $ python -m spacy download en_core_web_lg-3.8.0 --direct
12+
```
13+
14+
To run the LLM examples, store your OpenAI API key in a `.env` file in this directory:
15+
16+
```
17+
OPENAI_API_KEY="<your-api-key>"
1118
```

‎embeddings-and-vector-databases-with-chromadb/car_data_etl.py‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ def prepare_car_reviews_data(
2020
}
2121

2222
# Scan the car reviews dataset(s)
23-
car_reviews = pl.scan_csv(data_path, dtypes=dtypes)
23+
car_reviews = pl.scan_csv(data_path, schema_overrides=dtypes)
2424

25-
# Extract the vehicle title and year as new columns
25+
# Extract the vehicle year and make as new columns
2626
# Filter on selected years
2727
car_review_db_data = (
2828
car_reviews.with_columns(
@@ -34,7 +34,7 @@ def prepare_car_reviews_data(
3434
.cast(pl.Int64)
3535
).alias("Vehicle_Year"),
3636
(pl.col("Vehicle_Title").str.split(by=" ").list.get(1)).alias(
37-
"Vehicle_Model"
37+
"Vehicle_Make"
3838
),
3939
]
4040
)
@@ -45,10 +45,10 @@ def prepare_car_reviews_data(
4545
"Review",
4646
"Rating",
4747
"Vehicle_Year",
48-
"Vehicle_Model",
48+
"Vehicle_Make",
4949
]
5050
)
51-
.sort(["Vehicle_Model", "Rating"])
51+
.sort(["Vehicle_Make", "Rating"], maintain_order=True)
5252
.collect()
5353
)
5454

‎embeddings-and-vector-databases-with-chromadb/chroma_utils.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import pathlib
2+
from itertools import batched
23

34
import chromadb
45
from chromadb.utils import embedding_functions
5-
from more_itertools import batched
66

77

88
def build_chroma_collection(
@@ -25,14 +25,15 @@ def build_chroma_collection(
2525
collection = chroma_client.create_collection(
2626
name=collection_name,
2727
embedding_function=embedding_func,
28-
metadata={"hnsw:space": distance_func_name},
28+
configuration={"hnsw": {"space": distance_func_name}},
2929
)
3030

31+
batch_size = chroma_client.get_max_batch_size()
3132
document_indices = list(range(len(documents)))
3233

33-
for batch in batched(document_indices, 166):
34+
for batch in batched(document_indices, batch_size):
3435
start_idx = batch[0]
35-
end_idx = batch[-1]
36+
end_idx = batch[-1] + 1
3637

3738
collection.add(
3839
ids=ids[start_idx:end_idx],

‎embeddings-and-vector-databases-with-chromadb/config.json‎

Lines changed: 0 additions & 3 deletions
This file was deleted.

‎embeddings-and-vector-databases-with-chromadb/create_car_review_collection.py‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import chromadb
22
from car_data_etl import prepare_car_reviews_data
33
from chroma_utils import build_chroma_collection
4-
from chromadb.utils import embedding_functions
54

65
DATA_PATH = "data/archive/*"
76
CHROMA_PATH = "car_review_embeddings"
@@ -20,12 +19,9 @@
2019
)
2120

2221
client = chromadb.PersistentClient(CHROMA_PATH)
23-
embedding_func = embedding_functions.SentenceTransformerEmbeddingFunction(
24-
model_name=EMBEDDING_FUNC_NAME
25-
)
26-
collection = client.get_collection(
27-
name=COLLECTION_NAME, embedding_function=embedding_func
28-
)
22+
collection = client.get_collection(name=COLLECTION_NAME)
23+
24+
print(collection.count())
2925

3026
great_reviews = collection.query(
3127
query_texts=[
Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
1-
import json
21
import os
32

43
import chromadb
5-
import openai
6-
from chromadb.utils import embedding_functions
4+
from dotenv import load_dotenv
5+
from openai import OpenAI
76

87
os.environ["TOKENIZERS_PARALLELISM"] = "false"
98

10-
DATA_PATH = "data/archive/*"
119
CHROMA_PATH = "car_review_embeddings"
12-
EMBEDDING_FUNC_NAME = "multi-qa-MiniLM-L6-cos-v1"
1310
COLLECTION_NAME = "car_reviews"
11+
MODEL = "gpt-5.6-luna"
1412

15-
with open("config.json", "r") as json_file:
16-
config_data = json.load(json_file)
13+
load_dotenv()
1714

18-
openai.api_key = config_data.get("openai-secret-key")
19-
20-
client = chromadb.PersistentClient(CHROMA_PATH)
21-
embedding_func = embedding_functions.SentenceTransformerEmbeddingFunction(
22-
model_name=EMBEDDING_FUNC_NAME
23-
)
24-
25-
collection = client.get_collection(
26-
name=COLLECTION_NAME, embedding_function=embedding_func
27-
)
15+
openai_client = OpenAI()
16+
chroma_client = chromadb.PersistentClient(CHROMA_PATH)
17+
collection = chroma_client.get_collection(name=COLLECTION_NAME)
2818

2919
context = """
30-
You are a customer success employee at a large
31-
car dealership. Use the following car reviews
32-
to answer questions: {}
33-
"""
20+
You are a customer success employee at a large
21+
car dealership. Use the following car reviews
22+
to answer questions: {}
23+
"""
3424

3525
question = """
36-
What's the key to great customer satisfaction
37-
based on detailed positive reviews?
38-
"""
26+
What's the key to great customer satisfaction
27+
based on detailed positive reviews?
28+
"""
3929

4030
good_reviews = collection.query(
4131
query_texts=[question],
@@ -46,45 +36,25 @@
4636

4737
reviews_str = ",".join(good_reviews["documents"][0])
4838

49-
good_review_summaries = openai.ChatCompletion.create(
50-
model="gpt-3.5-turbo",
51-
messages=[
52-
{"role": "system", "content": context.format(reviews_str)},
53-
{"role": "user", "content": question},
54-
],
55-
temperature=0,
56-
n=1,
57-
)
58-
59-
reviews_str = ",".join(good_reviews["documents"][0])
60-
6139
print("Good reviews: ")
6240
print(reviews_str)
6341
print("###########################################")
6442

65-
good_review_summaries = openai.ChatCompletion.create(
66-
model="gpt-3.5-turbo",
67-
messages=[
68-
{"role": "system", "content": context.format(reviews_str)},
69-
{"role": "user", "content": question},
70-
],
71-
temperature=0,
72-
n=1,
43+
good_review_summaries = openai_client.responses.create(
44+
model=MODEL,
45+
instructions=context.format(reviews_str),
46+
input=question,
7347
)
7448

7549
print("AI-Generated summary of good reviews: ")
76-
print(good_review_summaries["choices"][0]["message"]["content"])
50+
print(good_review_summaries.output_text)
7751
print("###########################################")
7852

79-
80-
context = """
81-
You are a customer success employee at a large car dealership.
82-
Use the following car reivews to answer questions: {}
83-
"""
8453
question = """
85-
Which of these poor reviews has the worst implications about
86-
our dealership? Explain why.
87-
"""
54+
Which of these poor reviews has the
55+
worst implications about our dealership?
56+
Explain why.
57+
"""
8858

8959
poor_reviews = collection.query(
9060
query_texts=[question],
@@ -99,16 +69,12 @@
9969
print(poor_reviews["documents"][0][0])
10070
print("###########################################")
10171

102-
poor_review_analysis = openai.ChatCompletion.create(
103-
model="gpt-3.5-turbo",
104-
messages=[
105-
{"role": "system", "content": context.format(reviews_str)},
106-
{"role": "user", "content": question},
107-
],
108-
temperature=0,
109-
n=1,
72+
poor_review_analysis = openai_client.responses.create(
73+
model=MODEL,
74+
instructions=context.format(reviews_str),
75+
input=question,
11076
)
11177

11278
print("AI-Generated summary of the single worst review: ")
113-
print(poor_review_analysis["choices"][0]["message"]["content"])
79+
print(poor_review_analysis.output_text)
11480
print("###########################################")
Lines changed: 7 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,7 @@
1-
aiohttp==3.8.6
2-
aiosignal==1.3.1
3-
annotated-types==0.6.0
4-
anyio==3.7.1
5-
async-timeout==4.0.3
6-
attrs==23.1.0
7-
backoff==2.2.1
8-
bcrypt==4.0.1
9-
blis==0.7.11
10-
catalogue==2.0.10
11-
certifi==2023.7.22
12-
charset-normalizer==3.3.0
13-
chroma-hnswlib==0.7.3
14-
chromadb==0.4.14
15-
click==8.1.7
16-
cloudpathlib==0.16.0
17-
coloredlogs==15.0.1
18-
confection==0.1.3
19-
cymem==2.0.8
20-
fastapi==0.104.0
21-
filelock==3.12.4
22-
flatbuffers==23.5.26
23-
frozenlist==1.4.0
24-
fsspec==2023.9.2
25-
grpcio==1.59.0
26-
h11==0.14.0
27-
httptools==0.6.1
28-
huggingface-hub==0.17.3
29-
humanfriendly==10.0
30-
idna==3.4
31-
importlib-resources==6.1.0
32-
Jinja2==3.1.2
33-
joblib==1.3.2
34-
langcodes==3.3.0
35-
MarkupSafe==2.1.3
36-
monotonic==1.6
37-
more-itertools==10.1.0
38-
mpmath==1.3.0
39-
multidict==6.0.4
40-
murmurhash==1.0.10
41-
networkx==3.2
42-
nltk==3.8.1
43-
numpy==1.26.1
44-
onnxruntime==1.16.1
45-
openai==0.28.1
46-
overrides==7.4.0
47-
packaging==23.2
48-
Pillow==10.1.0
49-
polars==0.19.9
50-
posthog==3.0.2
51-
preshed==3.0.9
52-
protobuf==4.24.4
53-
pulsar-client==3.3.0
54-
pydantic==2.4.2
55-
pydantic_core==2.10.1
56-
PyPika==0.48.9
57-
python-dateutil==2.8.2
58-
python-dotenv==1.0.0
59-
PyYAML==6.0.1
60-
regex==2023.10.3
61-
requests==2.31.0
62-
safetensors==0.4.0
63-
scikit-learn==1.3.1
64-
scipy==1.11.3
65-
sentence-transformers==2.2.2
66-
sentencepiece==0.1.99
67-
six==1.16.0
68-
smart-open==6.4.0
69-
sniffio==1.3.0
70-
spacy==3.7.2
71-
spacy-legacy==3.0.12
72-
spacy-loggers==1.0.5
73-
srsly==2.4.8
74-
starlette==0.27.0
75-
sympy==1.12
76-
thinc==8.2.1
77-
threadpoolctl==3.2.0
78-
tokenizers==0.14.1
79-
torch==2.1.0
80-
torchvision==0.16.0
81-
tqdm==4.66.1
82-
transformers==4.34.1
83-
typer==0.9.0
84-
typing_extensions==4.8.0
85-
urllib3==2.0.7
86-
uvicorn==0.23.2
87-
uvloop==0.18.0
88-
wasabi==1.1.2
89-
watchfiles==0.21.0
90-
weasel==0.3.3
91-
websockets==11.0.3
92-
yarl==1.9.2
1+
chromadb==1.5.9
2+
numpy==2.5.3
3+
openai==3.14.0
4+
polars==1.44.2
5+
python-dotenv==1.2.3
6+
sentence-transformers==6.0.1
7+
spacy==3.8.16

‎embeddings-and-vector-databases-with-chromadb/word_vectors.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import spacy
44

55
# Load the medium-size English model
6-
nlp = spacy.load("en_core_web_md")
6+
nlp = spacy.load("en_core_web_lg")
77

88
# Get the word vector for the word "dog"
99
dog_embedding = nlp.vocab["dog"].vector

‎uv.lock‎

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)