From 842ba5f514d5d7e2cb709b441720ee0583837177 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Wed, 18 Feb 2026 14:11:00 -0500 Subject: [PATCH 1/6] regenerate core from OpenAPI and protos off of 2025-10 to pull in scan_factor and max_candidates --- codegen/apis | 2 +- .../core/grpc/protos/db_data_2025_10_pb2.py | 84 +++++++++---------- .../core/grpc/protos/db_data_2025_10_pb2.pyi | 6 +- .../openapi/db_data/model/query_request.py | 10 +++ pinecone/openapi_support/api_version.py | 2 +- 5 files changed, 59 insertions(+), 45 deletions(-) diff --git a/codegen/apis b/codegen/apis index be7f2b2f..103cc9d8 160000 --- a/codegen/apis +++ b/codegen/apis @@ -1 +1 @@ -Subproject commit be7f2b2f3f501661431790398ed9b39263ee9959 +Subproject commit 103cc9d8937de2e21c574d2868446aa141668ee0 diff --git a/pinecone/core/grpc/protos/db_data_2025_10_pb2.py b/pinecone/core/grpc/protos/db_data_2025_10_pb2.py index 83094d3d..3f34d8cf 100644 --- a/pinecone/core/grpc/protos/db_data_2025_10_pb2.py +++ b/pinecone/core/grpc/protos/db_data_2025_10_pb2.py @@ -27,7 +27,7 @@ from google.api import field_behavior_pb2 as google_dot_api_dot_field__behavior__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x64\x62_data_2025-10.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/api/field_behavior.proto\"J\n\x0cSparseValues\x12\x1d\n\x07indices\x18\x01 \x03(\rB\x03\xe0\x41\x02R\x07indices\x12\x1b\n\x06values\x18\x02 \x03(\x02\x42\x03\xe0\x41\x02R\x06values\"\x9e\x01\n\x06Vector\x12\x13\n\x02id\x18\x01 \x01(\tB\x03\xe0\x41\x02R\x02id\x12\x16\n\x06values\x18\x02 \x03(\x02R\x06values\x12\x32\n\rsparse_values\x18\x04 \x01(\x0b\x32\r.SparseValuesR\x0csparseValues\x12\x33\n\x08metadata\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"\xba\x01\n\x0cScoredVector\x12\x13\n\x02id\x18\x01 \x01(\tB\x03\xe0\x41\x02R\x02id\x12\x14\n\x05score\x18\x02 \x01(\x02R\x05score\x12\x16\n\x06values\x18\x03 \x03(\x02R\x06values\x12\x32\n\rsparse_values\x18\x05 \x01(\x0b\x32\r.SparseValuesR\x0csparseValues\x12\x33\n\x08metadata\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x08metadata\"\xa1\x01\n\x0cRequestUnion\x12(\n\x06upsert\x18\x01 \x01(\x0b\x32\x0e.UpsertRequestH\x00R\x06upsert\x12(\n\x06\x64\x65lete\x18\x02 \x01(\x0b\x32\x0e.DeleteRequestH\x00R\x06\x64\x65lete\x12(\n\x06update\x18\x03 \x01(\x0b\x32\x0e.UpdateRequestH\x00R\x06updateB\x13\n\x11RequestUnionInner\"U\n\rUpsertRequest\x12&\n\x07vectors\x18\x01 \x03(\x0b\x32\x07.VectorB\x03\xe0\x41\x02R\x07vectors\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\"7\n\x0eUpsertResponse\x12%\n\x0eupserted_count\x18\x01 \x01(\rR\rupsertedCount\"\x8f\x01\n\rDeleteRequest\x12\x10\n\x03ids\x18\x01 \x03(\tR\x03ids\x12\x1d\n\ndelete_all\x18\x02 \x01(\x08R\tdeleteAll\x12\x1c\n\tnamespace\x18\x03 \x01(\tR\tnamespace\x12/\n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x06\x66ilter\"\x10\n\x0e\x44\x65leteResponse\"C\n\x0c\x46\x65tchRequest\x12\x15\n\x03ids\x18\x01 \x03(\tB\x03\xe0\x41\x02R\x03ids\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\"\xe1\x01\n\x16\x46\x65tchByMetadataRequest\x12\x1c\n\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x34\n\x06\x66ilter\x18\x02 \x01(\x0b\x32\x17.google.protobuf.StructH\x00R\x06\x66ilter\x88\x01\x01\x12\x19\n\x05limit\x18\x03 \x01(\rH\x01R\x05limit\x88\x01\x01\x12.\n\x10pagination_token\x18\x04 \x01(\tH\x02R\x0fpaginationToken\x88\x01\x01\x42\t\n\x07_filterB\x08\n\x06_limitB\x13\n\x11_pagination_token\"\xab\x02\n\x17\x46\x65tchByMetadataResponse\x12?\n\x07vectors\x18\x01 \x03(\x0b\x32%.FetchByMetadataResponse.VectorsEntryR\x07vectors\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\x12!\n\x05usage\x18\x03 \x01(\x0b\x32\x06.UsageH\x00R\x05usage\x88\x01\x01\x12\x30\n\npagination\x18\x04 \x01(\x0b\x32\x0b.PaginationH\x01R\npagination\x88\x01\x01\x1a\x43\n\x0cVectorsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x07.VectorR\x05value:\x02\x38\x01\x42\x08\n\x06_usageB\r\n\x0b_pagination\"\xd6\x01\n\rFetchResponse\x12\x35\n\x07vectors\x18\x01 \x03(\x0b\x32\x1b.FetchResponse.VectorsEntryR\x07vectors\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\x12!\n\x05usage\x18\x03 \x01(\x0b\x32\x06.UsageH\x00R\x05usage\x88\x01\x01\x1a\x43\n\x0cVectorsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x07.VectorR\x05value:\x02\x38\x01\x42\x08\n\x06_usage\"\xbd\x01\n\x0bListRequest\x12\x1b\n\x06prefix\x18\x01 \x01(\tH\x00R\x06prefix\x88\x01\x01\x12\x19\n\x05limit\x18\x02 \x01(\rH\x01R\x05limit\x88\x01\x01\x12.\n\x10pagination_token\x18\x03 \x01(\tH\x02R\x0fpaginationToken\x88\x01\x01\x12\x1c\n\tnamespace\x18\x04 \x01(\tR\tnamespaceB\t\n\x07_prefixB\x08\n\x06_limitB\x13\n\x11_pagination_token\" \n\nPagination\x12\x12\n\x04next\x18\x01 \x01(\tR\x04next\"\x1a\n\x08ListItem\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\"\xbf\x01\n\x0cListResponse\x12#\n\x07vectors\x18\x01 \x03(\x0b\x32\t.ListItemR\x07vectors\x12\x30\n\npagination\x18\x02 \x01(\x0b\x32\x0b.PaginationH\x00R\npagination\x88\x01\x01\x12\x1c\n\tnamespace\x18\x03 \x01(\tR\tnamespace\x12!\n\x05usage\x18\x04 \x01(\x0b\x32\x06.UsageH\x01R\x05usage\x88\x01\x01\x42\r\n\x0b_paginationB\x08\n\x06_usage\"\xbd\x01\n\x0bQueryVector\x12\x16\n\x06values\x18\x01 \x03(\x02R\x06values\x12\x32\n\rsparse_values\x18\x05 \x01(\x0b\x32\r.SparseValuesR\x0csparseValues\x12\x13\n\x05top_k\x18\x02 \x01(\rR\x04topK\x12\x1c\n\tnamespace\x18\x03 \x01(\tR\tnamespace\x12/\n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x06\x66ilter\"\xd1\x02\n\x0cQueryRequest\x12\x1c\n\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x18\n\x05top_k\x18\x02 \x01(\rB\x03\xe0\x41\x02R\x04topK\x12/\n\x06\x66ilter\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructR\x06\x66ilter\x12%\n\x0einclude_values\x18\x04 \x01(\x08R\rincludeValues\x12)\n\x10include_metadata\x18\x05 \x01(\x08R\x0fincludeMetadata\x12*\n\x07queries\x18\x06 \x03(\x0b\x32\x0c.QueryVectorB\x02\x18\x01R\x07queries\x12\x16\n\x06vector\x18\x07 \x03(\x02R\x06vector\x12\x32\n\rsparse_vector\x18\t \x01(\x0b\x32\r.SparseValuesR\x0csparseVector\x12\x0e\n\x02id\x18\x08 \x01(\tR\x02id\"[\n\x12SingleQueryResults\x12\'\n\x07matches\x18\x01 \x03(\x0b\x32\r.ScoredVectorR\x07matches\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\"\xb6\x01\n\rQueryResponse\x12\x31\n\x07results\x18\x01 \x03(\x0b\x32\x13.SingleQueryResultsB\x02\x18\x01R\x07results\x12\'\n\x07matches\x18\x02 \x03(\x0b\x32\r.ScoredVectorR\x07matches\x12\x1c\n\tnamespace\x18\x03 \x01(\tR\tnamespace\x12!\n\x05usage\x18\x04 \x01(\x0b\x32\x06.UsageH\x00R\x05usage\x88\x01\x01\x42\x08\n\x06_usage\":\n\x05Usage\x12\"\n\nread_units\x18\x01 \x01(\rH\x00R\treadUnits\x88\x01\x01\x42\r\n\x0b_read_units\"\xb0\x02\n\rUpdateRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n\x06values\x18\x02 \x03(\x02R\x06values\x12\x32\n\rsparse_values\x18\x05 \x01(\x0b\x32\r.SparseValuesR\x0csparseValues\x12:\n\x0cset_metadata\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructR\x0bsetMetadata\x12\x1c\n\tnamespace\x18\x04 \x01(\tR\tnamespace\x12\x34\n\x06\x66ilter\x18\x06 \x01(\x0b\x32\x17.google.protobuf.StructH\x00R\x06\x66ilter\x88\x01\x01\x12\x1c\n\x07\x64ry_run\x18\x07 \x01(\x08H\x01R\x06\x64ryRun\x88\x01\x01\x42\t\n\x07_filterB\n\n\x08_dry_run\"R\n\x0eUpdateResponse\x12,\n\x0fmatched_records\x18\x01 \x01(\x05H\x00R\x0ematchedRecords\x88\x01\x01\x42\x12\n\x10_matched_records\"L\n\x19\x44\x65scribeIndexStatsRequest\x12/\n\x06\x66ilter\x18\x01 \x01(\x0b\x32\x17.google.protobuf.StructR\x06\x66ilter\"5\n\x10NamespaceSummary\x12!\n\x0cvector_count\x18\x01 \x01(\rR\x0bvectorCount\"\xa9\x01\n\x15ListNamespacesRequest\x12.\n\x10pagination_token\x18\x01 \x01(\tH\x00R\x0fpaginationToken\x88\x01\x01\x12\x19\n\x05limit\x18\x02 \x01(\rH\x01R\x05limit\x88\x01\x01\x12\x1b\n\x06prefix\x18\x03 \x01(\tH\x02R\x06prefix\x88\x01\x01\x42\x13\n\x11_pagination_tokenB\x08\n\x06_limitB\t\n\x07_prefix\"\xb1\x01\n\x16ListNamespacesResponse\x12\x35\n\nnamespaces\x18\x01 \x03(\x0b\x32\x15.NamespaceDescriptionR\nnamespaces\x12\x30\n\npagination\x18\x02 \x01(\x0b\x32\x0b.PaginationH\x00R\npagination\x88\x01\x01\x12\x1f\n\x0btotal_count\x18\x03 \x01(\x05R\ntotalCountB\r\n\x0b_pagination\"8\n\x18\x44\x65scribeNamespaceRequest\x12\x1c\n\tnamespace\x18\x01 \x01(\tR\tnamespace\"e\n\x16\x43reateNamespaceRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12,\n\x06schema\x18\x02 \x01(\x0b\x32\x0f.MetadataSchemaH\x00R\x06schema\x88\x01\x01\x42\t\n\x07_schema\"\'\n\rIndexedFields\x12\x16\n\x06\x66ields\x18\x01 \x03(\tR\x06\x66ields\"\xd5\x01\n\x14NamespaceDescription\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12!\n\x0crecord_count\x18\x02 \x01(\x04R\x0brecordCount\x12,\n\x06schema\x18\x03 \x01(\x0b\x32\x0f.MetadataSchemaH\x00R\x06schema\x88\x01\x01\x12:\n\x0eindexed_fields\x18\x04 \x01(\x0b\x32\x0e.IndexedFieldsH\x01R\rindexedFields\x88\x01\x01\x42\t\n\x07_schemaB\x11\n\x0f_indexed_fields\"6\n\x16\x44\x65leteNamespaceRequest\x12\x1c\n\tnamespace\x18\x01 \x01(\tR\tnamespace\"\xa6\x04\n\x1a\x44\x65scribeIndexStatsResponse\x12K\n\nnamespaces\x18\x01 \x03(\x0b\x32+.DescribeIndexStatsResponse.NamespacesEntryR\nnamespaces\x12!\n\tdimension\x18\x02 \x01(\rH\x00R\tdimension\x88\x01\x01\x12%\n\x0eindex_fullness\x18\x03 \x01(\x02R\rindexFullness\x12,\n\x12total_vector_count\x18\x04 \x01(\rR\x10totalVectorCount\x12\x1b\n\x06metric\x18\x05 \x01(\tH\x01R\x06metric\x88\x01\x01\x12$\n\x0bvector_type\x18\x06 \x01(\tH\x02R\nvectorType\x88\x01\x01\x12,\n\x0fmemory_fullness\x18\x07 \x01(\x02H\x03R\x0ememoryFullness\x88\x01\x01\x12.\n\x10storage_fullness\x18\x08 \x01(\x02H\x04R\x0fstorageFullness\x88\x01\x01\x1aP\n\x0fNamespacesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\'\n\x05value\x18\x02 \x01(\x0b\x32\x11.NamespaceSummaryR\x05value:\x02\x38\x01\x42\x0c\n\n_dimensionB\t\n\x07_metricB\x0e\n\x0c_vector_typeB\x12\n\x10_memory_fullnessB\x13\n\x11_storage_fullness\"9\n\x17MetadataFieldProperties\x12\x1e\n\nfilterable\x18\x01 \x01(\x08R\nfilterable\"\x9a\x01\n\x0eMetadataSchema\x12\x33\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x1b.MetadataSchema.FieldsEntryR\x06\x66ields\x1aS\n\x0b\x46ieldsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x18.MetadataFieldPropertiesR\x05value:\x02\x38\x01\x32\x9c\x08\n\rVectorService\x12\x45\n\x06Upsert\x12\x0e.UpsertRequest\x1a\x0f.UpsertResponse\"\x1a\x82\xd3\xe4\x93\x02\x14\"\x0f/vectors/upsert:\x01*\x12X\n\x06\x44\x65lete\x12\x0e.DeleteRequest\x1a\x0f.DeleteResponse\"-\x82\xd3\xe4\x93\x02\'\"\x0f/vectors/delete:\x01*Z\x11*\x0f/vectors/delete\x12>\n\x05\x46\x65tch\x12\r.FetchRequest\x1a\x0e.FetchResponse\"\x16\x82\xd3\xe4\x93\x02\x10\x12\x0e/vectors/fetch\x12:\n\x04List\x12\x0c.ListRequest\x1a\r.ListResponse\"\x15\x82\xd3\xe4\x93\x02\x0f\x12\r/vectors/list\x12\x39\n\x05Query\x12\r.QueryRequest\x1a\x0e.QueryResponse\"\x11\x82\xd3\xe4\x93\x02\x0b\"\x06/query:\x01*\x12\x45\n\x06Update\x12\x0e.UpdateRequest\x1a\x0f.UpdateResponse\"\x1a\x82\xd3\xe4\x93\x02\x14\"\x0f/vectors/update:\x01*\x12\x88\x01\n\x12\x44\x65scribeIndexStats\x12\x1a.DescribeIndexStatsRequest\x1a\x1b.DescribeIndexStatsResponse\"9\x82\xd3\xe4\x93\x02\x33\"\x15/describe_index_stats:\x01*Z\x17\x12\x15/describe_index_stats\x12V\n\x0eListNamespaces\x12\x16.ListNamespacesRequest\x1a\x17.ListNamespacesResponse\"\x13\x82\xd3\xe4\x93\x02\r\x12\x0b/namespaces\x12\x66\n\x11\x44\x65scribeNamespace\x12\x19.DescribeNamespaceRequest\x1a\x15.NamespaceDescription\"\x1f\x82\xd3\xe4\x93\x02\x19\x12\x17/namespaces/{namespace}\x12\\\n\x0f\x44\x65leteNamespace\x12\x17.DeleteNamespaceRequest\x1a\x0f.DeleteResponse\"\x1f\x82\xd3\xe4\x93\x02\x19*\x17/namespaces/{namespace}\x12V\n\x0f\x43reateNamespace\x12\x17.CreateNamespaceRequest\x1a\x15.NamespaceDescription\"\x13\x82\xd3\xe4\x93\x02\r\"\x0b/namespaces\x12k\n\x0f\x46\x65tchByMetadata\x12\x17.FetchByMetadataRequest\x1a\x18.FetchByMetadataResponse\"%\x82\xd3\xe4\x93\x02\x1f\"\x1a/vectors/fetch_by_metadata:\x01*BS\n\x11io.pinecone.protoP\x01Z\n\x05\x46\x65tch\x12\r.FetchRequest\x1a\x0e.FetchResponse\"\x16\x82\xd3\xe4\x93\x02\x10\x12\x0e/vectors/fetch\x12:\n\x04List\x12\x0c.ListRequest\x1a\r.ListResponse\"\x15\x82\xd3\xe4\x93\x02\x0f\x12\r/vectors/list\x12\x39\n\x05Query\x12\r.QueryRequest\x1a\x0e.QueryResponse\"\x11\x82\xd3\xe4\x93\x02\x0b\"\x06/query:\x01*\x12\x45\n\x06Update\x12\x0e.UpdateRequest\x1a\x0f.UpdateResponse\"\x1a\x82\xd3\xe4\x93\x02\x14\"\x0f/vectors/update:\x01*\x12\x88\x01\n\x12\x44\x65scribeIndexStats\x12\x1a.DescribeIndexStatsRequest\x1a\x1b.DescribeIndexStatsResponse\"9\x82\xd3\xe4\x93\x02\x33\"\x15/describe_index_stats:\x01*Z\x17\x12\x15/describe_index_stats\x12V\n\x0eListNamespaces\x12\x16.ListNamespacesRequest\x1a\x17.ListNamespacesResponse\"\x13\x82\xd3\xe4\x93\x02\r\x12\x0b/namespaces\x12\x66\n\x11\x44\x65scribeNamespace\x12\x19.DescribeNamespaceRequest\x1a\x15.NamespaceDescription\"\x1f\x82\xd3\xe4\x93\x02\x19\x12\x17/namespaces/{namespace}\x12\\\n\x0f\x44\x65leteNamespace\x12\x17.DeleteNamespaceRequest\x1a\x0f.DeleteResponse\"\x1f\x82\xd3\xe4\x93\x02\x19*\x17/namespaces/{namespace}\x12V\n\x0f\x43reateNamespace\x12\x17.CreateNamespaceRequest\x1a\x15.NamespaceDescription\"\x13\x82\xd3\xe4\x93\x02\r\"\x0b/namespaces\x12k\n\x0f\x46\x65tchByMetadata\x12\x17.FetchByMetadataRequest\x1a\x18.FetchByMetadataResponse\"%\x82\xd3\xe4\x93\x02\x1f\"\x1a/vectors/fetch_by_metadata:\x01*BS\n\x11io.pinecone.protoP\x01Z None: ... + scan_factor: float + max_candidates: int + def __init__(self, namespace: _Optional[str] = ..., top_k: _Optional[int] = ..., filter: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., include_values: _Optional[bool] = ..., include_metadata: _Optional[bool] = ..., queries: _Optional[_Iterable[_Union[QueryVector, _Mapping]]] = ..., vector: _Optional[_Iterable[float]] = ..., sparse_vector: _Optional[_Union[SparseValues, _Mapping]] = ..., id: _Optional[str] = ..., scan_factor: _Optional[float] = ..., max_candidates: _Optional[int] = ...) -> None: ... class SingleQueryResults(_message.Message): __slots__ = () diff --git a/pinecone/core/openapi/db_data/model/query_request.py b/pinecone/core/openapi/db_data/model/query_request.py index f0ed26fe..31e5fa3d 100644 --- a/pinecone/core/openapi/db_data/model/query_request.py +++ b/pinecone/core/openapi/db_data/model/query_request.py @@ -80,6 +80,8 @@ class QueryRequest(ModelNormal): ("queries",): {}, ("vector",): {}, ("id",): {"max_length": 512}, + ("scan_factor",): {"inclusive_maximum": 4.0, "inclusive_minimum": 0.5}, + ("max_candidates",): {"inclusive_maximum": 100000, "inclusive_minimum": 1}, } @cached_class_property @@ -114,6 +116,8 @@ def openapi_types(cls): "vector": ([float],), # noqa: E501 "sparse_vector": (SparseValues,), # noqa: E501 "id": (str,), # noqa: E501 + "scan_factor": (float,), # noqa: E501 + "max_candidates": (int,), # noqa: E501 } @cached_class_property @@ -130,6 +134,8 @@ def discriminator(cls): "vector": "vector", # noqa: E501 "sparse_vector": "sparseVector", # noqa: E501 "id": "id", # noqa: E501 + "scan_factor": "scanFactor", # noqa: E501 + "max_candidates": "maxCandidates", # noqa: E501 } read_only_vars: Set[str] = set([]) @@ -194,6 +200,8 @@ def _from_openapi_data(cls: Type[T], top_k, *args, **kwargs) -> T: # noqa: E501 vector ([float]): The query vector. This should be the same length as the dimension of the index being queried. Each `query` request can contain only one of the parameters `id` or `vector`. [optional] # noqa: E501 sparse_vector (SparseValues): [optional] # noqa: E501 id (str): The unique ID of the vector to be used as a query vector. Each request can contain either the `vector` or `id` parameter. [optional] # noqa: E501 + scan_factor (float): An optimization parameter for IVF dense indexes in dedicated read node indexes. It adjusts how much of the index is scanned to find vector candidates. Range: 0.5 – 4 (default). Keep the default (4.0) for the best search results. If query latency is too high, try lowering this value incrementally (minimum 0.5) to speed up the search at the cost of slightly lower accuracy. This parameter is only supported for dedicated (DRN) dense indexes. [optional] # noqa: E501 + max_candidates (int): An optimization parameter that controls the maximum number of candidate dense vectors to rerank. Reranking computes exact distances to improve recall but increases query latency. Range: top_k – 100000. Keep the default for a balance of recall and latency. Increase this value if recall is too low, or decrease it to reduce latency at the cost of accuracy. This parameter is only supported for dedicated (DRN) dense indexes. [optional] # noqa: E501 """ _enforce_allowed_values = kwargs.pop("_enforce_allowed_values", False) @@ -295,6 +303,8 @@ def __init__(self, top_k, *args, **kwargs) -> None: # noqa: E501 vector ([float]): The query vector. This should be the same length as the dimension of the index being queried. Each `query` request can contain only one of the parameters `id` or `vector`. [optional] # noqa: E501 sparse_vector (SparseValues): [optional] # noqa: E501 id (str): The unique ID of the vector to be used as a query vector. Each request can contain either the `vector` or `id` parameter. [optional] # noqa: E501 + scan_factor (float): An optimization parameter for IVF dense indexes in dedicated read node indexes. It adjusts how much of the index is scanned to find vector candidates. Range: 0.5 – 4 (default). Keep the default (4.0) for the best search results. If query latency is too high, try lowering this value incrementally (minimum 0.5) to speed up the search at the cost of slightly lower accuracy. This parameter is only supported for dedicated (DRN) dense indexes. [optional] # noqa: E501 + max_candidates (int): An optimization parameter that controls the maximum number of candidate dense vectors to rerank. Reranking computes exact distances to improve recall but increases query latency. Range: top_k – 100000. Keep the default for a balance of recall and latency. Increase this value if recall is too low, or decrease it to reduce latency at the cost of accuracy. This parameter is only supported for dedicated (DRN) dense indexes. [optional] # noqa: E501 """ _enforce_allowed_values = kwargs.pop("_enforce_allowed_values", True) diff --git a/pinecone/openapi_support/api_version.py b/pinecone/openapi_support/api_version.py index 426a6d99..21ca4af7 100644 --- a/pinecone/openapi_support/api_version.py +++ b/pinecone/openapi_support/api_version.py @@ -2,4 +2,4 @@ # Do not edit this file manually. API_VERSION = "2025-10" -APIS_REPO_SHA = "c968f9da428c7cf3742db5a3d2802c0d1bd728e7" +APIS_REPO_SHA = "be7f2b2f3f501661431790398ed9b39263ee9959" From b55ca7632ce3d20b3beabe48aa2ae04860943323 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Wed, 18 Feb 2026 15:08:34 -0500 Subject: [PATCH 2/6] add max_candidates and scan_factor to query and query_namespaces in db_data/index.py, db_data/index_asyncio.py, db_data/resources/sync/vector.py, and db_data/resources/asyncio/vector_asyncio.py. update IndexRequestFactory.query_request() to handle max_candidates and scan_factor --- pinecone/db_data/index.py | 12 ++++++++++++ pinecone/db_data/index_asyncio.py | 12 ++++++++++++ pinecone/db_data/request_factory.py | 4 ++++ pinecone/db_data/resources/asyncio/vector_asyncio.py | 12 ++++++++++++ pinecone/db_data/resources/sync/vector.py | 12 ++++++++++++ 5 files changed, 52 insertions(+) diff --git a/pinecone/db_data/index.py b/pinecone/db_data/index.py index e3337181..1fa3d5f2 100644 --- a/pinecone/db_data/index.py +++ b/pinecone/db_data/index.py @@ -945,6 +945,8 @@ def query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: SparseValues | SparseVectorTypedDict | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryResponse | ApplyResult: """Query a namespace using a query vector. @@ -1033,6 +1035,8 @@ def query( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) @@ -1054,6 +1058,8 @@ def _query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: SparseValues | SparseVectorTypedDict | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> OpenAPIQueryResponse: if len(args) > 0: @@ -1073,6 +1079,8 @@ def _query( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) from typing import cast @@ -1092,6 +1100,8 @@ def query_namespaces( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: SparseValues | SparseVectorTypedDict | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryNamespacesResults: """Query multiple namespaces in parallel and combine the results. @@ -1171,6 +1181,8 @@ def query_namespaces( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, async_threadpool_executor=True, _preload_content=False, **kwargs, diff --git a/pinecone/db_data/index_asyncio.py b/pinecone/db_data/index_asyncio.py index 3016f031..25bb8c0b 100644 --- a/pinecone/db_data/index_asyncio.py +++ b/pinecone/db_data/index_asyncio.py @@ -728,6 +728,8 @@ async def query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryResponse: """ @@ -846,6 +848,8 @@ async def main(): include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) return parse_query_response(response) @@ -861,6 +865,8 @@ async def _query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> OpenAPIQueryResponse: if len(args) > 0: @@ -877,6 +883,8 @@ async def _query( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) from typing import cast @@ -897,6 +905,8 @@ async def query_namespaces( include_metadata: bool | None = None, vector: list[float] | None = None, sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryNamespacesResults: """The query_namespaces() method is used to make a query to multiple namespaces in parallel and combine the results into one result set. @@ -965,6 +975,8 @@ async def main(): sparse_vector=sparse_vector, async_threadpool_executor=True, _preload_content=False, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) for ns in target_namespaces diff --git a/pinecone/db_data/request_factory.py b/pinecone/db_data/request_factory.py index c40c3e09..ba6c73a3 100644 --- a/pinecone/db_data/request_factory.py +++ b/pinecone/db_data/request_factory.py @@ -56,6 +56,8 @@ def query_request( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: SparseValues | SparseVectorTypedDict | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryRequest: if vector is not None and id is not None: @@ -73,6 +75,8 @@ def query_request( ("include_values", include_values), ("include_metadata", include_metadata), ("sparse_vector", sparse_vector_normalized), + ("scan_factor", scan_factor), + ("max_candidates", max_candidates), ] ) diff --git a/pinecone/db_data/resources/asyncio/vector_asyncio.py b/pinecone/db_data/resources/asyncio/vector_asyncio.py index 92048331..cfc03661 100644 --- a/pinecone/db_data/resources/asyncio/vector_asyncio.py +++ b/pinecone/db_data/resources/asyncio/vector_asyncio.py @@ -372,6 +372,8 @@ async def query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryResponse: """Query the index. @@ -423,6 +425,8 @@ async def query( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) # parse_query_response already returns QueryResponse @@ -439,6 +443,8 @@ async def _query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> OpenAPIQueryResponse: if len(args) > 0: @@ -455,6 +461,8 @@ async def _query( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) from typing import cast @@ -475,6 +483,8 @@ async def query_namespaces( include_metadata: bool | None = None, vector: list[float] | None = None, sparse_vector: (SparseValues | dict[str, list[float] | list[int]]) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryNamespacesResults: """Query across multiple namespaces. @@ -530,6 +540,8 @@ async def query_namespaces( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, # type: ignore[arg-type] + scan_factor=scan_factor, + max_candidates=max_candidates, async_threadpool_executor=True, _preload_content=False, **kwargs, diff --git a/pinecone/db_data/resources/sync/vector.py b/pinecone/db_data/resources/sync/vector.py index 50b9a303..cad7957a 100644 --- a/pinecone/db_data/resources/sync/vector.py +++ b/pinecone/db_data/resources/sync/vector.py @@ -416,6 +416,8 @@ def query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryResponse | ApplyResult: """Query the index. @@ -467,6 +469,8 @@ def query( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) @@ -489,6 +493,8 @@ def _query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> OpenAPIQueryResponse: if len(args) > 0: @@ -508,6 +514,8 @@ def _query( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) from typing import cast @@ -526,6 +534,8 @@ def query_namespaces( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryNamespacesResults: """Query across multiple namespaces. @@ -581,6 +591,8 @@ def query_namespaces( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, async_threadpool_executor=True, _preload_content=False, **kwargs, From 2c142ae3165aeb33818517803054534c50891a84 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Wed, 18 Feb 2026 15:18:49 -0500 Subject: [PATCH 3/6] update doc comments for query and query_namespaces in various locations --- pinecone/db_data/index.py | 16 ++++++++++++++++ pinecone/db_data/index_asyncio.py | 16 ++++++++++++++++ .../db_data/resources/asyncio/vector_asyncio.py | 16 ++++++++++++++++ pinecone/db_data/resources/sync/vector.py | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/pinecone/db_data/index.py b/pinecone/db_data/index.py index 1fa3d5f2..7f25b8ea 100644 --- a/pinecone/db_data/index.py +++ b/pinecone/db_data/index.py @@ -972,6 +972,14 @@ def query( sparse_vector: Sparse values of the query vector. Expected to be either a SparseValues object or a dict of the form: ``{'indices': list[int], 'values': list[float]}``, where the lists each have the same length. [optional] + scan_factor: An optimization parameter for the IVF dense indexes in dedicated + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameters is only + supported for dedicated (DRN) dense indexes. [optional] + max_candidates: An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] **kwargs: Additional keyword arguments for the API call. Returns: @@ -1126,6 +1134,14 @@ def query_namespaces( include_values: Boolean field indicating whether vector values should be included with results. Defaults to None. [optional] include_metadata: Boolean field indicating whether vector metadata should be included with results. Defaults to None. [optional] sparse_vector: If you are working with a dotproduct index, you can pass a sparse vector as part of your hybrid search. Defaults to None. [optional] + scan_factor: An optimization parameter for the IVF dense indexes in dedicated + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameters is only + supported for dedicated (DRN) dense indexes. [optional] + max_candidates: An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] **kwargs: Additional keyword arguments for the API call. Returns: diff --git a/pinecone/db_data/index_asyncio.py b/pinecone/db_data/index_asyncio.py index 25bb8c0b..8b198963 100644 --- a/pinecone/db_data/index_asyncio.py +++ b/pinecone/db_data/index_asyncio.py @@ -834,6 +834,14 @@ async def main(): sparse_vector: (Union[SparseValues, dict[str, Union[list[float], list[int]]]]): sparse values of the query vector. Expected to be either a SparseValues object or a dict of the form: {'indices': list[int], 'values': list[float]}, where the lists each have the same length. + scan_factor: An optimization parameter for the IVF dense indexes in dedicated + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameters is only + supported for dedicated (DRN) dense indexes. [optional] + max_candidates: An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] Returns: QueryResponse object which contains the list of the closest vectors as ScoredVector objects, and namespace name. @@ -919,6 +927,14 @@ async def query_namespaces( include_values (Optional[bool], optional): Boolean field indicating whether vector values should be included with results. Defaults to None. include_metadata (Optional[bool], optional): Boolean field indicating whether vector metadata should be included with results. Defaults to None. sparse_vector (Optional[ Union[SparseValues, dict[str, Union[list[float], list[int]]]] ], optional): If you are working with a dotproduct index, you can pass a sparse vector as part of your hybrid search. Defaults to None. + scan_factor: An optimization parameter for the IVF dense indexes in dedicated + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameters is only + supported for dedicated (DRN) dense indexes. [optional] + max_candidates: An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] Returns: QueryNamespacesResults: A QueryNamespacesResults object containing the combined results from all namespaces, as well as the combined usage cost in read units. diff --git a/pinecone/db_data/resources/asyncio/vector_asyncio.py b/pinecone/db_data/resources/asyncio/vector_asyncio.py index cfc03661..2813fa05 100644 --- a/pinecone/db_data/resources/asyncio/vector_asyncio.py +++ b/pinecone/db_data/resources/asyncio/vector_asyncio.py @@ -403,6 +403,14 @@ async def query( SparseValues object or a dict of the form {'indices': list[int], 'values': list[float]}, where the lists each have the same length. [optional] + scan_factor: An optimization parameter for the IVF dense indexes in dedicated + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameters is only + supported for dedicated (DRN) dense indexes. [optional] + max_candidates: An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] **kwargs: Additional keyword arguments. Returns: @@ -505,6 +513,14 @@ async def query_namespaces( include_metadata: Indicates whether metadata is included in the response. [optional] sparse_vector: Sparse values of the query vector. [optional] + scan_factor: An optimization parameter for the IVF dense indexes in dedicated + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameters is only + supported for dedicated (DRN) dense indexes. [optional] + max_candidates: An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] **kwargs: Additional keyword arguments. Returns: diff --git a/pinecone/db_data/resources/sync/vector.py b/pinecone/db_data/resources/sync/vector.py index cad7957a..c4f0ffbb 100644 --- a/pinecone/db_data/resources/sync/vector.py +++ b/pinecone/db_data/resources/sync/vector.py @@ -447,6 +447,14 @@ def query( SparseValues object or a dict of the form {'indices': list[int], 'values': list[float]}, where the lists each have the same length. [optional] + scan_factor: An optimization parameter for the IVF dense indexes in dedicated + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameters is only + supported for dedicated (DRN) dense indexes. [optional] + max_candidates: An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] **kwargs: Additional keyword arguments. Returns: @@ -556,6 +564,14 @@ def query_namespaces( include_metadata: Indicates whether metadata is included in the response. [optional] sparse_vector: Sparse values of the query vector. [optional] + scan_factor: An optimization parameter for the IVF dense indexes in dedicated + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameters is only + supported for dedicated (DRN) dense indexes. [optional] + max_candidates: An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] **kwargs: Additional keyword arguments. Returns: From d5f31c05d0d45d72b1bb77a78ba17d3266fbd514 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Thu, 19 Feb 2026 10:38:35 -0500 Subject: [PATCH 4/6] doc comment tweaks --- pinecone/db_data/index.py | 24 ++++++++++++------------ pinecone/db_data/index_asyncio.py | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/pinecone/db_data/index.py b/pinecone/db_data/index.py index 7f25b8ea..c15cc2a0 100644 --- a/pinecone/db_data/index.py +++ b/pinecone/db_data/index.py @@ -973,13 +973,13 @@ def query( of the form: ``{'indices': list[int], 'values': list[float]}``, where the lists each have the same length. [optional] scan_factor: An optimization parameter for the IVF dense indexes in dedicated - read node indexes. It adjusts how much of the index is scanned to find - vector candidates. Range: 0.5 - 4 (default). This parameters is only - supported for dedicated (DRN) dense indexes. [optional] + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameter is only + supported for dedicated (DRN) dense indexes. [optional] max_candidates: An optimization parameter that controls the maximum number of - candidate dense vectors to rerank. Reranking computes exact distances to - improve recall but increases query latency. Range: top_k - 100000. This - parameter is only supported for dedicated (DRN) dense indexes. [optional] + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] **kwargs: Additional keyword arguments for the API call. Returns: @@ -1135,13 +1135,13 @@ def query_namespaces( include_metadata: Boolean field indicating whether vector metadata should be included with results. Defaults to None. [optional] sparse_vector: If you are working with a dotproduct index, you can pass a sparse vector as part of your hybrid search. Defaults to None. [optional] scan_factor: An optimization parameter for the IVF dense indexes in dedicated - read node indexes. It adjusts how much of the index is scanned to find - vector candidates. Range: 0.5 - 4 (default). This parameters is only - supported for dedicated (DRN) dense indexes. [optional] + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameter is only + supported for dedicated (DRN) dense indexes. [optional] max_candidates: An optimization parameter that controls the maximum number of - candidate dense vectors to rerank. Reranking computes exact distances to - improve recall but increases query latency. Range: top_k - 100000. This - parameter is only supported for dedicated (DRN) dense indexes. [optional] + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] **kwargs: Additional keyword arguments for the API call. Returns: diff --git a/pinecone/db_data/index_asyncio.py b/pinecone/db_data/index_asyncio.py index 8b198963..a7da5b30 100644 --- a/pinecone/db_data/index_asyncio.py +++ b/pinecone/db_data/index_asyncio.py @@ -835,13 +835,13 @@ async def main(): Expected to be either a SparseValues object or a dict of the form: {'indices': list[int], 'values': list[float]}, where the lists each have the same length. scan_factor: An optimization parameter for the IVF dense indexes in dedicated - read node indexes. It adjusts how much of the index is scanned to find - vector candidates. Range: 0.5 - 4 (default). This parameters is only - supported for dedicated (DRN) dense indexes. [optional] + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameter is only + supported for dedicated (DRN) dense indexes. [optional] max_candidates: An optimization parameter that controls the maximum number of - candidate dense vectors to rerank. Reranking computes exact distances to - improve recall but increases query latency. Range: top_k - 100000. This - parameter is only supported for dedicated (DRN) dense indexes. [optional] + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] Returns: QueryResponse object which contains the list of the closest vectors as ScoredVector objects, and namespace name. @@ -928,13 +928,13 @@ async def query_namespaces( include_metadata (Optional[bool], optional): Boolean field indicating whether vector metadata should be included with results. Defaults to None. sparse_vector (Optional[ Union[SparseValues, dict[str, Union[list[float], list[int]]]] ], optional): If you are working with a dotproduct index, you can pass a sparse vector as part of your hybrid search. Defaults to None. scan_factor: An optimization parameter for the IVF dense indexes in dedicated - read node indexes. It adjusts how much of the index is scanned to find - vector candidates. Range: 0.5 - 4 (default). This parameters is only - supported for dedicated (DRN) dense indexes. [optional] + read node indexes. It adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). This parameter is only + supported for dedicated (DRN) dense indexes. [optional] max_candidates: An optimization parameter that controls the maximum number of - candidate dense vectors to rerank. Reranking computes exact distances to - improve recall but increases query latency. Range: top_k - 100000. This - parameter is only supported for dedicated (DRN) dense indexes. [optional] + candidate dense vectors to rerank. Reranking computes exact distances to + improve recall but increases query latency. Range: top_k - 100000. This + parameter is only supported for dedicated (DRN) dense indexes. [optional] Returns: QueryNamespacesResults: A QueryNamespacesResults object containing the combined results from all namespaces, as well as the combined usage cost in read units. From 3857127bda5ad9f815b7227fec67f06aa6027376 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Thu, 19 Feb 2026 13:34:58 -0500 Subject: [PATCH 5/6] add max_candidates and scan_factor to GRPCIndex.query() and GRPCIndex.query_namespaces() --- pinecone/grpc/index_grpc.py | 21 ++++++++ tests/unit_grpc/test_grpc_index_query.py | 69 ++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/pinecone/grpc/index_grpc.py b/pinecone/grpc/index_grpc.py index fd01e994..a4a402c1 100644 --- a/pinecone/grpc/index_grpc.py +++ b/pinecone/grpc/index_grpc.py @@ -501,6 +501,8 @@ def _query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | GRPCSparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> tuple[ProtoQueryResponse, dict[str, str] | None]: """ @@ -529,6 +531,8 @@ def _query( ("include_values", include_values), ("include_metadata", include_metadata), ("sparse_vector", sparse_vector), + ("scan_factor", scan_factor), + ("max_candidates", max_candidates), ] ) @@ -548,6 +552,8 @@ def query( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (SparseValues | GRPCSparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, async_req: bool | None = False, **kwargs, ) -> "QueryResponse" | PineconeGrpcFuture: @@ -588,6 +594,13 @@ def query( sparse_vector: (Union[SparseValues, dict[str, Union[list[float], list[int]]]]): sparse values of the query vector. Expected to be either a SparseValues object or a dict of the form: {'indices': list[int], 'values': list[float]}, where the lists each have the same length. + scan_factor (float): An optimization parameter for IVF dense indexes in dedicated + read node indexes. Adjusts how much of the index is scanned to find + vector candidates. Range: 0.5 - 4 (default). Only supported for + dedicated (DRN) dense indexes. [optional] + max_candidates (int): An optimization parameter that controls the maximum number of + candidate dense vectors to rerank. Range: top_k - 100000. Only + supported for dedicated (DRN) dense indexes. [optional] Returns: QueryResponse object which contains the list of the closest vectors as ScoredVector objects, and namespace name. @@ -616,6 +629,8 @@ def query( ("include_values", include_values), ("include_metadata", include_metadata), ("sparse_vector", sparse_vector), + ("scan_factor", scan_factor), + ("max_candidates", max_candidates), ] ) @@ -637,6 +652,8 @@ def query( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, timeout=timeout, **kwargs, ) @@ -654,6 +671,8 @@ def query_namespaces( include_values: bool | None = None, include_metadata: bool | None = None, sparse_vector: (GRPCSparseValues | SparseVectorTypedDict) | None = None, + scan_factor: float | None = None, + max_candidates: int | None = None, **kwargs, ) -> QueryNamespacesResults: if namespaces is None or len(namespaces) == 0: @@ -675,6 +694,8 @@ def query_namespaces( include_values=include_values, include_metadata=include_metadata, sparse_vector=sparse_vector, + scan_factor=scan_factor, + max_candidates=max_candidates, **kwargs, ) for ns in target_namespaces diff --git a/tests/unit_grpc/test_grpc_index_query.py b/tests/unit_grpc/test_grpc_index_query.py index 32a273b7..e531014b 100644 --- a/tests/unit_grpc/test_grpc_index_query.py +++ b/tests/unit_grpc/test_grpc_index_query.py @@ -46,3 +46,72 @@ def test_query_byVecId_queryByVecId(self, mocker): def test_query_rejects_both_id_and_vector(self): with pytest.raises(ValueError, match="Cannot specify both `id` and `vector`"): self.index.query(top_k=10, id="vec1", vector=[1, 2, 3]) + + def test_query_with_scan_factor_forwarded_to_proto(self, mocker, vals1): + mock_response = QueryResponse() + mocker.patch.object(self.index.runner, "run", return_value=(mock_response, None)) + self.index.query(top_k=10, vector=vals1, scan_factor=2.0) + self.index.runner.run.assert_called_once_with( + self.index.stub.Query, + QueryRequest(top_k=10, vector=vals1, scan_factor=2.0), + timeout=None, + ) + + def test_query_with_max_candidates_forwarded_to_proto(self, mocker, vals1): + mock_response = QueryResponse() + mocker.patch.object(self.index.runner, "run", return_value=(mock_response, None)) + self.index.query(top_k=10, vector=vals1, max_candidates=500) + self.index.runner.run.assert_called_once_with( + self.index.stub.Query, + QueryRequest(top_k=10, vector=vals1, max_candidates=500), + timeout=None, + ) + + def test_query_with_scan_factor_and_max_candidates_forwarded_to_proto(self, mocker, vals1): + mock_response = QueryResponse() + mocker.patch.object(self.index.runner, "run", return_value=(mock_response, None)) + self.index.query(top_k=10, vector=vals1, scan_factor=1.5, max_candidates=1000) + self.index.runner.run.assert_called_once_with( + self.index.stub.Query, + QueryRequest(top_k=10, vector=vals1, scan_factor=1.5, max_candidates=1000), + timeout=None, + ) + + def test_query_async_with_scan_factor_and_max_candidates_forwarded_to_proto( + self, mocker, vals1 + ): + mock_future = mocker.MagicMock() + mocker.patch.object(self.index.runner, "run", return_value=(mock_future, None)) + self.index.query( + top_k=10, vector=vals1, scan_factor=0.5, max_candidates=200, async_req=True + ) + self.index.runner.run.assert_called_once_with( + self.index.stub.Query.future, + QueryRequest(top_k=10, vector=vals1, scan_factor=0.5, max_candidates=200), + timeout=None, + ) + + +class TestGrpcIndexQueryNamespaces: + def setup_method(self): + self.config = Config(api_key="test-api-key", host="foo.pinecone.io") + self.index = GRPCIndex( + config=self.config, index_name="example-name", _endpoint_override="test-endpoint" + ) + + def test_query_namespaces_forwards_scan_factor_and_max_candidates(self, mocker, vals1): + mock_response = QueryResponse() + mocker.patch.object(self.index, "_query", return_value=(mock_response, None)) + self.index.query_namespaces( + vector=vals1, + namespaces=["ns1", "ns2"], + metric="cosine", + top_k=5, + scan_factor=3.0, + max_candidates=750, + ) + calls = self.index._query.call_args_list + assert len(calls) == 2 + for call in calls: + assert call.kwargs["scan_factor"] == 3.0 + assert call.kwargs["max_candidates"] == 750 From 0c40a4374d6a7e287624048a24d44b602985e43d Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Thu, 19 Feb 2026 14:13:12 -0500 Subject: [PATCH 6/6] add some additional query and query_namespace tests to validate passing scan_factor and max_candidates --- tests/unit/data/test_request_factory.py | 35 ++++++++ tests/unit/test_index.py | 105 ++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/tests/unit/data/test_request_factory.py b/tests/unit/data/test_request_factory.py index c88b6ed1..8334755f 100644 --- a/tests/unit/data/test_request_factory.py +++ b/tests/unit/data/test_request_factory.py @@ -7,6 +7,7 @@ ) from pinecone.core.openapi.db_data.models import FetchByMetadataRequest +from pinecone.core.openapi.db_data.model.query_request import QueryRequest from pinecone import RerankModel from tests.fixtures import ( @@ -623,3 +624,37 @@ def test_update_request_without_dry_run_not_included(self): assert "dry_run" not in request._data_store # endregion + + +class TestQueryRequest: + def test_query_request_basic(self): + request = IndexRequestFactory.query_request(top_k=10, vector=[0.1, 0.2, 0.3]) + assert isinstance(request, QueryRequest) + assert request.top_k == 10 + assert request.vector == [0.1, 0.2, 0.3] + + def test_query_request_with_scan_factor(self): + request = IndexRequestFactory.query_request( + top_k=10, vector=[0.1, 0.2, 0.3], scan_factor=2.0 + ) + assert request.scan_factor == 2.0 + assert "max_candidates" not in request._data_store + + def test_query_request_with_max_candidates(self): + request = IndexRequestFactory.query_request( + top_k=10, vector=[0.1, 0.2, 0.3], max_candidates=500 + ) + assert request.max_candidates == 500 + assert "scan_factor" not in request._data_store + + def test_query_request_with_scan_factor_and_max_candidates(self): + request = IndexRequestFactory.query_request( + top_k=10, vector=[0.1, 0.2, 0.3], scan_factor=1.5, max_candidates=1000 + ) + assert request.scan_factor == 1.5 + assert request.max_candidates == 1000 + + def test_query_request_omits_scan_factor_and_max_candidates_when_none(self): + request = IndexRequestFactory.query_request(top_k=10, vector=[0.1, 0.2, 0.3]) + assert "scan_factor" not in request._data_store + assert "max_candidates" not in request._data_store diff --git a/tests/unit/test_index.py b/tests/unit/test_index.py index 6117d444..a1fc6886 100644 --- a/tests/unit/test_index.py +++ b/tests/unit/test_index.py @@ -410,6 +410,60 @@ def test_query_with_positional_args(self, mocker): in str(e.value) ) + def test_query_with_scan_factor_and_max_candidates(self, mocker): + mocker.patch.object(self.index._vector_api, "query_vectors", autospec=True) + self.index.query(top_k=10, vector=self.vals1, scan_factor=2.0, max_candidates=500) + self.index._vector_api.query_vectors.assert_called_once_with( + oai.QueryRequest(top_k=10, vector=self.vals1, scan_factor=2.0, max_candidates=500) + ) + + def test_query_with_scan_factor_only(self, mocker): + mocker.patch.object(self.index._vector_api, "query_vectors", autospec=True) + self.index.query(top_k=10, vector=self.vals1, scan_factor=0.5) + request = self.index._vector_api.query_vectors.call_args[0][0] + assert request.scan_factor == 0.5 + assert "max_candidates" not in request._data_store + + def test_query_with_max_candidates_only(self, mocker): + mocker.patch.object(self.index._vector_api, "query_vectors", autospec=True) + self.index.query(top_k=10, vector=self.vals1, max_candidates=1000) + request = self.index._vector_api.query_vectors.call_args[0][0] + assert request.max_candidates == 1000 + assert "scan_factor" not in request._data_store + + # endregion + + # region: query_namespaces tests + + def test_query_namespaces_forwards_scan_factor_and_max_candidates(self, mocker): + import json + from concurrent.futures import Future + from unittest.mock import MagicMock + + def make_future(**kwargs): + raw = MagicMock() + raw.data = json.dumps( + {"matches": [], "namespace": kwargs.get("namespace", ""), "usage": {"readUnits": 0}} + ).encode() + f = Future() + f.set_result(raw) + return f + + mock_query = mocker.patch.object(self.index, "query", side_effect=make_future) + self.index.query_namespaces( + vector=self.vals1, + namespaces=["ns1", "ns2"], + metric="cosine", + top_k=5, + scan_factor=3.0, + max_candidates=750, + ) + calls = mock_query.call_args_list + assert len(calls) == 2 + for call in calls: + assert call.kwargs["scan_factor"] == 3.0 + assert call.kwargs["max_candidates"] == 750 + # endregion # region: delete tests @@ -719,6 +773,57 @@ async def test_asyncio_update_withDryRunAndAllParams_updateWithDryRunAndAllParam # endregion + # region: asyncio query tests + + @pytest.mark.asyncio + async def test_asyncio_query_with_scan_factor_and_max_candidates(self, mocker): + asyncio_index = _IndexAsyncio(api_key="asdf", host="https://test.pinecone.io") + mock_response = oai.QueryResponse(matches=[], namespace="test", _check_type=False) + mocker.patch.object( + asyncio_index._vector_api, + "query_vectors", + return_value=mock_response, + new_callable=mocker.AsyncMock, + ) + await asyncio_index.query(top_k=10, vector=self.vals1, scan_factor=2.0, max_candidates=500) + asyncio_index._vector_api.query_vectors.assert_called_once_with( + oai.QueryRequest(top_k=10, vector=self.vals1, scan_factor=2.0, max_candidates=500) + ) + + @pytest.mark.asyncio + async def test_asyncio_query_namespaces_forwards_scan_factor_and_max_candidates(self, mocker): + import json + from unittest.mock import MagicMock + from pinecone.openapi_support.rest_utils import RESTResponse + + asyncio_index = _IndexAsyncio(api_key="asdf", host="https://test.pinecone.io") + + async def async_query_side_effect(**kwargs): + raw = MagicMock(spec=RESTResponse) + raw.data = json.dumps( + {"matches": [], "namespace": kwargs.get("namespace", ""), "usage": {"readUnits": 0}} + ).encode() + return raw + + mock_query = mocker.patch.object( + asyncio_index, "_query", side_effect=async_query_side_effect + ) + await asyncio_index.query_namespaces( + vector=self.vals1, + namespaces=["ns1", "ns2"], + metric="cosine", + top_k=5, + scan_factor=3.0, + max_candidates=750, + ) + calls = mock_query.call_args_list + assert len(calls) == 2 + for call in calls: + assert call.kwargs["scan_factor"] == 3.0 + assert call.kwargs["max_candidates"] == 750 + + # endregion + # region: describe index tests def test_describeIndexStats_callWithoutFilter_CalledWithoutFilter(self, mocker):