Generating embeddings¶
Pinecone hosts embedding models, so you can turn text into vectors without running
embedding infrastructure of your own. embed()
hands the vectors back to you, to upsert, to query with, or to store somewhere else.
If you would rather never handle a vector, give a document index a field with integrated embedding, and Pinecone embeds on both write and read. See How Pinecone Works. Legacy integrated indexes do the same through the legacy Records API; see Legacy integrated indexes.
Basic usage¶
from pinecone import Pinecone
pc = Pinecone(api_key="your-api-key")
embeddings = pc.inference.embed(
model="multilingual-e5-large",
inputs=["The quick brown fox", "A second piece of text"],
parameters={"input_type": "passage"},
)
for embedding in embeddings:
print(embedding.values[:3])
One embedding comes back per input, in input order. A bare string is wrapped for you, so
inputs="one string" still returns a one-item result rather than a lone embedding.
input_type: the mismatch that costs you relevance¶
Many hosted models are asymmetric: they embed stored text and search queries
differently, and input_type says which side you are on. Embed your corpus as
"passage" and your queries as "query":
passages = pc.inference.embed(
model="multilingual-e5-large",
inputs=["Vector databases index embeddings for similarity search."],
parameters={"input_type": "passage"},
)
query = pc.inference.embed(
model="multilingual-e5-large",
inputs="How does similarity search work?",
parameters={"input_type": "query"},
)
Get this backwards and nothing fails: both calls succeed, both return vectors of the right width, and search quality quietly degrades. Decide per call site which side you are embedding.
Discover what a model accepts¶
parameters is model-specific. Read the keys off the model rather than hardcoding a list
that will go stale:
info = pc.inference.model.get("multilingual-e5-large")
for parameter in info.supported_parameters:
print(parameter.parameter, parameter.allowed_values, parameter.default)
Each entry carries allowed_values when the set is fixed, min/max when the value is
numeric, default for what the model uses when you omit the key, and required for
whether you must send it.
The same ModelInfo reports the model’s limits
and output shape:
print(info.type) # 'embed' or 'rerank'
print(info.vector_type) # 'dense' or 'sparse'
print(info.default_dimension) # output dimension when none is requested
print(info.max_batch_size) # most inputs one call may carry
print(info.max_sequence_length) # longest input accepted
vector_type and default_dimension are None on a reranking model, and
default_dimension is None on a sparse model.
Response: EmbeddingsList¶
embed returns an EmbeddingsList. Iterating it
yields the embeddings, and len() and integer indexing reach the same items:
.data: the same list the iteration walks, one entry per input..vector_type:"dense"or"sparse", and so which fields the entries carry..model: the model that served the request..usage.total_tokens: tokens counted for the call.
Dense and sparse embeddings¶
The two shapes carry the vector in different fields. A
DenseEmbedding has values; a
SparseEmbedding has sparse_values and
sparse_indices paired position by position, and no values field. Branch on
vector_type when the model is not fixed in advance:
first = embeddings.data[0]
if embeddings.vector_type == "sparse":
print(first.sparse_indices, first.sparse_values)
else:
print(first.values[:3])
Reading .values on a sparse embedding does not raise: it returns a dict-view method
instead of a vector, and your code carries on with the wrong object. The vector_type
check is what protects you.
One embed call returns either all-dense or all-sparse results, never a mix. For hybrid
search, call embed once with a dense model and once with a sparse model, and pair the
two result sets yourself.
Using the EmbedModel enum¶
EmbedModel gives you tab-completion and typo safety in
place of a bare string:
from pinecone import EmbedModel, Pinecone
pc = Pinecone(api_key="your-api-key")
embeddings = pc.inference.embed(
model=EmbedModel.Multilingual_E5_Large,
inputs=["search query"],
parameters={"input_type": "query"},
)
embed accepts a plain string too, so a model that has no enum member yet still works.
Batch size¶
Sending several inputs in one call saves round trips, up to the model’s
max_batch_size (see Discover what a model accepts).
The SDK does not split an oversized batch; the server rejects it. Chunk your inputs
yourself:
texts = [f"document number {n}" for n in range(250)]
batch_size = info.max_batch_size
all_embeddings = []
for start in range(0, len(texts), batch_size):
batch = pc.inference.embed(
model="multilingual-e5-large",
inputs=texts[start : start + batch_size],
parameters={"input_type": "passage"},
)
all_embeddings.extend(batch.data)
Overlong individual inputs are a separate concern: where a model supports the truncate
parameter, it decides whether the model trims them or rejects the request.
Storing embeddings in an index¶
Declare a dense_vector field at the model’s output dimension, then upsert each
embedding’s values into it:
from pinecone import DenseVectorQuery, Pinecone
pc = Pinecone(api_key="your-api-key")
info = pc.inference.model.get("multilingual-e5-large")
pc.indexes.create(
name="product-search",
schema={
"fields": {
"embedding": {
"type": "dense_vector",
"dimension": info.default_dimension,
"metric": "cosine",
}
}
},
)
index = pc.index("product-search")
texts = ["Lightweight trail running shoes", "Waterproof hiking boots"]
embeddings = pc.inference.embed(
model="multilingual-e5-large",
inputs=texts,
parameters={"input_type": "passage"},
)
index.documents.upsert(
namespace="products-en",
documents=[
{"_id": f"prod-{i}", "embedding": embedding.values, "description": text}
for i, (embedding, text) in enumerate(zip(embeddings, texts))
],
)
At query time, embed the query text with input_type="query" and score by the same
field:
query = pc.inference.embed(
model="multilingual-e5-large",
inputs="shoes for muddy trails",
parameters={"input_type": "query"},
)
results = index.documents.search(
namespace="products-en",
top_k=5,
score_by=[DenseVectorQuery(field="embedding", values=query.data[0].values)],
)
for match in results.matches:
print(match.id, match.score)
List available models¶
models = pc.inference.model.list(type="embed")
print(models.names())
type narrows the listing to "embed" or "rerank"; omit it for both. For embedding
models, vector_type="dense" or "sparse" narrows it further. Pairing vector_type
with type="rerank" raises PineconeValueError.
sparse_models = pc.inference.model.list(type="embed", vector_type="sparse")
print(sparse_models.names())