Pinecone
Use Pinecone serverless and pod indexes from Flux tasks.
Pinecone is a managed vector database. Flux 0.56.0 has no native Pinecone client — wrap the official SDK in @task and store the API key in Flux’s secret store.
Install
pip install pinecone
The package was renamed from pinecone-client to pinecone in early 2024. Both still resolve on PyPI for now; new code should pin pinecone.
API key
Pinecone authenticates with a single API key. Put it in the Flux secret store rather than an environment variable:
flux secrets set pinecone_api_key pcsk_...
Then declare it on the task:
from flux import task
@task.with_options(secret_requests=["pinecone_api_key"])
async def upsert_vectors(secrets, index_name: str, records: list[dict]) -> int:
from pinecone import Pinecone
pc = Pinecone(api_key=secrets["pinecone_api_key"])
index = pc.Index(index_name)
index.upsert(vectors=records)
return len(records)
The secrets dict is injected by Flux when secret_requests is set (flux/secret_managers.py). The key never appears in event logs or in the workflow source that travels server-to-worker.
Create an index
Index creation is a control-plane operation — do it once, outside of workflows, unless you have a real reason to create indexes on the fly:
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key=...)
pc.create_index(
name="kb",
dimension=1536,
metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
)
The free tier allows one index. If create_index fails with a quota error in CI, an existing index is the most likely cause; check pc.list_indexes() before creating.
Query
@task.with_options(secret_requests=["pinecone_api_key"])
async def query(secrets, index_name: str, vector: list[float], k: int = 5):
from pinecone import Pinecone
pc = Pinecone(api_key=secrets["pinecone_api_key"])
index = pc.Index(index_name)
result = index.query(vector=vector, top_k=k, include_metadata=True)
return [
{"id": m.id, "score": m.score, "metadata": m.metadata}
for m in result.matches
]
Pinecone’s query returns a QueryResponse whose matches have .id, .score, .values, and .metadata. Convert to a plain dict before returning so the result serializes cleanly into the event log.
Sparse-dense hybrid
Pinecone supports hybrid search by passing both vector (dense) and sparse_vector (lexical) to query. The sparse vector is {"indices": [...], "values": [...]}. Build sparse vectors with pinecone-text or a BM25 implementation; weight the two with the alpha parameter (0.0 = pure sparse, 1.0 = pure dense). Hybrid only works on indexes created with metric="dotproduct".
Namespaces
Pinecone namespaces are cheap logical partitions inside an index. They fit Flux’s multi-tenant model well: name the namespace after the workflow’s namespace, or after a tenant ID, and pass it to every upsert / query call. There is no separate “create namespace” call — writing to a new namespace creates it.
What goes wrong
- Quota errors look like permission errors. A 403 from
create_indexon the free tier usually means you already have one index, not that the key is invalid. - Serverless cold starts. First query against a long-idle serverless index can take several seconds. Wrap query tasks with
retry_max_attempts=2and a small backoff if you care. - Dimension mismatch. Upserting a 1536-dim vector into a 768-dim index fails with a generic message. Set
dimensionon the index to match the embedding model exactly.
See also
- Secret manager — how
secret_requestsresolves at task runtime. - pgvector — self-hosted alternative on the database Flux already uses.
Derived against Pinecone Python SDK 5.x, 2026-05.