A python client for Pinecone. For more information, see the docs at https://www.pinecone.io/docs/
Pinecone Client V3 is based on pre-compiled Rust code and gRPC, aimed at improving performance and stability.
Using native gRPC transport, client V3 is able to achieve a 2x-3x speedup for vector upsert over the previous Rest-based client versions, as well as a 10-20% speedup for vector query latency.
As the client installation is fully self-contained, it does not require any additional dependencies (e.g. grpcio
), making it easier to install and use in any python environment.
⚠️ WarningThis is a public preview ("Beta") version. Please test it thoroughly before using it in production.
Some functionalities are not backwards compatible with the previous versions of the client, please refer to the cahangelog for more details.
Install public preview version from pip:
pip3 install pinecone-client==3.0.0rc2
See CONTRIBUTING.md
If you are migrating from pinecone client V2, here is the most minimal code change required to upgrade to V3:
# Pinecone client V2
import pinecone
pinecone.init() # One time init
...
index = pinecone.Index("example-index")
index.upsert(...)
# Pinecone client V3
from pinecone import Client
pinecone = Client() # This is now a `Client` instance
...
# Unchanged!
index = pinecone.Index("example-index")
index.upsert(...)
(For more API changes see the CHANGELOG.md)
The Client
is the main entry point for control operations like creating, deleting and configuring pinecone indexes.
Initializing a Client
requires your Pinecone API key and a region, which can be passed as either environment variables or as parameters to the Client
constructor.
import os
from pinecone import Client
# Initialize a client using environment variables
os.environ['PINECONE_API_KEY'] = 'YOUR_API_KEY'
os.environ['PINECONE_REGION'] = 'us-west1-gcp'
client = Client()
# Initialize a client using parameters
client = Client(api_key = 'YOUR_API_KEY', region = 'us-west1-gcp')
The following example creates an index without a metadata configuration. By default, Pinecone indexes all metadata.
from pinecone import Client
client = Client(api_key="YOUR_API_KEY", region="us-west1-gcp")
client.create_index("example-index", dimension=1024)
The following example creates an index that only indexes the "color" metadata field. Queries against this index cannot filter based on any other metadata field.
metadata_config = {
"indexed": ["color"]
}
client.create_index("example-index-2", dimension=1024,
metadata_config=metadata_config)
The following example returns all indexes in your project.
active_indexes = client.list_indexes()
The following example returns information about the index example-index
.
index_description = client.describe_index("example-index")
The following example deletes example-index
.
client.delete_index("example-index")
The following example changes the number of replicas for example-index
.
new_number_of_replicas = 4
client.scale_index("example-index", replicas=new_number_of_replicas)
The index object is the entry point for data operations like upserting, querying and deleting vectors.
from pinecone import Client
client = Client(api_key="YOUR_API_KEY", region="us-west1-gcp")
index = client.get_index("example-index")
# Backwards compatibility
index = client.Index("example-index")
The following example returns statistics about the index example-index
.
from pinecone import Client
client = Client(api_key="YOUR_API_KEY", region="us-west1-gcp")
index = client.Index("example-index")
print(index.describe_index_stats())
The following example upserts vectors to example-index
.
from pinecone import Client, Vector, SparseValues
client = Client(api_key="YOUR_API_KEY", region="us-west1-gcp")
index = client.get_index("example-index")
upsert_response = index.upsert(
vectors=[
("vec1", [0.1, 0.2, 0.3, 0.4], {"genre": "drama"}),
("vec2", [0.2, 0.3, 0.4, 0.5], {"genre": "action"}),
],
namespace="example-namespace"
)
# Mixing different vector representations is allowed
upsert_response = index.upsert(
vectors=[
# Tuples
("vec1", [0.1, 0.2, 0.3, 0.4]),
("vec2", [0.2, 0.3, 0.4, 0.5], {"genre": "action"}),
# Vector objects
Vector(id='id1', values=[1.0, 2.0, 3.0], metadata={'key': 'value'}),
Vector(id='id3', values=[1.0, 2.0, 3.0], sparse_values=SparseValues(indices=[1, 2], values=[0.2, 0.4])),
# Dictionaries
{'id': 'id1', 'values': [1.0, 2.0, 3.0], 'metadata': {'key': 'value'}},
{'id': 'id2', 'values': [1.0, 2.0, 3.0], 'sparse_values': {'indices': [1, 2], 'values': [0.2, 0.4]}},
],
namespace="example-namespace"
)
The following example queries the index example-index
with metadata
filtering.
query_response = index.query(
values=[1.0, 5.3, 8.9, 0.5], # values of a query vector
sparse_values = None, # optional sparse values of the query vector
top_k=10,
namespace="example-namespace",
include_values=True,
include_metadata=True,
filter={
"genre": {"$in": ["comedy", "documentary", "drama"]}
}
)
The following example queries the index example-index
for the top_k=10
nearest neighbors of the vector with ID vec1
.
query_response = index.query(
id="vec1",
top_k=10,
namespace="example-namespace",
include_values=True,
include_metadata=True,
)
# Delete vectors by IDs
index.delete(ids=["vec1", "vec2"], namespace="example-namespace")
# Delete vectors by metadata filters
index.delete(filter={"genre": {"$in": ["comedy", "documentary", "drama"]}}, namespace="example-namespace")
# Delete all vectors in a given namespace (use namespace="" to delete all vectors in the DEFAULT namespace)
index.delete_all(namespace="example-namespace")
The following example fetches vectors by ID without querying for nearest neighbors.
fetch_response = index.fetch(ids=["vec1", "vec2"], namespace="example-namespace")
The following example updates vectors by ID.
update_response = index.update(
id="vec1",
values=[0.1, 0.2, 0.3, 0.4],
set_metadata={"genre": "drama"},
namespace="example-namespace"
)
To upsert an entire dataset of vectors, we recommend using concurrent batched upsert requests. The following example shows how to do this using the asyncio
library:
import asyncio
from pinecone import Client, Vector
def chunker(seq, batch_size):
return (seq[pos:pos + batch_size] for pos in range(0, len(seq), batch_size))
async def async_upload(index, vectors, batch_size, max_concurrent=50):
sem = asyncio.Semaphore(max_concurrent)
async def send_batch(batch):
async with sem:
return await index.upsert(vectors=batch, async_req=True)
await asyncio.gather(*[send_batch(chunk) for chunk in chunker(vectors, batch_size=batch_size)])
# To use it:
client = Client()
index = client.get_index("example-index")
asyncio.run(async_upload(index, vectors, batch_size=100))
# In a jypter notebook, asyncio.run() is not supported. Instead, use
await async_upload(index, vectors, batch_size=100)
TODO: Decide if we want to suggest this longer, more verbose version, which includes a progress bar and return type:
from tqdm.asyncio import tqdm
import asyncio
from pinecone import UpsertResponse, Client, Vector
def chunker(seq, batch_size):
return (seq[pos:pos + batch_size] for pos in range(0, len(seq), batch_size))
async def async_upload(index, vectors, batch_size, max_concurrent=50):
sem = asyncio.Semaphore(max_concurrent)
async def send_batch(batch):
async with sem:
return await index.upsert(vectors=batch, async_req=True)
tasks = [send_batch(chunk) for chunk in chunker(vectors, batch_size=batch_size)]
pbar = tqdm(total=len(vectors), desc="upserted vectors")
total_upserted_count = 0
for task in asyncio.as_completed(tasks):
res = await task
total_upserted_count += res.upserted_count
pbar.update(res.upserted_count)
return UpsertResponse(upserted_count=total_upserted_count)
# To use it:
client = Client()
index = client.get_index("example-index")
vectors = [Vector(id=f"vec{i}", values=[1.0, 2.0, 3.0]) for i in range(1000)]
res = asyncio.run(async_upload(index, vectors, batch_size=100))
# In a jypter notebook, asyncio.run() is not supported. Instead, use
res = await async_upload(index, vectors, batch_size=100)
Due to limitations with the underlying pyo3
library, code completion and type hints are not available in some IDEs, or might require additional configuration.
- Jupyter notebooks: Should work out of the box
- VSCode: Change the
languageServer
setting tojedi
- PyCharm: For the moment, all fucntion signatures would show
(*args, **kwargs)
. We are working on a solution ASAP. (Function docstrings would still show full arguments and type hints)