pinecone 5.4.0.dev3__tar.gz → 5.4.0.dev5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/PKG-INFO +2 -2
  2. pinecone-5.4.0.dev5/pinecone/__version__ +1 -0
  3. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/control/pinecone.py +8 -0
  4. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/shared/api_client.py +67 -6
  5. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/shared/configuration.py +20 -0
  6. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/data/index.py +138 -6
  7. pinecone-5.4.0.dev5/pinecone/data/query_results_aggregator.py +193 -0
  8. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/__init__.py +4 -0
  9. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/base.py +10 -0
  10. pinecone-5.4.0.dev5/pinecone/grpc/future.py +86 -0
  11. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/index_grpc.py +70 -10
  12. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/pinecone.py +3 -1
  13. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/utils.py +9 -4
  14. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pyproject.toml +6 -2
  15. pinecone-5.4.0.dev3/pinecone/__version__ +0 -1
  16. pinecone-5.4.0.dev3/pinecone/grpc/future.py +0 -34
  17. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/LICENSE.txt +0 -0
  18. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/README.md +0 -0
  19. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/__init__.py +0 -0
  20. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/config/__init__.py +0 -0
  21. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/config/config.py +0 -0
  22. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/config/openapi.py +0 -0
  23. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/config/pinecone_config.py +0 -0
  24. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/control/__init__.py +0 -0
  25. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/control/index_host_store.py +0 -0
  26. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/control/langchain_import_warnings.py +0 -0
  27. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/control/repr_overrides.py +0 -0
  28. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/__init__.py +0 -0
  29. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/grpc/protos/vector_service_pb2.py +0 -0
  30. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/grpc/protos/vector_service_pb2.pyi +0 -0
  31. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/grpc/protos/vector_service_pb2_grpc.py +0 -0
  32. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/__init__.py +0 -0
  33. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/api/__init__.py +0 -0
  34. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/api/inference_api.py +0 -0
  35. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/api/manage_indexes_api.py +0 -0
  36. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/apis/__init__.py +0 -0
  37. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/__init__.py +0 -0
  38. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/collection_list.py +0 -0
  39. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/collection_model.py +0 -0
  40. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/configure_index_request.py +0 -0
  41. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/configure_index_request_spec.py +0 -0
  42. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/configure_index_request_spec_pod.py +0 -0
  43. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/create_collection_request.py +0 -0
  44. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/create_index_request.py +0 -0
  45. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/deletion_protection.py +0 -0
  46. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/embed_request.py +0 -0
  47. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/embed_request_inputs.py +0 -0
  48. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/embed_request_parameters.py +0 -0
  49. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/embedding.py +0 -0
  50. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/embeddings_list.py +0 -0
  51. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/embeddings_list_usage.py +0 -0
  52. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/error_response.py +0 -0
  53. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/error_response_error.py +0 -0
  54. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/index_list.py +0 -0
  55. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/index_model.py +0 -0
  56. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/index_model_spec.py +0 -0
  57. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/index_model_status.py +0 -0
  58. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/index_spec.py +0 -0
  59. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/pod_spec.py +0 -0
  60. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/pod_spec_metadata_config.py +0 -0
  61. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/model/serverless_spec.py +0 -0
  62. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/control/models/__init__.py +0 -0
  63. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/__init__.py +0 -0
  64. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/api/__init__.py +0 -0
  65. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/api/data_plane_api.py +0 -0
  66. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/apis/__init__.py +0 -0
  67. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/__init__.py +0 -0
  68. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/delete_request.py +0 -0
  69. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/describe_index_stats_request.py +0 -0
  70. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/describe_index_stats_response.py +0 -0
  71. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/fetch_response.py +0 -0
  72. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/list_item.py +0 -0
  73. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/list_response.py +0 -0
  74. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/namespace_summary.py +0 -0
  75. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/pagination.py +0 -0
  76. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/protobuf_any.py +0 -0
  77. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/protobuf_null_value.py +0 -0
  78. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/query_request.py +0 -0
  79. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/query_response.py +0 -0
  80. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/query_vector.py +0 -0
  81. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/rpc_status.py +0 -0
  82. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/scored_vector.py +0 -0
  83. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/single_query_results.py +0 -0
  84. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/sparse_values.py +0 -0
  85. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/update_request.py +0 -0
  86. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/upsert_request.py +0 -0
  87. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/upsert_response.py +0 -0
  88. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/usage.py +0 -0
  89. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/model/vector.py +0 -0
  90. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/data/models/__init__.py +0 -0
  91. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/shared/__init__.py +0 -0
  92. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/shared/exceptions.py +0 -0
  93. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/shared/model_utils.py +0 -0
  94. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core/openapi/shared/rest.py +0 -0
  95. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/__init__.py +0 -0
  96. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/api/__init__.py +0 -0
  97. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/api/manage_indexes_api.py +0 -0
  98. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/apis/__init__.py +0 -0
  99. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/__init__.py +0 -0
  100. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/collection_list.py +0 -0
  101. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/collection_model.py +0 -0
  102. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/configure_index_request.py +0 -0
  103. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/configure_index_request_spec.py +0 -0
  104. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/configure_index_request_spec_pod.py +0 -0
  105. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/create_collection_request.py +0 -0
  106. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/create_index_request.py +0 -0
  107. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/deletion_protection.py +0 -0
  108. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/error_response.py +0 -0
  109. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/error_response_error.py +0 -0
  110. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/index_list.py +0 -0
  111. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/index_model.py +0 -0
  112. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/index_model_spec.py +0 -0
  113. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/index_model_status.py +0 -0
  114. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/index_spec.py +0 -0
  115. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/pod_spec.py +0 -0
  116. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/pod_spec_metadata_config.py +0 -0
  117. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/model/serverless_spec.py +0 -0
  118. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_control/models/__init__.py +0 -0
  119. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/__init__.py +0 -0
  120. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/api/__init__.py +0 -0
  121. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/api/bulk_operations_api.py +0 -0
  122. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/api/vector_operations_api.py +0 -0
  123. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/apis/__init__.py +0 -0
  124. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/__init__.py +0 -0
  125. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/delete_request.py +0 -0
  126. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/describe_index_stats_request.py +0 -0
  127. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/describe_index_stats_response.py +0 -0
  128. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/fetch_response.py +0 -0
  129. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/import_error_mode.py +0 -0
  130. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/import_list_response.py +0 -0
  131. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/import_model.py +0 -0
  132. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/list_item.py +0 -0
  133. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/list_response.py +0 -0
  134. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/namespace_summary.py +0 -0
  135. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/pagination.py +0 -0
  136. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/protobuf_any.py +0 -0
  137. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/protobuf_null_value.py +0 -0
  138. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/query_request.py +0 -0
  139. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/query_response.py +0 -0
  140. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/query_vector.py +0 -0
  141. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/rpc_status.py +0 -0
  142. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/scored_vector.py +0 -0
  143. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/single_query_results.py +0 -0
  144. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/sparse_values.py +0 -0
  145. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/start_import_request.py +0 -0
  146. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/start_import_response.py +0 -0
  147. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/update_request.py +0 -0
  148. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/upsert_request.py +0 -0
  149. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/upsert_response.py +0 -0
  150. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/usage.py +0 -0
  151. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/model/vector.py +0 -0
  152. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/db_data/models/__init__.py +0 -0
  153. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/shared/__init__.py +0 -0
  154. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/shared/api_client.py +0 -0
  155. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/shared/configuration.py +0 -0
  156. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/shared/exceptions.py +0 -0
  157. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/shared/model_utils.py +0 -0
  158. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/core_ea/openapi/shared/rest.py +0 -0
  159. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/data/__init__.py +0 -0
  160. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/data/errors.py +0 -0
  161. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/data/features/__init__.py +0 -0
  162. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/data/features/bulk_import.py +0 -0
  163. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/data/sparse_vector_factory.py +0 -0
  164. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/data/vector_factory.py +0 -0
  165. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/deprecation_warnings.py +0 -0
  166. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/exceptions/__init__.py +0 -0
  167. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/exceptions/exceptions.py +0 -0
  168. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/channel_factory.py +0 -0
  169. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/config.py +0 -0
  170. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/grpc_runner.py +0 -0
  171. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/retry.py +0 -0
  172. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/sparse_values_factory.py +0 -0
  173. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/grpc/vector_factory_grpc.py +0 -0
  174. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/__init__.py +0 -0
  175. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/collection_description.py +0 -0
  176. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/collection_list.py +0 -0
  177. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/index_description.py +0 -0
  178. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/index_list.py +0 -0
  179. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/index_model.py +0 -0
  180. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/list_response.py +0 -0
  181. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/pod_spec.py +0 -0
  182. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/models/serverless_spec.py +0 -0
  183. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/__init__.py +0 -0
  184. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/check_kwargs.py +0 -0
  185. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/constants.py +0 -0
  186. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/convert_to_list.py +0 -0
  187. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/deprecation_notice.py +0 -0
  188. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/docslinks.py +0 -0
  189. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/error_handling.py +0 -0
  190. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/fix_tuple_length.py +0 -0
  191. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/normalize_host.py +0 -0
  192. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/parse_args.py +0 -0
  193. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/repr_overrides.py +0 -0
  194. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/setup_openapi_client.py +0 -0
  195. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/user_agent.py +0 -0
  196. {pinecone-5.4.0.dev3 → pinecone-5.4.0.dev5}/pinecone/utils/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pinecone
3
- Version: 5.4.0.dev3
3
+ Version: 5.4.0.dev5
4
4
  Summary: Pinecone client and SDK
5
5
  Home-page: https://www.pinecone.io
6
6
  License: Apache-2.0
@@ -35,7 +35,7 @@ Requires-Dist: grpcio (>=1.59.0) ; (python_version >= "3.11" and python_version
35
35
  Requires-Dist: lz4 (>=3.1.3) ; extra == "grpc"
36
36
  Requires-Dist: pinecone-plugin-inference (>=2.0.0,<3.0.0)
37
37
  Requires-Dist: pinecone-plugin-interface (>=0.0.7,<0.0.8)
38
- Requires-Dist: protobuf (>=5.28,<6.0) ; extra == "grpc"
38
+ Requires-Dist: protobuf (>=4.25,<5.0) ; extra == "grpc"
39
39
  Requires-Dist: protoc-gen-openapiv2 (>=0.0.1,<0.0.2) ; extra == "grpc"
40
40
  Requires-Dist: python-dateutil (>=2.5.3)
41
41
  Requires-Dist: tqdm (>=4.64.1)
@@ -0,0 +1 @@
1
+ 5.4.0.dev5
@@ -765,6 +765,14 @@ class Pinecone:
765
765
  # Now you're ready to perform data operations
766
766
  index.query(vector=[...], top_k=10)
767
767
  ```
768
+
769
+ Arguments:
770
+ name: The name of the index to target. If you specify the name of the index, the client will
771
+ fetch the host url from the Pinecone control plane.
772
+ host: The host url of the index to target. If you specify the host url, the client will use
773
+ the host url directly without making any additional calls to the control plane.
774
+ pool_threads: The number of threads to use when making parallel requests by calling index methods with optional kwarg async_req=True, or using methods that make use of parallelism automatically such as query_namespaces(). Default: 1
775
+ connection_pool_maxsize: The maximum number of connections to keep in the connection pool. Default: 5 * multiprocessing.cpu_count()
768
776
  """
769
777
  if name == "" and host == "":
770
778
  raise ValueError("Either name or host must be specified")
@@ -2,12 +2,31 @@ import json
2
2
  import atexit
3
3
  import mimetypes
4
4
  from multiprocessing.pool import ThreadPool
5
+ from concurrent.futures import ThreadPoolExecutor
5
6
  import io
6
7
  import os
7
8
  import re
8
9
  import typing
9
10
  from urllib.parse import quote
10
11
  from urllib3.fields import RequestField
12
+ import time
13
+ import random
14
+
15
+ def retry_api_call(
16
+ func, args=(), kwargs={}, retries=3, backoff=1, jitter=0.5
17
+ ):
18
+ attempts = 0
19
+ while attempts < retries:
20
+ try:
21
+ return func(*args, **kwargs) # Attempt to call __call_api
22
+ except Exception as e:
23
+ attempts += 1
24
+ if attempts >= retries:
25
+ print(f"API call failed after {attempts} attempts: {e}")
26
+ raise # Re-raise exception if retries are exhausted
27
+ sleep_time = backoff * (2 ** (attempts - 1)) + random.uniform(0, jitter)
28
+ # print(f"Retrying ({attempts}/{retries}) in {sleep_time:.2f} seconds after error: {e}")
29
+ time.sleep(sleep_time)
11
30
 
12
31
 
13
32
  from pinecone.core.openapi.shared import rest
@@ -52,6 +71,7 @@ class ApiClient(object):
52
71
  """
53
72
 
54
73
  _pool = None
74
+ _threadpool_executor = None
55
75
 
56
76
  def __init__(self, configuration=None, header_name=None, header_value=None, cookie=None, pool_threads=1):
57
77
  if configuration is None:
@@ -74,6 +94,9 @@ class ApiClient(object):
74
94
  self.close()
75
95
 
76
96
  def close(self):
97
+ if self._threadpool_executor:
98
+ self._threadpool_executor.shutdown()
99
+ self._threadpool_executor = None
77
100
  if self._pool:
78
101
  self._pool.close()
79
102
  self._pool.join()
@@ -91,6 +114,12 @@ class ApiClient(object):
91
114
  self._pool = ThreadPool(self.pool_threads)
92
115
  return self._pool
93
116
 
117
+ @property
118
+ def threadpool_executor(self):
119
+ if self._threadpool_executor is None:
120
+ self._threadpool_executor = ThreadPoolExecutor(max_workers=self.pool_threads)
121
+ return self._threadpool_executor
122
+
94
123
  @property
95
124
  def user_agent(self):
96
125
  """User agent for this API client"""
@@ -316,6 +345,7 @@ class ApiClient(object):
316
345
  response_type: typing.Optional[typing.Tuple[typing.Any]] = None,
317
346
  auth_settings: typing.Optional[typing.List[str]] = None,
318
347
  async_req: typing.Optional[bool] = None,
348
+ async_threadpool_executor: typing.Optional[bool] = None,
319
349
  _return_http_data_only: typing.Optional[bool] = None,
320
350
  collection_formats: typing.Optional[typing.Dict[str, str]] = None,
321
351
  _preload_content: bool = True,
@@ -376,8 +406,9 @@ class ApiClient(object):
376
406
  If parameter async_req is False or missing,
377
407
  then the method will return the response directly.
378
408
  """
379
- if not async_req:
380
- return self.__call_api(
409
+ if async_threadpool_executor:
410
+ return self.threadpool_executor.submit(
411
+ self.__call_api,
381
412
  resource_path,
382
413
  method,
383
414
  path_params,
@@ -396,9 +427,8 @@ class ApiClient(object):
396
427
  _check_type,
397
428
  )
398
429
 
399
- return self.pool.apply_async(
400
- self.__call_api,
401
- (
430
+ if not async_req:
431
+ return self.__call_api(
402
432
  resource_path,
403
433
  method,
404
434
  path_params,
@@ -415,7 +445,35 @@ class ApiClient(object):
415
445
  _request_timeout,
416
446
  _host,
417
447
  _check_type,
418
- ),
448
+ )
449
+
450
+ return self.pool.apply_async(
451
+ retry_api_call,
452
+ args=(
453
+ self.__call_api, # Pass the API call function as the first argument
454
+ (
455
+ resource_path,
456
+ method,
457
+ path_params,
458
+ query_params,
459
+ header_params,
460
+ body,
461
+ post_params,
462
+ files,
463
+ response_type,
464
+ auth_settings,
465
+ _return_http_data_only,
466
+ collection_formats,
467
+ _preload_content,
468
+ _request_timeout,
469
+ _host,
470
+ _check_type,
471
+ ),
472
+ {}, # empty kwargs dictionary
473
+ 3, # retries
474
+ 1, # backoff time
475
+ 0.5 # jitter
476
+ )
419
477
  )
420
478
 
421
479
  def request(
@@ -665,6 +723,7 @@ class Endpoint(object):
665
723
  self.params_map["all"].extend(
666
724
  [
667
725
  "async_req",
726
+ "async_threadpool_executor",
668
727
  "_host_index",
669
728
  "_preload_content",
670
729
  "_request_timeout",
@@ -679,6 +738,7 @@ class Endpoint(object):
679
738
  self.openapi_types = root_map["openapi_types"]
680
739
  extra_types = {
681
740
  "async_req": (bool,),
741
+ "async_threadpool_executor": (bool, ),
682
742
  "_host_index": (none_type, int),
683
743
  "_preload_content": (bool,),
684
744
  "_request_timeout": (none_type, float, (float,), [float], int, (int,), [int]),
@@ -828,6 +888,7 @@ class Endpoint(object):
828
888
  response_type=self.settings["response_type"],
829
889
  auth_settings=self.settings["auth"],
830
890
  async_req=kwargs["async_req"],
891
+ async_threadpool_executor=kwargs.get("async_threadpool_executor", None),
831
892
  _check_type=kwargs["_check_return_type"],
832
893
  _return_http_data_only=kwargs["_return_http_data_only"],
833
894
  _preload_content=kwargs["_preload_content"],
@@ -469,3 +469,23 @@ class Configuration(object):
469
469
  """Fix base path."""
470
470
  self._base_path = value
471
471
  self.server_index = None
472
+
473
+ def __repr__(self):
474
+ attrs = [
475
+ f"host={self.host}",
476
+ f"api_key=***",
477
+ f"api_key_prefix={self.api_key_prefix}",
478
+ f"access_token={self.access_token}",
479
+ f"connection_pool_maxsize={self.connection_pool_maxsize}",
480
+ f"username={self.username}",
481
+ f"password={self.password}",
482
+ f"discard_unknown_keys={self.discard_unknown_keys}",
483
+ f"disabled_client_side_validations={self.disabled_client_side_validations}",
484
+ f"server_index={self.server_index}",
485
+ f"server_variables={self.server_variables}",
486
+ f"server_operation_index={self.server_operation_index}",
487
+ f"server_operation_variables={self.server_operation_variables}",
488
+ f"ssl_ca_cert={self.ssl_ca_cert}",
489
+
490
+ ]
491
+ return f"Configuration({', '.join(attrs)})"
@@ -1,6 +1,7 @@
1
1
  from tqdm.autonotebook import tqdm
2
2
 
3
3
  import logging
4
+ import json
4
5
  from typing import Union, List, Optional, Dict, Any
5
6
 
6
7
  from pinecone.config import ConfigBuilder
@@ -33,6 +34,10 @@ from ..utils import (
33
34
  )
34
35
  from .features.bulk_import import ImportFeatureMixin
35
36
  from .vector_factory import VectorFactory
37
+ from .query_results_aggregator import QueryResultsAggregator, QueryNamespacesResults
38
+
39
+ from multiprocessing.pool import ApplyResult
40
+ from concurrent.futures import as_completed
36
41
 
37
42
  from pinecone_plugin_interface import load_and_install as install_plugins
38
43
 
@@ -65,6 +70,7 @@ _OPENAPI_ENDPOINT_PARAMS = (
65
70
  "_check_return_type",
66
71
  "_host_index",
67
72
  "async_req",
73
+ "async_threadpool_executor",
68
74
  )
69
75
 
70
76
 
@@ -97,16 +103,19 @@ class Index(ImportFeatureMixin):
97
103
  **kwargs,
98
104
  )
99
105
 
100
- self._config = ConfigBuilder.build(
106
+ self.config = ConfigBuilder.build(
101
107
  api_key=api_key, host=host, additional_headers=additional_headers, **kwargs
102
108
  )
103
- self._openapi_config = ConfigBuilder.build_openapi_config(self._config, openapi_config)
109
+ self._openapi_config = ConfigBuilder.build_openapi_config(self.config, openapi_config)
104
110
  self._pool_threads = pool_threads
105
111
 
112
+ if kwargs.get("connection_pool_maxsize", None):
113
+ self._openapi_config.connection_pool_maxsize = kwargs.get("connection_pool_maxsize")
114
+
106
115
  self._vector_api = setup_openapi_client(
107
116
  api_client_klass=ApiClient,
108
117
  api_klass=DataPlaneApi,
109
- config=self._config,
118
+ config=self.config,
110
119
  openapi_config=self._openapi_config,
111
120
  pool_threads=self._pool_threads,
112
121
  api_version=API_VERSION,
@@ -121,7 +130,7 @@ class Index(ImportFeatureMixin):
121
130
  # try block just in case to make sure a bad plugin doesn't
122
131
  # halt client initialization.
123
132
  openapi_client_builder = build_plugin_setup_client(
124
- config=self._config,
133
+ config=self.config,
125
134
  openapi_config=self._openapi_config,
126
135
  pool_threads=self._pool_threads,
127
136
  )
@@ -387,7 +396,7 @@ class Index(ImportFeatureMixin):
387
396
  Union[SparseValues, Dict[str, Union[List[float], List[int]]]]
388
397
  ] = None,
389
398
  **kwargs,
390
- ) -> QueryResponse:
399
+ ) -> Union[QueryResponse, ApplyResult]:
391
400
  """
392
401
  The Query operation searches a namespace, using a query vector.
393
402
  It retrieves the ids of the most similar items in a namespace, along with their similarity scores.
@@ -429,6 +438,39 @@ class Index(ImportFeatureMixin):
429
438
  and namespace name.
430
439
  """
431
440
 
441
+ response = self._query(
442
+ *args,
443
+ top_k=top_k,
444
+ vector=vector,
445
+ id=id,
446
+ namespace=namespace,
447
+ filter=filter,
448
+ include_values=include_values,
449
+ include_metadata=include_metadata,
450
+ sparse_vector=sparse_vector,
451
+ **kwargs,
452
+ )
453
+
454
+ if kwargs.get("async_req", False) or kwargs.get("async_threadpool_executor", False):
455
+ return response
456
+ else:
457
+ return parse_query_response(response)
458
+
459
+ def _query(
460
+ self,
461
+ *args,
462
+ top_k: int,
463
+ vector: Optional[List[float]] = None,
464
+ id: Optional[str] = None,
465
+ namespace: Optional[str] = None,
466
+ filter: Optional[Dict[str, Union[str, float, int, bool, List, dict]]] = None,
467
+ include_values: Optional[bool] = None,
468
+ include_metadata: Optional[bool] = None,
469
+ sparse_vector: Optional[
470
+ Union[SparseValues, Dict[str, Union[List[float], List[int]]]]
471
+ ] = None,
472
+ **kwargs,
473
+ ) -> QueryResponse:
432
474
  if len(args) > 0:
433
475
  raise ValueError(
434
476
  "The argument order for `query()` has changed; please use keyword arguments instead of positional arguments. Example: index.query(vector=[0.1, 0.2, 0.3], top_k=10, namespace='my_namespace')"
@@ -453,6 +495,7 @@ class Index(ImportFeatureMixin):
453
495
  ("sparse_vector", sparse_vector),
454
496
  ]
455
497
  )
498
+
456
499
  response = self._vector_api.query(
457
500
  QueryRequest(
458
501
  **args_dict,
@@ -461,7 +504,96 @@ class Index(ImportFeatureMixin):
461
504
  ),
462
505
  **{k: v for k, v in kwargs.items() if k in _OPENAPI_ENDPOINT_PARAMS},
463
506
  )
464
- return parse_query_response(response)
507
+ return response
508
+
509
+ @validate_and_convert_errors
510
+ def query_namespaces(
511
+ self,
512
+ vector: List[float],
513
+ namespaces: List[str],
514
+ top_k: Optional[int] = None,
515
+ filter: Optional[Dict[str, Union[str, float, int, bool, List, dict]]] = None,
516
+ include_values: Optional[bool] = None,
517
+ include_metadata: Optional[bool] = None,
518
+ sparse_vector: Optional[
519
+ Union[SparseValues, Dict[str, Union[List[float], List[int]]]]
520
+ ] = None,
521
+ **kwargs,
522
+ ) -> QueryNamespacesResults:
523
+ """The query_namespaces() method is used to make a query to multiple namespaces in parallel and combine the results into one result set.
524
+
525
+ Since several asynchronous calls are made on your behalf when calling this method, you will need to tune the pool_threads and connection_pool_maxsize parameter of the Index constructor to suite your workload.
526
+
527
+ Examples:
528
+
529
+ ```python
530
+ from pinecone import Pinecone
531
+
532
+ pc = Pinecone(api_key="your-api-key")
533
+ index = pc.Index(
534
+ host="index-name",
535
+ pool_threads=32,
536
+ connection_pool_maxsize=32
537
+ )
538
+
539
+ query_vec = [0.1, 0.2, 0.3] # An embedding that matches the index dimension
540
+ combined_results = index.query_namespaces(
541
+ vector=query_vec,
542
+ namespaces=['ns1', 'ns2', 'ns3', 'ns4'],
543
+ top_k=10,
544
+ filter={'genre': {"$eq": "drama"}},
545
+ include_values=True,
546
+ include_metadata=True
547
+ )
548
+ for vec in combined_results.matches:
549
+ print(vec.id, vec.score)
550
+ print(combined_results.usage)
551
+ ```
552
+
553
+ Args:
554
+ vector (List[float]): The query vector, must be the same length as the dimension of the index being queried.
555
+ namespaces (List[str]): The list of namespaces to query.
556
+ top_k (Optional[int], optional): The number of results you would like to request from each namespace. Defaults to 10.
557
+ filter (Optional[Dict[str, Union[str, float, int, bool, List, dict]]], optional): Pass an optional filter to filter results based on metadata. Defaults to None.
558
+ include_values (Optional[bool], optional): Boolean field indicating whether vector values should be included with results. Defaults to None.
559
+ include_metadata (Optional[bool], optional): Boolean field indicating whether vector metadata should be included with results. Defaults to None.
560
+ 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.
561
+
562
+ Returns:
563
+ QueryNamespacesResults: A QueryNamespacesResults object containing the combined results from all namespaces, as well as the combined usage cost in read units.
564
+ """
565
+ if namespaces is None or len(namespaces) == 0:
566
+ raise ValueError("At least one namespace must be specified")
567
+ if len(vector) == 0:
568
+ raise ValueError("Query vector must not be empty")
569
+
570
+ overall_topk = top_k if top_k is not None else 10
571
+ aggregator = QueryResultsAggregator(top_k=overall_topk)
572
+
573
+ target_namespaces = set(namespaces) # dedup namespaces
574
+ async_futures = [
575
+ self.query(
576
+ vector=vector,
577
+ namespace=ns,
578
+ top_k=overall_topk,
579
+ filter=filter,
580
+ include_values=include_values,
581
+ include_metadata=include_metadata,
582
+ sparse_vector=sparse_vector,
583
+ async_threadpool_executor=True,
584
+ _preload_content=False,
585
+ **kwargs,
586
+ )
587
+ for ns in target_namespaces
588
+ ]
589
+
590
+ for result in as_completed(async_futures):
591
+ raw_result = result.result()
592
+ response = json.loads(raw_result.data.decode("utf-8"))
593
+ aggregator.add_results(response)
594
+
595
+ final_results = aggregator.get_results()
596
+ return final_results
465
597
 
466
598
  @validate_and_convert_errors
467
599
  def update(
@@ -0,0 +1,193 @@
1
+ from typing import List, Tuple, Optional, Any, Dict
2
+ import json
3
+ import heapq
4
+ from pinecone.core.openapi.data.models import Usage
5
+ from pinecone.core.openapi.data.models import QueryResponse as OpenAPIQueryResponse
6
+
7
+ from dataclasses import dataclass, asdict
8
+
9
+
10
+ @dataclass
11
+ class ScoredVectorWithNamespace:
12
+ namespace: str
13
+ score: float
14
+ id: str
15
+ values: List[float]
16
+ sparse_values: dict
17
+ metadata: dict
18
+
19
+ def __init__(self, aggregate_results_heap_tuple: Tuple[float, int, object, str]):
20
+ json_vector = aggregate_results_heap_tuple[2]
21
+ self.namespace = aggregate_results_heap_tuple[3]
22
+ self.id = json_vector.get("id") # type: ignore
23
+ self.score = json_vector.get("score") # type: ignore
24
+ self.values = json_vector.get("values") # type: ignore
25
+ self.sparse_values = json_vector.get("sparse_values", None) # type: ignore
26
+ self.metadata = json_vector.get("metadata", None) # type: ignore
27
+
28
+ def __getitem__(self, key):
29
+ if hasattr(self, key):
30
+ return getattr(self, key)
31
+ else:
32
+ raise KeyError(f"'{key}' not found in ScoredVectorWithNamespace")
33
+
34
+ def get(self, key, default=None):
35
+ return getattr(self, key, default)
36
+
37
+ def __repr__(self):
38
+ return json.dumps(self._truncate(asdict(self)), indent=4)
39
+
40
+ def __json__(self):
41
+ return self._truncate(asdict(self))
42
+
43
+ def _truncate(self, obj, max_items=2):
44
+ """
45
+ Recursively traverse and truncate lists that exceed max_items length.
46
+ Only display the "... X more" message if at least 2 elements are hidden.
47
+ """
48
+ if obj is None:
49
+ return None # Skip None values
50
+ elif isinstance(obj, list):
51
+ filtered_list = [self._truncate(i, max_items) for i in obj if i is not None]
52
+ if len(filtered_list) > max_items:
53
+ # Show the truncation message only if more than 1 item is hidden
54
+ remaining_items = len(filtered_list) - max_items
55
+ if remaining_items > 1:
56
+ return filtered_list[:max_items] + [f"... {remaining_items} more"]
57
+ else:
58
+ # If only 1 item remains, show it
59
+ return filtered_list
60
+ return filtered_list
61
+ elif isinstance(obj, dict):
62
+ # Recursively process dictionaries, omitting None values
63
+ return {k: self._truncate(v, max_items) for k, v in obj.items() if v is not None}
64
+ return obj
65
+
66
+
67
+ @dataclass
68
+ class QueryNamespacesResults:
69
+ usage: Usage
70
+ matches: List[ScoredVectorWithNamespace]
71
+
72
+ def __getitem__(self, key):
73
+ if hasattr(self, key):
74
+ return getattr(self, key)
75
+ else:
76
+ raise KeyError(f"'{key}' not found in QueryNamespacesResults")
77
+
78
+ def get(self, key, default=None):
79
+ return getattr(self, key, default)
80
+
81
+ def __repr__(self):
82
+ return json.dumps(
83
+ {
84
+ "usage": self.usage.to_dict(),
85
+ "matches": [match.__json__() for match in self.matches],
86
+ },
87
+ indent=4,
88
+ )
89
+
90
+
91
+ class QueryResultsAggregregatorNotEnoughResultsError(Exception):
92
+ def __init__(self):
93
+ super().__init__(
94
+ "Cannot interpret results without at least two matches. In order to aggregate results from multiple queries, top_k must be greater than 1 in order to correctly infer the similarity metric from scores."
95
+ )
96
+
97
+
98
+ class QueryResultsAggregatorInvalidTopKError(Exception):
99
+ def __init__(self, top_k: int):
100
+ super().__init__(
101
+ f"Invalid top_k value {top_k}. To aggregate results from multiple queries the top_k must be at least 2."
102
+ )
103
+
104
+
105
+ class QueryResultsAggregator:
106
+ def __init__(self, top_k: int):
107
+ if top_k < 2:
108
+ raise QueryResultsAggregatorInvalidTopKError(top_k)
109
+ self.top_k = top_k
110
+ self.usage_read_units = 0
111
+ self.heap: List[Tuple[float, int, object, str]] = []
112
+ self.insertion_counter = 0
113
+ self.is_dotproduct = None
114
+ self.read = False
115
+ self.final_results: Optional[QueryNamespacesResults] = None
116
+
117
+ def _is_dotproduct_index(self, matches):
118
+ # The interpretation of the score depends on the similar metric used.
119
+ # Unlike other index types, in indexes configured for dotproduct,
120
+ # a higher score is better. We have to infer this is the case by inspecting
121
+ # the order of the scores in the results.
122
+ for i in range(1, len(matches)):
123
+ if matches[i].get("score") > matches[i - 1].get("score"): # Found an increase
124
+ return False
125
+ return True
126
+
127
+ def _dotproduct_heap_item(self, match, ns):
128
+ return (match.get("score"), -self.insertion_counter, match, ns)
129
+
130
+ def _non_dotproduct_heap_item(self, match, ns):
131
+ return (-match.get("score"), -self.insertion_counter, match, ns)
132
+
133
+ def _process_matches(self, matches, ns, heap_item_fn):
134
+ for match in matches:
135
+ self.insertion_counter += 1
136
+ if len(self.heap) < self.top_k:
137
+ heapq.heappush(self.heap, heap_item_fn(match, ns))
138
+ else:
139
+ # Assume we have dotproduct scores sorted in descending order
140
+ if self.is_dotproduct and match["score"] < self.heap[0][0]:
141
+ # No further matches can improve the top-K heap
142
+ break
143
+ elif not self.is_dotproduct and match["score"] > -self.heap[0][0]:
144
+ # No further matches can improve the top-K heap
145
+ break
146
+ heapq.heappushpop(self.heap, heap_item_fn(match, ns))
147
+
148
+ def add_results(self, results: Dict[str, Any]):
149
+ if self.read:
150
+ # This is mainly just to sanity check in test cases which get quite confusing
151
+ # if you read results twice due to the heap being emptied when constructing
152
+ # the ordered results.
153
+ raise ValueError("Results have already been read. Cannot add more results.")
154
+
155
+ matches = results.get("matches", [])
156
+ ns: str = results.get("namespace", "")
157
+ if isinstance(results, OpenAPIQueryResponse):
158
+ self.usage_read_units += results.usage.read_units
159
+ else:
160
+ self.usage_read_units += results.get("usage", {}).get("readUnits", 0)
161
+
162
+ if len(matches) == 0:
163
+ return
164
+
165
+ if self.is_dotproduct is None:
166
+ if len(matches) == 1:
167
+ # This condition should match the second time we add results containing
168
+ # only one match. We need at least two matches in a single response in order
169
+ # to infer the similarity metric
170
+ raise QueryResultsAggregregatorNotEnoughResultsError()
171
+ self.is_dotproduct = self._is_dotproduct_index(matches)
172
+
173
+ if self.is_dotproduct:
174
+ self._process_matches(matches, ns, self._dotproduct_heap_item)
175
+ else:
176
+ self._process_matches(matches, ns, self._non_dotproduct_heap_item)
177
+
178
+ def get_results(self) -> QueryNamespacesResults:
179
+ if self.read:
180
+ if self.final_results is not None:
181
+ return self.final_results
182
+ else:
183
+ # I don't think this branch can ever actually be reached, but the type checker disagrees
184
+ raise ValueError("Results have already been read. Cannot get results again.")
185
+ self.read = True
186
+
187
+ self.final_results = QueryNamespacesResults(
188
+ usage=Usage(read_units=self.usage_read_units),
189
+ matches=[
190
+ ScoredVectorWithNamespace(heapq.heappop(self.heap)) for _ in range(len(self.heap))
191
+ ][::-1],
192
+ )
193
+ return self.final_results
@@ -47,20 +47,24 @@ index.query(vector=[...], top_k=10)
47
47
  from .index_grpc import GRPCIndex
48
48
  from .pinecone import PineconeGRPC
49
49
  from .config import GRPCClientConfig
50
+ from .future import PineconeGrpcFuture
50
51
 
51
52
  from pinecone.core.grpc.protos.vector_service_pb2 import (
52
53
  Vector as GRPCVector,
53
54
  SparseValues as GRPCSparseValues,
54
55
  Vector,
55
56
  SparseValues,
57
+ DeleteResponse as GRPCDeleteResponse,
56
58
  )
57
59
 
58
60
  __all__ = [
59
61
  "GRPCIndex",
60
62
  "PineconeGRPC",
63
+ "GRPCDeleteResponse",
61
64
  "GRPCClientConfig",
62
65
  "GRPCVector",
63
66
  "GRPCSparseValues",
64
67
  "Vector",
65
68
  "SparseValues",
69
+ "PineconeGrpcFuture",
66
70
  ]
@@ -10,6 +10,7 @@ from .channel_factory import GrpcChannelFactory
10
10
  from pinecone import Config
11
11
  from .config import GRPCClientConfig
12
12
  from .grpc_runner import GrpcRunner
13
+ from concurrent.futures import ThreadPoolExecutor
13
14
 
14
15
  from pinecone_plugin_interface import load_and_install as install_plugins
15
16
 
@@ -29,10 +30,12 @@ class GRPCIndexBase(ABC):
29
30
  config: Config,
30
31
  channel: Optional[Channel] = None,
31
32
  grpc_config: Optional[GRPCClientConfig] = None,
33
+ pool_threads: Optional[int] = None,
32
34
  _endpoint_override: Optional[str] = None,
33
35
  ):
34
36
  self.config = config
35
37
  self.grpc_client_config = grpc_config or GRPCClientConfig()
38
+ self.pool_threads = pool_threads
36
39
 
37
40
  self._endpoint_override = _endpoint_override
38
41
 
@@ -58,6 +61,13 @@ class GRPCIndexBase(ABC):
58
61
  except Exception as e:
59
62
  _logger.error(f"Error loading plugins in GRPCIndex: {e}")
60
63
 
64
+ @property
65
+ def threadpool_executor(self):
66
+ if self._pool is None:
67
+ pt = self.pool_threads or 10
68
+ self._pool = ThreadPoolExecutor(max_workers=pt)
69
+ return self._pool
70
+
61
71
  @property
62
72
  @abstractmethod
63
73
  def stub_class(self):