diff --git a/pinecone/db_data/index.py b/pinecone/db_data/index.py index 537dac94..8cf549d9 100644 --- a/pinecone/db_data/index.py +++ b/pinecone/db_data/index.py @@ -31,7 +31,6 @@ UpsertResponse, UpdateResponse, ) -from .interfaces import IndexInterface from .request_factory import IndexRequestFactory from .types import ( SparseVectorTypedDict, @@ -131,7 +130,7 @@ def __getattr__(self, name: str) -> Any: return getattr(self._apply_result, name) -class Index(PluginAware, IndexInterface): +class Index(PluginAware): """ A client for interacting with a Pinecone index via REST API. For improved performance, use the Pinecone GRPC index client. diff --git a/pinecone/db_data/index_asyncio.py b/pinecone/db_data/index_asyncio.py index 0cb24342..a8bc1185 100644 --- a/pinecone/db_data/index_asyncio.py +++ b/pinecone/db_data/index_asyncio.py @@ -7,7 +7,6 @@ import asyncio import json -from .index_asyncio_interface import IndexAsyncioInterface from .query_results_aggregator import QueryResultsAggregator from typing import List, Dict, Any, Literal, AsyncIterator, TYPE_CHECKING from typing_extensions import Self @@ -115,7 +114,7 @@ def parse_query_response(response: OpenAPIQueryResponse) -> QueryResponse: ) -class _IndexAsyncio(IndexAsyncioInterface): +class _IndexAsyncio: """ The `IndexAsyncio` class provides an asynchronous interface to interact with Pinecone indexes. @@ -315,6 +314,138 @@ async def upsert( show_progress: bool = True, **kwargs, ) -> UpsertResponse: + """ + Args: + vectors (Union[list[Vector], list[VectorTuple], list[VectorTupleWithMetadata], list[VectorTypedDict]]): A list of vectors to upsert. + namespace (str): The namespace to write to. If not specified, the default namespace is used. [optional] + batch_size (int): The number of vectors to upsert in each batch. + If not specified, all vectors will be upserted in a single batch. [optional] + show_progress (bool): Whether to show a progress bar using tqdm. + Applied only if batch_size is provided. Default is True. + + Returns: + `UpsertResponse`, includes the number of vectors upserted. + + + The upsert operation writes vectors into a namespace. + If a new value is upserted for an existing vector id, it will overwrite the previous value. + + To upsert in parallel follow `this link `_. + + **Upserting dense vectors** + + .. admonition:: Note + + The dimension of each dense vector must match the dimension of the index. + + A vector can be represented in a variety of ways. + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + # A Vector object + await idx.upsert( + namespace = 'my-namespace', + vectors = [ + Vector(id='id1', values=[0.1, 0.2, 0.3, 0.4], metadata={'metadata_key': 'metadata_value'}), + ] + ) + + # A vector tuple + await idx.upsert( + namespace = 'my-namespace', + vectors = [ + ('id1', [0.1, 0.2, 0.3, 0.4]), + ] + ) + + # A vector tuple with metadata + await idx.upsert( + namespace = 'my-namespace', + vectors = [ + ('id1', [0.1, 0.2, 0.3, 0.4], {'metadata_key': 'metadata_value'}), + ] + ) + + # A vector dictionary + await idx.upsert( + namespace = 'my-namespace', + vectors = [ + {"id": 1, "values": [0.1, 0.2, 0.3, 0.4], "metadata": {"metadata_key": "metadata_value"}}, + ] + ) + + asyncio.run(main()) + + + **Upserting sparse vectors** + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector, SparseValues + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + # A Vector object + await idx.upsert( + namespace = 'my-namespace', + vectors = [ + Vector(id='id1', sparse_values=SparseValues(indices=[1, 2], values=[0.2, 0.4])), + ] + ) + + # A dictionary + await idx.upsert( + namespace = 'my-namespace', + vectors = [ + {"id": 1, "sparse_values": {"indices": [1, 2], "values": [0.2, 0.4]}}, + ] + ) + + asyncio.run(main()) + + + **Batch upsert** + + If you have a large number of vectors, you can upsert them in batches. + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector, SparseValues + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + + await idx.upsert( + namespace = 'my-namespace', + vectors = [ + {'id': 'id1', 'values': [0.1, 0.2, 0.3, 0.4]}, + {'id': 'id2', 'values': [0.2, 0.3, 0.4, 0.5]}, + {'id': 'id3', 'values': [0.3, 0.4, 0.5, 0.6]}, + {'id': 'id4', 'values': [0.4, 0.5, 0.6, 0.7]}, + {'id': 'id5', 'values': [0.5, 0.6, 0.7, 0.8]}, + # More vectors here + ], + batch_size = 50 + ) + + asyncio.run(main()) + + + **Visual progress bar with tqdm** + + To see a progress bar when upserting in batches, you will need to separately install the `tqdm` package. + If `tqdm` is present, the client will detect and use it to display progress when `show_progress=True`. + """ _check_type = kwargs.pop("_check_type", True) if batch_size is None: @@ -389,6 +520,7 @@ def vec_builder(v): async def upsert_from_dataframe( self, df, namespace: str | None = None, batch_size: int = 500, show_progress: bool = True ): + """This method has not been implemented yet for the IndexAsyncio class.""" raise NotImplementedError("upsert_from_dataframe is not implemented for asyncio") @validate_and_convert_errors @@ -400,6 +532,64 @@ async def delete( filter: FilterTypedDict | None = None, **kwargs, ) -> dict[str, Any]: + """ + Args: + ids (list[str]): Vector ids to delete [optional] + delete_all (bool): This indicates that all vectors in the index namespace should be deleted.. [optional] + Default is False. + namespace (str): The namespace to delete vectors from [optional] + If not specified, the default namespace is used. + filter (dict[str, Union[str, float, int, bool, List, dict]]): + If specified, the metadata filter here will be used to select the vectors to delete. + This is mutually exclusive with specifying ids to delete in the ids param or using delete_all=True. + See `metadata filtering _` [optional] + + + The Delete operation deletes vectors from the index, from a single namespace. + + No error is raised if the vector id does not exist. + + Note: For any delete call, if namespace is not specified, the default namespace `""` is used. + Since the delete operation does not error when ids are not present, this means you may not receive + an error if you delete from the wrong namespace. + + Delete can occur in the following mutual exclusive ways: + + 1. Delete by ids from a single namespace + 2. Delete all vectors from a single namespace by setting delete_all to True + 3. Delete all vectors from a single namespace by specifying a metadata filter + (note that for this option delete all must be set to False) + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector, SparseValues + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + # Delete specific ids + await idx.delete( + ids=['id1', 'id2'], + namespace='my_namespace' + ) + + # Delete everything in a namespace + await idx.delete( + delete_all=True, + namespace='my_namespace' + ) + + # Delete by metadata filter + await idx.delete( + filter={'key': 'value'}, + namespace='my_namespace' + ) + + asyncio.run(main()) + + Returns: An empty dictionary if the delete operation was successful. + """ _check_type = kwargs.pop("_check_type", False) args_dict = parse_non_empty_args( [("ids", ids), ("delete_all", delete_all), ("namespace", namespace), ("filter", filter)] @@ -423,6 +613,38 @@ async def delete( @validate_and_convert_errors async def fetch(self, ids: list[str], namespace: str | None = None, **kwargs) -> FetchResponse: + """ + The fetch operation looks up and returns vectors, by ID, from a single namespace. + The returned vectors include the vector data and/or metadata. + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector, SparseValues + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + # Fetch specific ids in namespace + fetched = await idx.fetch( + ids=['id1', 'id2'], + namespace='my_namespace' + ) + for vec_id in fetched.vectors: + vector = fetched.vectors[vec_id] + print(vector.id) + print(vector.metadata) + print(vector.values) + + asyncio.run(main()) + + Args: + ids (list[str]): The vector IDs to fetch. + namespace (str): The namespace to fetch vectors from. + If not specified, the default namespace is used. [optional] + + Returns: FetchResponse object which contains the list of Vector objects, and namespace name. + """ args_dict = parse_non_empty_args([("namespace", namespace)]) result = await self._vector_api.fetch_vectors(ids=ids, **args_dict, **kwargs) # Copy response info from OpenAPI response if present @@ -537,6 +759,112 @@ async def query( sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, **kwargs, ) -> QueryResponse: + """ + The Query operation searches a namespace, using a query vector. + It retrieves the ids of the most similar items in a namespace, along with their similarity scores. + + **Querying with dense vectors** + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector, SparseValues + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + query_embedding = [0.1, 0.2, 0.3, ...] # An embedding that matches the index dimension + + # Query by vector values + results = await idx.query( + vector=query_embedding, + top_k=10, + filter={'genre': {"$eq": "drama"}}, # Optionally filter by metadata + namespace='my_namespace', + include_values=False, + include_metadata=True + ) + + # Query using vector id (the values from this stored vector will be used to query) + results = await idx.query( + id='1', + top_k=10, + filter={"year": {"$gt": 2000}}, + namespace='my_namespace', + ) + + asyncio.run(main()) + + + **Query with sparse vectors** + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector, SparseValues + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + query_embedding = [0.1, 0.2, 0.3, ...] # An embedding that matches the index dimension + + # Query by vector values + results = await idx.query( + vector=query_embedding, + top_k=10, + filter={'genre': {"$eq": "drama"}}, # Optionally filter by metadata + namespace='my_namespace', + include_values=False, + include_metadata=True + ) + + # Query using vector id (the values from this stored vector will be used to query) + results = await idx.query( + id='1', + top_k=10, + filter={"year": {"$gt": 2000}}, + namespace='my_namespace', + ) + + asyncio.run(main()) + + Examples: + + .. code-block:: python + + >>> index.query(vector=[1, 2, 3], top_k=10, namespace='my_namespace') + >>> index.query(id='id1', top_k=10, namespace='my_namespace') + >>> index.query(vector=[1, 2, 3], top_k=10, namespace='my_namespace', filter={'key': 'value'}) + >>> index.query(id='id1', top_k=10, namespace='my_namespace', include_metadata=True, include_values=True) + >>> index.query(vector=[1, 2, 3], sparse_vector={'indices': [1, 2], 'values': [0.2, 0.4]}, + >>> top_k=10, namespace='my_namespace') + >>> index.query(vector=[1, 2, 3], sparse_vector=SparseValues([1, 2], [0.2, 0.4]), + >>> top_k=10, namespace='my_namespace') + + Args: + vector (list[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] + id (str): The unique ID of the vector to be used as a query vector. + Each `query()` request can contain only one of the parameters + `vector` or `id`. [optional] + top_k (int): The number of results to return for each query. Must be an integer greater than 1. + namespace (str): The namespace to fetch vectors from. + If not specified, the default namespace is used. [optional] + filter (dict[str, Union[str, float, int, bool, List, dict]): + The filter to apply. You can use vector metadata to limit your search. + See `metadata filtering _` [optional] + include_values (bool): Indicates whether vector values are included in the response. + If omitted the server will use the default value of False [optional] + include_metadata (bool): Indicates whether metadata is included in the response as well as the ids. + If omitted the server will use the default value of False [optional] + 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. + + Returns: QueryResponse object which contains the list of the closest vectors as ScoredVector objects, + and namespace name. + """ response = await self._query( *args, top_k=top_k, @@ -600,6 +928,51 @@ async def query_namespaces( sparse_vector: (SparseValues | SparseVectorTypedDict) | 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. + + Args: + vector (list[float]): The query vector, must be the same length as the dimension of the index being queried. + namespaces (list[str]): The list of namespaces to query. + top_k (Optional[int], optional): The number of results you would like to request from each namespace. Defaults to 10. + 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. + 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. + + Returns: + QueryNamespacesResults: A QueryNamespacesResults object containing the combined results from all namespaces, as well as the combined usage cost in read units. + + Examples: + + .. code-block:: python + + import asyncio + from pinecone import Pinecone + + async def main(): + pc = Pinecone(api_key="your-api-key") + idx = pc.IndexAsyncio( + host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io", + ) + + query_vec = [0.1, 0.2, 0.3] # An embedding that matches the index dimension + combined_results = await idx.query_namespaces( + vector=query_vec, + namespaces=['ns1', 'ns2', 'ns3', 'ns4'], + top_k=10, + filter={'genre': {"$eq": "drama"}}, + include_values=True, + include_metadata=True + ) + for vec in combined_results.matches: + print(vec.id, vec.score) + print(combined_results.usage) + + await idx.close() + + asyncio.run(main()) + + """ if namespaces is None or len(namespaces) == 0: raise ValueError("At least one namespace must be specified") if sparse_vector is None and vector is not None and len(vector) == 0: @@ -654,6 +1027,113 @@ async def update( dry_run: bool | None = None, **kwargs, ) -> UpdateResponse: + """ + The Update operation updates vectors in a namespace. + + This method supports two update modes: + + 1. **Single vector update by ID**: Provide `id` to update a specific vector. + - Updates the vector with the given ID + - If `values` is included, it will overwrite the previous vector values + - If `set_metadata` is included, the metadata will be merged with existing metadata on the vector. + Fields specified in `set_metadata` will overwrite existing fields with the same key, while + fields not in `set_metadata` will remain unchanged. + + 2. **Bulk update by metadata filter**: Provide `filter` to update all vectors matching the filter criteria. + - Updates all vectors in the namespace that match the filter expression + - Useful for updating metadata across multiple vectors at once + - If `set_metadata` is included, the metadata will be merged with existing metadata on each vector. + Fields specified in `set_metadata` will overwrite existing fields with the same key, while + fields not in `set_metadata` will remain unchanged. + - The response includes `matched_records` indicating how many vectors were updated + + Either `id` or `filter` must be provided (but not both in the same call). + + Examples: + + **Single vector update by ID:** + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector, SparseValues + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + # Update vector values + await idx.update( + id='id1', + values=[0.1, 0.2, 0.3, ...], + namespace='my_namespace' + ) + + # Update metadata + await idx.update( + id='id1', + set_metadata={'key': 'value'}, + namespace='my_namespace' + ) + + # Update sparse values + await idx.update( + id='id1', + sparse_values={'indices': [1, 2], 'values': [0.2, 0.4]}, + namespace='my_namespace' + ) + + # Update sparse values with SparseValues object + await idx.update( + id='id1', + sparse_values=SparseValues(indices=[234781, 5432], values=[0.2, 0.4]), + namespace='my_namespace' + ) + + **Bulk update by metadata filter:** + + .. code-block:: python + + # Update metadata for all vectors matching the filter + response = await idx.update( + set_metadata={'status': 'active'}, + filter={'genre': {'$eq': 'drama'}}, + namespace='my_namespace' + ) + print(f"Updated {response.matched_records} vectors") + # Preview how many vectors would be updated (dry run) + response = await idx.update( + set_metadata={'status': 'active'}, + filter={'genre': {'$eq': 'drama'}}, + namespace='my_namespace', + dry_run=True + ) + print(f"Would update {response.matched_records} vectors") + + asyncio.run(main()) + + Args: + id (str): Vector's unique id. Required for single vector updates. Must not be provided when using filter. [optional] + values (list[float]): Vector values to set. [optional] + set_metadata (dict[str, Union[str, float, int, bool, list[int], list[float], list[str]]]]): + Metadata to merge with existing metadata on the vector(s). Fields specified will overwrite + existing fields with the same key, while fields not specified will remain unchanged. [optional] + namespace (str): Namespace name where to update the vector(s). [optional] + sparse_values: (dict[str, Union[list[float], list[int]]]): Sparse values to update for the 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] + filter (dict[str, Union[str, float, int, bool, List, dict]]): A metadata filter expression. + When provided, updates all vectors in the namespace that match the filter criteria. + See `metadata filtering _`. + Must not be provided when using id. Either `id` or `filter` must be provided. [optional] + dry_run (bool): If `True`, return the number of records that match the `filter` without executing + the update. Only meaningful when using `filter` (not with `id`). Useful for previewing + the impact of a bulk update before applying changes. Defaults to `False`. [optional] + + Returns: + UpdateResponse: An UpdateResponse object. When using filter-based updates, the response includes + `matched_records` indicating the number of vectors that were updated (or would be updated if + `dry_run=True`). + """ # Validate that exactly one of id or filter is provided if id is None and filter is None: raise ValueError("Either 'id' or 'filter' must be provided to update vectors.") @@ -701,6 +1181,30 @@ async def update( async def describe_index_stats( self, filter: FilterTypedDict | None = None, **kwargs ) -> DescribeIndexStatsResponse: + """ + The DescribeIndexStats operation returns statistics about the index's contents. + For example: The vector count per namespace and the number of dimensions. + + Args: + filter (dict[str, Union[str, float, int, bool, List, dict]]): + If this parameter is present, the operation only returns statistics for vectors that satisfy the filter. + See `metadata filtering _` [optional] + + Returns: DescribeIndexStatsResponse object which contains stats about the index. + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, Vector, SparseValues + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + print(await idx.describe_index_stats()) + + asyncio.run(main()) + + """ from typing import cast result = await self._vector_api.describe_index_stats( @@ -718,6 +1222,31 @@ async def list_paginated( namespace: str | None = None, **kwargs, ) -> ListResponse: + """ + The list_paginated operation finds vectors based on an id prefix within a single namespace. + It returns matching ids in a paginated form, with a pagination token to fetch the next page of results. + This id list can then be passed to fetch or delete operations, depending on your use case. + + Consider using the `list` method to avoid having to handle pagination tokens manually. + + Examples: + >>> results = index.list_paginated(prefix='99', limit=5, namespace='my_namespace') + >>> [v.id for v in results.vectors] + ['99', '990', '991', '992', '993'] + >>> results.pagination.next + eyJza2lwX3Bhc3QiOiI5OTMiLCJwcmVmaXgiOiI5OSJ9 + >>> next_results = index.list_paginated(prefix='99', limit=5, namespace='my_namespace', pagination_token=results.pagination.next) + + Args: + prefix (Optional[str]): The id prefix to match. If unspecified, an empty string prefix will + be used with the effect of listing all ids in a namespace [optional] + limit (Optional[int]): The maximum number of ids to return. If unspecified, the server will use a default value. [optional] + pagination_token (Optional[str]): A token needed to fetch the next page of results. This token is returned + in the response if additional results are available. [optional] + namespace (Optional[str]): The namespace to fetch vectors from. If not specified, the default namespace is used. [optional] + + Returns: ListResponse object which contains the list of ids, the namespace name, pagination information, and usage showing the number of read_units consumed. + """ args_dict = IndexRequestFactory.list_paginated_args( prefix=prefix, limit=limit, @@ -732,6 +1261,26 @@ async def list_paginated( @validate_and_convert_errors async def list(self, **kwargs) -> AsyncIterator[list[str]]: + """ + The list operation accepts all of the same arguments as list_paginated, and returns a generator that yields + a list of the matching vector ids in each page of results. It automatically handles pagination tokens on your + behalf. + + Examples: + >>> for ids in index.list(prefix='99', limit=5, namespace='my_namespace'): + >>> print(ids) + ['99', '990', '991', '992', '993'] + ['994', '995', '996', '997', '998'] + ['999'] + + Args: + prefix (Optional[str]): The id prefix to match. If unspecified, an empty string prefix will + be used with the effect of listing all ids in a namespace [optional] + limit (Optional[int]): The maximum number of ids to return. If unspecified, the server will use a default value. [optional] + pagination_token (Optional[str]): A token needed to fetch the next page of results. This token is returned + in the response if additional results are available. [optional] + namespace (Optional[str]): The namespace to fetch vectors from. If not specified, the default namespace is used. [optional] + """ done = False while not done: results = await self.list_paginated(**kwargs) @@ -744,6 +1293,86 @@ async def list(self, **kwargs) -> AsyncIterator[list[str]]: done = True async def upsert_records(self, namespace: str, records: List[Dict]) -> UpsertResponse: + """ + :param namespace: The namespace of the index to upsert records to. + :type namespace: str, required + :param records: The records to upsert into the index. + :type records: list[Dict], required + + Upsert records to a namespace. A record is a dictionary that contains eitiher an `id` or `_id` + field along with other fields that will be stored as metadata. The `id` or `_id` field is used + as the unique identifier for the record. At least one field in the record should correspond to + a field mapping in the index's embed configuration. + + When records are upserted, Pinecone converts mapped fields into embeddings and upserts them into + the specified namespacce of the index. + + .. code-block:: python + + import asyncio + from pinecone import ( + Pinecone, + CloudProvider, + AwsRegion, + EmbedModel + IndexEmbed + ) + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + # upsert records + await idx.upsert_records( + namespace="my-namespace", + records=[ + { + "_id": "test1", + "my_text_field": "Apple is a popular fruit known for its sweetness and crisp texture.", + }, + { + "_id": "test2", + "my_text_field": "The tech company Apple is known for its innovative products like the iPhone.", + }, + { + "_id": "test3", + "my_text_field": "Many people enjoy eating apples as a healthy snack.", + }, + { + "_id": "test4", + "my_text_field": "Apple Inc. has revolutionized the tech industry with its sleek designs and user-friendly interfaces.", + }, + { + "_id": "test5", + "my_text_field": "An apple a day keeps the doctor away, as the saying goes.", + }, + { + "_id": "test6", + "my_text_field": "Apple Computer Company was founded on April 1, 1976, by Steve Jobs, Steve Wozniak, and Ronald Wayne as a partnership.", + }, + ], + ) + + from pinecone import SearchQuery, SearchRerank, RerankModel + + # search for similar records + response = await idx.search_records( + namespace="my-namespace", + query=SearchQuery( + inputs={ + "text": "Apple corporation", + }, + top_k=3, + ), + rerank=SearchRerank( + model=RerankModel.Bge_Reranker_V2_M3, + rank_fields=["my_text_field"], + top_n=3, + ), + ) + + asyncio.run(main()) + + """ args = IndexRequestFactory.upsert_records_args(namespace=namespace, records=records) # Use _return_http_data_only=False to get headers for LSN extraction result = await self._vector_api.upsert_records_namespace( @@ -776,6 +1405,92 @@ async def search( rerank: (SearchRerankTypedDict | SearchRerank) | None = None, fields: List[str] | None = ["*"], # Default to returning all fields ) -> SearchRecordsResponse: + """ + :param namespace: The namespace in the index to search. + :type namespace: str, required + :param query: The SearchQuery to use for the search. The query can include a ``match_terms`` field + to specify which terms must be present in the text of each search hit. The match_terms + should be a dict with ``strategy`` (str) and ``terms`` (list[str]) keys, e.g. + ``{"strategy": "all", "terms": ["term1", "term2"]}``. Currently only "all" strategy + is supported, which means all specified terms must be present. + **Note:** match_terms is only supported for sparse indexes with integrated embedding + configured to use the pinecone-sparse-english-v0 model. + :type query: Union[Dict, SearchQuery], required + :param rerank: The SearchRerank to use with the search request. + :type rerank: Union[Dict, SearchRerank], optional + :return: The records that match the search. + + Search for records. + + This operation converts a query to a vector embedding and then searches a namespace. You + can optionally provide a reranking operation as part of the search. + + .. code-block:: python + + import asyncio + from pinecone import ( + Pinecone, + CloudProvider, + AwsRegion, + EmbedModel + IndexEmbed + ) + + async def main(): + pc = Pinecone() + async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: + # upsert records + await idx.upsert_records( + namespace="my-namespace", + records=[ + { + "_id": "test1", + "my_text_field": "Apple is a popular fruit known for its sweetness and crisp texture.", + }, + { + "_id": "test2", + "my_text_field": "The tech company Apple is known for its innovative products like the iPhone.", + }, + { + "_id": "test3", + "my_text_field": "Many people enjoy eating apples as a healthy snack.", + }, + { + "_id": "test4", + "my_text_field": "Apple Inc. has revolutionized the tech industry with its sleek designs and user-friendly interfaces.", + }, + { + "_id": "test5", + "my_text_field": "An apple a day keeps the doctor away, as the saying goes.", + }, + { + "_id": "test6", + "my_text_field": "Apple Computer Company was founded on April 1, 1976, by Steve Jobs, Steve Wozniak, and Ronald Wayne as a partnership.", + }, + ], + ) + + from pinecone import SearchQuery, SearchRerank, RerankModel + + # search for similar records + response = await idx.search_records( + namespace="my-namespace", + query=SearchQuery( + inputs={ + "text": "Apple corporation", + }, + top_k=3, + ), + rerank=SearchRerank( + model=RerankModel.Bge_Reranker_V2_M3, + rank_fields=["my_text_field"], + top_n=3, + ), + ) + + asyncio.run(main()) + + """ if namespace is None: raise Exception("Namespace is required when searching records") @@ -793,6 +1508,7 @@ async def search_records( rerank: (SearchRerankTypedDict | SearchRerank) | None = None, fields: List[str] | None = ["*"], # Default to returning all fields ) -> SearchRecordsResponse: + """Alias of the search() method.""" return await self.search(namespace, query=query, rerank=rerank, fields=fields) def _openapi_kwargs(self, kwargs: dict[str, Any]) -> dict[str, Any]: @@ -912,16 +1628,67 @@ async def cancel_import(self, id: str): async def create_namespace( self, name: str, schema: dict[str, Any] | None = None, **kwargs ) -> "NamespaceDescription": + """Create a namespace in a serverless index. + + Args: + name (str): The name of the namespace to create + schema (Optional[dict[str, Any]]): Optional schema configuration for the namespace as a dictionary. [optional] + + Returns: + NamespaceDescription: Information about the created namespace including vector count + + Create a namespace in a serverless index. For guidance and examples, see + `Manage namespaces `_. + + **Note:** This operation is not supported for pod-based indexes. + + Examples: + + .. code-block:: python + + >>> # Create a namespace with just a name + >>> import asyncio + >>> from pinecone import Pinecone + >>> + >>> async def main(): + ... pc = Pinecone() + ... async with pc.IndexAsyncio(host="example-index-dojoi3u.svc.eu-west1-gcp.pinecone.io") as idx: + ... namespace = await idx.create_namespace(name="my-namespace") + ... print(f"Created namespace: {namespace.name}, Vector count: {namespace.vector_count}") + >>> + >>> asyncio.run(main()) + + >>> # Create a namespace with schema configuration + >>> from pinecone.core.openapi.db_data.model.create_namespace_request_schema import CreateNamespaceRequestSchema + >>> schema = CreateNamespaceRequestSchema(fields={...}) + >>> namespace = await idx.create_namespace(name="my-namespace", schema=schema) + """ return await self.namespace.create(name=name, schema=schema, **kwargs) @validate_and_convert_errors @require_kwargs async def describe_namespace(self, namespace: str, **kwargs) -> "NamespaceDescription": + """Describe a namespace within an index, showing the vector count within the namespace. + + Args: + namespace (str): The namespace to describe + + Returns: + NamespaceDescription: Information about the namespace including vector count + """ return await self.namespace.describe(namespace=namespace, **kwargs) @validate_and_convert_errors @require_kwargs async def delete_namespace(self, namespace: str, **kwargs) -> dict[str, Any]: + """Delete a namespace from an index. + + Args: + namespace (str): The namespace to delete + + Returns: + dict[str, Any]: Response from the delete operation + """ from typing import cast result = await self.namespace.delete(namespace=namespace, **kwargs) @@ -929,9 +1696,24 @@ async def delete_namespace(self, namespace: str, **kwargs) -> dict[str, Any]: @validate_and_convert_errors @require_kwargs - async def list_namespaces( # type: ignore[override, misc] # mypy limitation: async generators in abstract methods + async def list_namespaces( self, limit: int | None = None, **kwargs ) -> AsyncIterator[ListNamespacesResponse]: + """List all namespaces in an index. This method automatically handles pagination to return all results. + + Args: + limit (Optional[int]): The maximum number of namespaces to return. If unspecified, the server will use a default value. [optional] + + Returns: + ``ListNamespacesResponse``: Object containing the list of namespaces. + + Examples: + .. code-block:: python + >>> async for namespace in index.list_namespaces(limit=5): + ... print(f"Namespace: {namespace.name}, Vector count: {namespace.vector_count}") + Namespace: namespace1, Vector count: 1000 + Namespace: namespace2, Vector count: 2000 + """ async for namespace in self.namespace.list(limit=limit, **kwargs): yield namespace @@ -940,6 +1722,25 @@ async def list_namespaces( # type: ignore[override, misc] # mypy limitation: a async def list_namespaces_paginated( self, limit: int | None = None, pagination_token: str | None = None, **kwargs ) -> ListNamespacesResponse: + """List all namespaces in an index with pagination support. The response includes pagination information if there are more results available. + + Consider using the ``list_namespaces`` method to avoid having to handle pagination tokens manually. + + Args: + limit (Optional[int]): The maximum number of namespaces to return. If unspecified, the server will use a default value. [optional] + pagination_token (Optional[str]): A token needed to fetch the next page of results. This token is returned + in the response if additional results are available. [optional] + + Returns: + ``ListNamespacesResponse``: Object containing the list of namespaces and pagination information. + + Examples: + .. code-block:: python + >>> results = await index.list_namespaces_paginated(limit=5) + >>> results.pagination.next + eyJza2lwX3Bhc3QiOiI5OTMiLCJwcmVmaXgiOiI5OSJ9 + >>> next_results = await index.list_namespaces_paginated(limit=5, pagination_token=results.pagination.next) + """ return await self.namespace.list_paginated( limit=limit, pagination_token=pagination_token, **kwargs ) diff --git a/pinecone/db_data/index_asyncio_interface.py b/pinecone/db_data/index_asyncio_interface.py deleted file mode 100644 index 01eeafcd..00000000 --- a/pinecone/db_data/index_asyncio_interface.py +++ /dev/null @@ -1,1043 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import List, Dict, Any, AsyncIterator, Literal - -from pinecone.core.openapi.db_data.models import ( - IndexDescription as DescribeIndexStatsResponse, - ListResponse, - SearchRecordsResponse, - NamespaceDescription, - ListNamespacesResponse, -) -from .query_results_aggregator import QueryNamespacesResults -from .types import ( - VectorTypedDict, - SparseVectorTypedDict, - VectorMetadataTypedDict, - FilterTypedDict, - VectorTuple, - VectorTupleWithMetadata, - SearchQueryTypedDict, - SearchRerankTypedDict, -) -from .dataclasses import ( - Vector, - SparseValues, - SearchQuery, - SearchRerank, - FetchResponse, - FetchByMetadataResponse, - QueryResponse, - UpsertResponse, - UpdateResponse, -) -from pinecone.utils import require_kwargs - - -class IndexAsyncioInterface(ABC): - @abstractmethod - async def upsert( - self, - vectors: ( - list[Vector] | list[VectorTuple] | list[VectorTupleWithMetadata] | list[VectorTypedDict] - ), - namespace: str | None = None, - batch_size: int | None = None, - show_progress: bool = True, - **kwargs, - ) -> UpsertResponse: - """ - Args: - vectors (Union[list[Vector], list[VectorTuple], list[VectorTupleWithMetadata], list[VectorTypedDict]]): A list of vectors to upsert. - namespace (str): The namespace to write to. If not specified, the default namespace is used. [optional] - batch_size (int): The number of vectors to upsert in each batch. - If not specified, all vectors will be upserted in a single batch. [optional] - show_progress (bool): Whether to show a progress bar using tqdm. - Applied only if batch_size is provided. Default is True. - - Returns: - `UpsertResponse`, includes the number of vectors upserted. - - - The upsert operation writes vectors into a namespace. - If a new value is upserted for an existing vector id, it will overwrite the previous value. - - To upsert in parallel follow `this link `_. - - **Upserting dense vectors** - - .. admonition:: Note - - The dimension of each dense vector must match the dimension of the index. - - A vector can be represented in a variety of ways. - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - # A Vector object - await idx.upsert( - namespace = 'my-namespace', - vectors = [ - Vector(id='id1', values=[0.1, 0.2, 0.3, 0.4], metadata={'metadata_key': 'metadata_value'}), - ] - ) - - # A vector tuple - await idx.upsert( - namespace = 'my-namespace', - vectors = [ - ('id1', [0.1, 0.2, 0.3, 0.4]), - ] - ) - - # A vector tuple with metadata - await idx.upsert( - namespace = 'my-namespace', - vectors = [ - ('id1', [0.1, 0.2, 0.3, 0.4], {'metadata_key': 'metadata_value'}), - ] - ) - - # A vector dictionary - await idx.upsert( - namespace = 'my-namespace', - vectors = [ - {"id": 1, "values": [0.1, 0.2, 0.3, 0.4], "metadata": {"metadata_key": "metadata_value"}}, - ] - - asyncio.run(main()) - - - **Upserting sparse vectors** - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector, SparseValues - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - # A Vector object - await idx.upsert( - namespace = 'my-namespace', - vectors = [ - Vector(id='id1', sparse_values=SparseValues(indices=[1, 2], values=[0.2, 0.4])), - ] - ) - - # A dictionary - await idx.upsert( - namespace = 'my-namespace', - vectors = [ - {"id": 1, "sparse_values": {"indices": [1, 2], "values": [0.2, 0.4]}}, - ] - ) - - asyncio.run(main()) - - - **Batch upsert** - - If you have a large number of vectors, you can upsert them in batches. - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector, SparseValues - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - - await idx.upsert( - namespace = 'my-namespace', - vectors = [ - {'id': 'id1', 'values': [0.1, 0.2, 0.3, 0.4]}, - {'id': 'id2', 'values': [0.2, 0.3, 0.4, 0.5]}, - {'id': 'id3', 'values': [0.3, 0.4, 0.5, 0.6]}, - {'id': 'id4', 'values': [0.4, 0.5, 0.6, 0.7]}, - {'id': 'id5', 'values': [0.5, 0.6, 0.7, 0.8]}, - # More vectors here - ], - batch_size = 50 - ) - - asyncio.run(main()) - - - **Visual progress bar with tqdm** - - To see a progress bar when upserting in batches, you will need to separately install the `tqdm` package. - If `tqdm` is present, the client will detect and use it to display progress when `show_progress=True`. - """ - pass - - @abstractmethod - async def upsert_from_dataframe( - self, df, namespace: str | None = None, batch_size: int = 500, show_progress: bool = True - ): - """This method has not been implemented yet for the IndexAsyncio class.""" - pass - - @abstractmethod - async def delete( - self, - ids: list[str] | None = None, - delete_all: bool | None = None, - namespace: str | None = None, - filter: FilterTypedDict | None = None, - **kwargs, - ) -> dict[str, Any]: - """ - Args: - ids (list[str]): Vector ids to delete [optional] - delete_all (bool): This indicates that all vectors in the index namespace should be deleted.. [optional] - Default is False. - namespace (str): The namespace to delete vectors from [optional] - If not specified, the default namespace is used. - filter (dict[str, Union[str, float, int, bool, List, dict]]): - If specified, the metadata filter here will be used to select the vectors to delete. - This is mutually exclusive with specifying ids to delete in the ids param or using delete_all=True. - See `metadata filtering _` [optional] - - - The Delete operation deletes vectors from the index, from a single namespace. - - No error is raised if the vector id does not exist. - - Note: For any delete call, if namespace is not specified, the default namespace `""` is used. - Since the delete operation does not error when ids are not present, this means you may not receive - an error if you delete from the wrong namespace. - - Delete can occur in the following mutual exclusive ways: - - 1. Delete by ids from a single namespace - 2. Delete all vectors from a single namespace by setting delete_all to True - 3. Delete all vectors from a single namespace by specifying a metadata filter - (note that for this option delete all must be set to False) - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector, SparseValues - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - # Delete specific ids - await idx.delete( - ids=['id1', 'id2'], - namespace='my_namespace' - ) - - # Delete everything in a namespace - await idx.delete( - delete_all=True, - namespace='my_namespace' - ) - - # Delete by metadata filter - await idx.delete( - filter={'key': 'value'}, - namespace='my_namespace' - ) - - asyncio.run(main()) - - Returns: An empty dictionary if the delete operation was successful. - """ - pass - - @abstractmethod - async def fetch(self, ids: list[str], namespace: str | None = None, **kwargs) -> FetchResponse: - """ - The fetch operation looks up and returns vectors, by ID, from a single namespace. - The returned vectors include the vector data and/or metadata. - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector, SparseValues - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - # Fetch specific ids in namespace - fetched = await idx.fetch( - ids=['id1', 'id2'], - namespace='my_namespace' - ) - for vec_id in fetched.vectors: - vector = fetched.vectors[vec_id] - print(vector.id) - print(vector.metadata) - print(vector.values) - - asyncio.run(main()) - - Args: - ids (list[str]): The vector IDs to fetch. - namespace (str): The namespace to fetch vectors from. - If not specified, the default namespace is used. [optional] - - Returns: FetchResponse object which contains the list of Vector objects, and namespace name. - """ - pass - - @abstractmethod - async def fetch_by_metadata( - self, - filter: FilterTypedDict, - namespace: str | None = None, - limit: int | None = None, - pagination_token: str | None = None, - **kwargs, - ) -> FetchByMetadataResponse: - """ - Fetch vectors by metadata filter. - - Look up and return vectors by metadata filter from a single namespace. - The returned vectors include the vector data and/or metadata. - - .. code-block:: python - - import asyncio - from pinecone import Pinecone - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-host") as idx: - result = await idx.fetch_by_metadata( - filter={'genre': {'$in': ['comedy', 'drama']}, 'year': {'$eq': 2019}}, - namespace='my_namespace', - limit=50 - ) - for vec_id in result.vectors: - vector = result.vectors[vec_id] - print(vector.id) - print(vector.metadata) - - asyncio.run(main()) - - Args: - filter (dict[str, Union[str, float, int, bool, List, dict]]): - Metadata filter expression to select vectors. - See `metadata filtering _` - namespace (str): The namespace to fetch vectors from. - If not specified, the default namespace is used. [optional] - limit (int): Max number of vectors to return. Defaults to 100. [optional] - pagination_token (str): Pagination token to continue a previous listing operation. [optional] - - Returns: - FetchByMetadataResponse: Object containing the fetched vectors, namespace, usage, and pagination token. - """ - pass - - @abstractmethod - async def query( - self, - *args, - top_k: int, - vector: list[float] | None = None, - id: str | None = None, - namespace: str | None = None, - filter: FilterTypedDict | None = None, - include_values: bool | None = None, - include_metadata: bool | None = None, - sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, - **kwargs, - ) -> QueryResponse: - """ - The Query operation searches a namespace, using a query vector. - It retrieves the ids of the most similar items in a namespace, along with their similarity scores. - - **Querying with dense vectors** - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector, SparseValues - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - query_embedding = [0.1, 0.2, 0.3, ...] # An embedding that matches the index dimension - - # Query by vector values - results = await idx.query( - vector=query_embedding, - top_k=10, - filter={'genre': {"$eq": "drama"}}, # Optionally filter by metadata - namespace='my_namespace', - include_values=False, - include_metadata=True - ) - - # Query using vector id (the values from this stored vector will be used to query) - results = await idx.query( - id='1', - top_k=10, - filter={"year": {"$gt": 2000}}, - namespace='my_namespace', - ) - - asyncio.run(main()) - - - **Query with sparse vectors** - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector, SparseValues - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - query_embedding = [0.1, 0.2, 0.3, ...] # An embedding that matches the index dimension - - # Query by vector values - results = await idx.query( - vector=query_embedding, - top_k=10, - filter={'genre': {"$eq": "drama"}}, # Optionally filter by metadata - namespace='my_namespace', - include_values=False, - include_metadata=True - ) - - # Query using vector id (the values from this stored vector will be used to query) - results = await idx.query( - id='1', - top_k=10, - filter={"year": {"$gt": 2000}}, - namespace='my_namespace', - ) - - asyncio.run(main()) - - Examples: - - .. code-block:: python - - >>> index.query(vector=[1, 2, 3], top_k=10, namespace='my_namespace') - >>> index.query(id='id1', top_k=10, namespace='my_namespace') - >>> index.query(vector=[1, 2, 3], top_k=10, namespace='my_namespace', filter={'key': 'value'}) - >>> index.query(id='id1', top_k=10, namespace='my_namespace', include_metadata=True, include_values=True) - >>> index.query(vector=[1, 2, 3], sparse_vector={'indices': [1, 2], 'values': [0.2, 0.4]}, - >>> top_k=10, namespace='my_namespace') - >>> index.query(vector=[1, 2, 3], sparse_vector=SparseValues([1, 2], [0.2, 0.4]), - >>> top_k=10, namespace='my_namespace') - - Args: - vector (list[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] - id (str): The unique ID of the vector to be used as a query vector. - Each `query()` request can contain only one of the parameters - `vector` or `id`. [optional] - top_k (int): The number of results to return for each query. Must be an integer greater than 1. - namespace (str): The namespace to fetch vectors from. - If not specified, the default namespace is used. [optional] - filter (dict[str, Union[str, float, int, bool, List, dict]): - The filter to apply. You can use vector metadata to limit your search. - See `metadata filtering _` [optional] - include_values (bool): Indicates whether vector values are included in the response. - If omitted the server will use the default value of False [optional] - include_metadata (bool): Indicates whether metadata is included in the response as well as the ids. - If omitted the server will use the default value of False [optional] - 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. - - Returns: QueryResponse object which contains the list of the closest vectors as ScoredVector objects, - and namespace name. - """ - pass - - @abstractmethod - async def query_namespaces( - self, - namespaces: list[str], - metric: Literal["cosine", "euclidean", "dotproduct"], - top_k: int | None = None, - filter: FilterTypedDict | None = None, - include_values: bool | None = None, - include_metadata: bool | None = None, - vector: list[float] | None = None, - sparse_vector: (SparseValues | SparseVectorTypedDict) | 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. - - Args: - vector (list[float]): The query vector, must be the same length as the dimension of the index being queried. - namespaces (list[str]): The list of namespaces to query. - top_k (Optional[int], optional): The number of results you would like to request from each namespace. Defaults to 10. - 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. - 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. - - Returns: - QueryNamespacesResults: A QueryNamespacesResults object containing the combined results from all namespaces, as well as the combined usage cost in read units. - - Examples: - - .. code-block:: python - - import asyncio - from pinecone import Pinecone - - async def main(): - pc = Pinecone(api_key="your-api-key") - idx = pc.IndexAsyncio( - host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io", - ) - - query_vec = [0.1, 0.2, 0.3] # An embedding that matches the index dimension - combined_results = await idx.query_namespaces( - vector=query_vec, - namespaces=['ns1', 'ns2', 'ns3', 'ns4'], - top_k=10, - filter={'genre': {"$eq": "drama"}}, - include_values=True, - include_metadata=True - ) - for vec in combined_results.matches: - print(vec.id, vec.score) - print(combined_results.usage) - - await idx.close() - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def update( - self, - id: str | None = None, - values: list[float] | None = None, - set_metadata: VectorMetadataTypedDict | None = None, - namespace: str | None = None, - sparse_values: (SparseValues | SparseVectorTypedDict) | None = None, - filter: FilterTypedDict | None = None, - dry_run: bool | None = None, - **kwargs, - ) -> UpdateResponse: - """ - The Update operation updates vectors in a namespace. - - This method supports two update modes: - - 1. **Single vector update by ID**: Provide `id` to update a specific vector. - - Updates the vector with the given ID - - If `values` is included, it will overwrite the previous vector values - - If `set_metadata` is included, the metadata will be merged with existing metadata on the vector. - Fields specified in `set_metadata` will overwrite existing fields with the same key, while - fields not in `set_metadata` will remain unchanged. - - 2. **Bulk update by metadata filter**: Provide `filter` to update all vectors matching the filter criteria. - - Updates all vectors in the namespace that match the filter expression - - Useful for updating metadata across multiple vectors at once - - If `set_metadata` is included, the metadata will be merged with existing metadata on each vector. - Fields specified in `set_metadata` will overwrite existing fields with the same key, while - fields not in `set_metadata` will remain unchanged. - - The response includes `matched_records` indicating how many vectors were updated - - Either `id` or `filter` must be provided (but not both in the same call). - - Examples: - - **Single vector update by ID:** - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector, SparseValues - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - # Update vector values - await idx.update( - id='id1', - values=[0.1, 0.2, 0.3, ...], - namespace='my_namespace' - ) - - # Update metadata - await idx.update( - id='id1', - set_metadata={'key': 'value'}, - namespace='my_namespace' - ) - - # Update sparse values - await idx.update( - id='id1', - sparse_values={'indices': [1, 2], 'values': [0.2, 0.4]}, - namespace='my_namespace' - ) - - # Update sparse values with SparseValues object - await idx.update( - id='id1', - sparse_values=SparseValues(indices=[234781, 5432], values=[0.2, 0.4]), - namespace='my_namespace' - ) - - **Bulk update by metadata filter:** - - .. code-block:: python - - # Update metadata for all vectors matching the filter - response = await idx.update( - set_metadata={'status': 'active'}, - filter={'genre': {'$eq': 'drama'}}, - namespace='my_namespace' - ) - print(f"Updated {response.matched_records} vectors") - # Preview how many vectors would be updated (dry run) - response = await idx.update( - set_metadata={'status': 'active'}, - filter={'genre': {'$eq': 'drama'}}, - namespace='my_namespace', - dry_run=True - ) - print(f"Would update {response.matched_records} vectors") - - asyncio.run(main()) - - Args: - id (str): Vector's unique id. Required for single vector updates. Must not be provided when using filter. [optional] - values (list[float]): Vector values to set. [optional] - set_metadata (dict[str, Union[str, float, int, bool, list[int], list[float], list[str]]]]): - Metadata to merge with existing metadata on the vector(s). Fields specified will overwrite - existing fields with the same key, while fields not specified will remain unchanged. [optional] - namespace (str): Namespace name where to update the vector(s). [optional] - sparse_values: (dict[str, Union[list[float], list[int]]]): Sparse values to update for the 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] - filter (dict[str, Union[str, float, int, bool, List, dict]]): A metadata filter expression. - When provided, updates all vectors in the namespace that match the filter criteria. - See `metadata filtering _`. - Must not be provided when using id. Either `id` or `filter` must be provided. [optional] - dry_run (bool): If `True`, return the number of records that match the `filter` without executing - the update. Only meaningful when using `filter` (not with `id`). Useful for previewing - the impact of a bulk update before applying changes. Defaults to `False`. [optional] - - Returns: - UpdateResponse: An UpdateResponse object. When using filter-based updates, the response includes - `matched_records` indicating the number of vectors that were updated (or would be updated if - `dry_run=True`). - """ - pass - - @abstractmethod - async def describe_index_stats( - self, filter: FilterTypedDict | None = None, **kwargs - ) -> DescribeIndexStatsResponse: - """ - The DescribeIndexStats operation returns statistics about the index's contents. - For example: The vector count per namespace and the number of dimensions. - - Args: - filter (dict[str, Union[str, float, int, bool, List, dict]]): - If this parameter is present, the operation only returns statistics for vectors that satisfy the filter. - See `metadata filtering _` [optional] - - Returns: DescribeIndexStatsResponse object which contains stats about the index. - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, Vector, SparseValues - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - print(await idx.describe_index_stats()) - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def list_paginated( - self, - prefix: str | None = None, - limit: int | None = None, - pagination_token: str | None = None, - namespace: str | None = None, - **kwargs, - ) -> ListResponse: - """ - The list_paginated operation finds vectors based on an id prefix within a single namespace. - It returns matching ids in a paginated form, with a pagination token to fetch the next page of results. - This id list can then be passed to fetch or delete operations, depending on your use case. - - Consider using the `list` method to avoid having to handle pagination tokens manually. - - Examples: - >>> results = index.list_paginated(prefix='99', limit=5, namespace='my_namespace') - >>> [v.id for v in results.vectors] - ['99', '990', '991', '992', '993'] - >>> results.pagination.next - eyJza2lwX3Bhc3QiOiI5OTMiLCJwcmVmaXgiOiI5OSJ9 - >>> next_results = index.list_paginated(prefix='99', limit=5, namespace='my_namespace', pagination_token=results.pagination.next) - - Args: - prefix (Optional[str]): The id prefix to match. If unspecified, an empty string prefix will - be used with the effect of listing all ids in a namespace [optional] - limit (Optional[int]): The maximum number of ids to return. If unspecified, the server will use a default value. [optional] - pagination_token (Optional[str]): A token needed to fetch the next page of results. This token is returned - in the response if additional results are available. [optional] - namespace (Optional[str]): The namespace to fetch vectors from. If not specified, the default namespace is used. [optional] - - Returns: ListResponse object which contains the list of ids, the namespace name, pagination information, and usage showing the number of read_units consumed. - """ - pass - - @abstractmethod - async def list(self, **kwargs): - """ - The list operation accepts all of the same arguments as list_paginated, and returns a generator that yields - a list of the matching vector ids in each page of results. It automatically handles pagination tokens on your - behalf. - - Examples: - >>> for ids in index.list(prefix='99', limit=5, namespace='my_namespace'): - >>> print(ids) - ['99', '990', '991', '992', '993'] - ['994', '995', '996', '997', '998'] - ['999'] - - Args: - prefix (Optional[str]): The id prefix to match. If unspecified, an empty string prefix will - be used with the effect of listing all ids in a namespace [optional] - limit (Optional[int]): The maximum number of ids to return. If unspecified, the server will use a default value. [optional] - pagination_token (Optional[str]): A token needed to fetch the next page of results. This token is returned - in the response if additional results are available. [optional] - namespace (Optional[str]): The namespace to fetch vectors from. If not specified, the default namespace is used. [optional] - """ - pass - - @abstractmethod - async def upsert_records(self, namespace: str, records: List[Dict]) -> UpsertResponse: - """ - :param namespace: The namespace of the index to upsert records to. - :type namespace: str, required - :param records: The records to upsert into the index. - :type records: list[Dict], required - - Upsert records to a namespace. A record is a dictionary that contains eitiher an `id` or `_id` - field along with other fields that will be stored as metadata. The `id` or `_id` field is used - as the unique identifier for the record. At least one field in the record should correspond to - a field mapping in the index's embed configuration. - - When records are upserted, Pinecone converts mapped fields into embeddings and upserts them into - the specified namespacce of the index. - - .. code-block:: python - - import asyncio - from pinecone import ( - Pinecone, - CloudProvider, - AwsRegion, - EmbedModel - IndexEmbed - ) - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - # upsert records - await idx.upsert_records( - namespace="my-namespace", - records=[ - { - "_id": "test1", - "my_text_field": "Apple is a popular fruit known for its sweetness and crisp texture.", - }, - { - "_id": "test2", - "my_text_field": "The tech company Apple is known for its innovative products like the iPhone.", - }, - { - "_id": "test3", - "my_text_field": "Many people enjoy eating apples as a healthy snack.", - }, - { - "_id": "test4", - "my_text_field": "Apple Inc. has revolutionized the tech industry with its sleek designs and user-friendly interfaces.", - }, - { - "_id": "test5", - "my_text_field": "An apple a day keeps the doctor away, as the saying goes.", - }, - { - "_id": "test6", - "my_text_field": "Apple Computer Company was founded on April 1, 1976, by Steve Jobs, Steve Wozniak, and Ronald Wayne as a partnership.", - }, - ], - ) - - from pinecone import SearchQuery, SearchRerank, RerankModel - - # search for similar records - response = await idx.search_records( - namespace="my-namespace", - query=SearchQuery( - inputs={ - "text": "Apple corporation", - }, - top_k=3, - ), - rerank=SearchRerank( - model=RerankModel.Bge_Reranker_V2_M3, - rank_fields=["my_text_field"], - top_n=3, - ), - ) - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def search( - self, - namespace: str, - query: SearchQueryTypedDict | SearchQuery, - rerank: (SearchRerankTypedDict | SearchRerank) | None = None, - fields: List[str] | None = ["*"], # Default to returning all fields - ) -> SearchRecordsResponse: - """ - :param namespace: The namespace in the index to search. - :type namespace: str, required - :param query: The SearchQuery to use for the search. The query can include a ``match_terms`` field - to specify which terms must be present in the text of each search hit. The match_terms - should be a dict with ``strategy`` (str) and ``terms`` (list[str]) keys, e.g. - ``{"strategy": "all", "terms": ["term1", "term2"]}``. Currently only "all" strategy - is supported, which means all specified terms must be present. - **Note:** match_terms is only supported for sparse indexes with integrated embedding - configured to use the pinecone-sparse-english-v0 model. - :type query: Union[Dict, SearchQuery], required - :param rerank: The SearchRerank to use with the search request. - :type rerank: Union[Dict, SearchRerank], optional - :return: The records that match the search. - - Search for records. - - This operation converts a query to a vector embedding and then searches a namespace. You - can optionally provide a reranking operation as part of the search. - - .. code-block:: python - - import asyncio - from pinecone import ( - Pinecone, - CloudProvider, - AwsRegion, - EmbedModel - IndexEmbed - ) - - async def main(): - pc = Pinecone() - async with pc.IndexAsyncio(host="example-dojoi3u.svc.aped-4627-b74a.pinecone.io") as idx: - # upsert records - await idx.upsert_records( - namespace="my-namespace", - records=[ - { - "_id": "test1", - "my_text_field": "Apple is a popular fruit known for its sweetness and crisp texture.", - }, - { - "_id": "test2", - "my_text_field": "The tech company Apple is known for its innovative products like the iPhone.", - }, - { - "_id": "test3", - "my_text_field": "Many people enjoy eating apples as a healthy snack.", - }, - { - "_id": "test4", - "my_text_field": "Apple Inc. has revolutionized the tech industry with its sleek designs and user-friendly interfaces.", - }, - { - "_id": "test5", - "my_text_field": "An apple a day keeps the doctor away, as the saying goes.", - }, - { - "_id": "test6", - "my_text_field": "Apple Computer Company was founded on April 1, 1976, by Steve Jobs, Steve Wozniak, and Ronald Wayne as a partnership.", - }, - ], - ) - - from pinecone import SearchQuery, SearchRerank, RerankModel - - # search for similar records - response = await idx.search_records( - namespace="my-namespace", - query=SearchQuery( - inputs={ - "text": "Apple corporation", - }, - top_k=3, - ), - rerank=SearchRerank( - model=RerankModel.Bge_Reranker_V2_M3, - rank_fields=["my_text_field"], - top_n=3, - ), - ) - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def search_records( - self, - namespace: str, - query: SearchQueryTypedDict | SearchQuery, - rerank: (SearchRerankTypedDict | SearchRerank) | None = None, - fields: List[str] | None = ["*"], # Default to returning all fields - ) -> SearchRecordsResponse: - """Alias of the search() method.""" - pass - - @abstractmethod - @require_kwargs - async def create_namespace( - self, name: str, schema: dict[str, Any] | None = None, **kwargs - ) -> NamespaceDescription: - """Create a namespace in a serverless index. - - Args: - name (str): The name of the namespace to create - schema (Optional[dict[str, Any]]): Optional schema configuration for the namespace as a dictionary. [optional] - - Returns: - NamespaceDescription: Information about the created namespace including vector count - - Create a namespace in a serverless index. For guidance and examples, see - `Manage namespaces `_. - - **Note:** This operation is not supported for pod-based indexes. - - Examples: - - .. code-block:: python - - >>> # Create a namespace with just a name - >>> import asyncio - >>> from pinecone import Pinecone - >>> - >>> async def main(): - ... pc = Pinecone() - ... async with pc.IndexAsyncio(host="example-index-dojoi3u.svc.eu-west1-gcp.pinecone.io") as idx: - ... namespace = await idx.create_namespace(name="my-namespace") - ... print(f"Created namespace: {namespace.name}, Vector count: {namespace.vector_count}") - >>> - >>> asyncio.run(main()) - - >>> # Create a namespace with schema configuration - >>> from pinecone.core.openapi.db_data.model.create_namespace_request_schema import CreateNamespaceRequestSchema - >>> schema = CreateNamespaceRequestSchema(fields={...}) - >>> namespace = await idx.create_namespace(name="my-namespace", schema=schema) - - """ - pass - - @abstractmethod - @require_kwargs - async def describe_namespace(self, namespace: str, **kwargs) -> NamespaceDescription: - """Describe a namespace within an index, showing the vector count within the namespace. - - Args: - namespace (str): The namespace to describe - - Returns: - NamespaceDescription: Information about the namespace including vector count - """ - pass - - @abstractmethod - @require_kwargs - async def delete_namespace(self, namespace: str, **kwargs) -> dict[str, Any]: - """Delete a namespace from an index. - - Args: - namespace (str): The namespace to delete - - Returns: - dict[str, Any]: Response from the delete operation - """ - pass - - @abstractmethod - @require_kwargs - async def list_namespaces( - self, limit: int | None = None, **kwargs - ) -> AsyncIterator[ListNamespacesResponse]: - """List all namespaces in an index. This method automatically handles pagination to return all results. - - Args: - limit (Optional[int]): The maximum number of namespaces to return. If unspecified, the server will use a default value. [optional] - - Returns: - ``ListNamespacesResponse``: Object containing the list of namespaces. - - Examples: - .. code-block:: python - >>> async for namespace in index.list_namespaces(limit=5): - ... print(f"Namespace: {namespace.name}, Vector count: {namespace.vector_count}") - Namespace: namespace1, Vector count: 1000 - Namespace: namespace2, Vector count: 2000 - """ - pass - - @abstractmethod - @require_kwargs - async def list_namespaces_paginated( - self, limit: int | None = None, pagination_token: str | None = None, **kwargs - ) -> ListNamespacesResponse: - """List all namespaces in an index with pagination support. The response includes pagination information if there are more results available. - - Consider using the ``list_namespaces`` method to avoid having to handle pagination tokens manually. - - Args: - limit (Optional[int]): The maximum number of namespaces to return. If unspecified, the server will use a default value. [optional] - pagination_token (Optional[str]): A token needed to fetch the next page of results. This token is returned - in the response if additional results are available. [optional] - - Returns: - ``ListNamespacesResponse``: Object containing the list of namespaces and pagination information. - - Examples: - .. code-block:: python - >>> results = await index.list_namespaces_paginated(limit=5) - >>> results.pagination.next - eyJza2lwX3Bhc3QiOiI5OTMiLCJwcmVmaXgiOiI5OSJ9 - >>> next_results = await index.list_namespaces_paginated(limit=5, pagination_token=results.pagination.next) - """ - pass diff --git a/pinecone/db_data/interfaces.py b/pinecone/db_data/interfaces.py deleted file mode 100644 index a8224de1..00000000 --- a/pinecone/db_data/interfaces.py +++ /dev/null @@ -1,1026 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Any, Iterator, Literal - -from pinecone.core.openapi.db_data.models import ( - IndexDescription as DescribeIndexStatsResponse, - ListResponse, - SearchRecordsResponse, - NamespaceDescription, - ListNamespacesResponse, -) -from .query_results_aggregator import QueryNamespacesResults -from multiprocessing.pool import ApplyResult -from .types import ( - VectorTypedDict, - SparseVectorTypedDict, - VectorMetadataTypedDict, - FilterTypedDict, - VectorTuple, - VectorTupleWithMetadata, - SearchQueryTypedDict, - SearchRerankTypedDict, -) -from .dataclasses import ( - SearchQuery, - SearchRerank, - FetchResponse, - FetchByMetadataResponse, - QueryResponse, - UpsertResponse, - UpdateResponse, - SparseValues, - Vector, -) -from pinecone.utils import require_kwargs - - -class IndexInterface(ABC): - @abstractmethod - def upsert( - self, - vectors: ( - list[Vector] | list[VectorTuple] | list[VectorTupleWithMetadata] | list[VectorTypedDict] - ), - namespace: str | None = None, - batch_size: int | None = None, - show_progress: bool = True, - **kwargs, - ) -> UpsertResponse | ApplyResult: - """ - Args: - vectors (Union[list[Vector], list[VectorTuple], list[VectorTupleWithMetadata], list[VectorTypedDict]]): A list of vectors to upsert. - namespace (str): The namespace to write to. If not specified, the default namespace is used. [optional] - batch_size (int): The number of vectors to upsert in each batch. - If not specified, all vectors will be upserted in a single batch. [optional] - show_progress (bool): Whether to show a progress bar using tqdm. - Applied only if batch_size is provided. Default is True. - - Returns: - `UpsertResponse`, includes the number of vectors upserted. - - - The upsert operation writes vectors into a namespace of your index. - - If a new value is upserted for an existing vector id, it will overwrite the previous value. - - To upsert in parallel follow `this link `_. - - **Upserting dense vectors** - - When working with dense vectors, the dimension of each vector must match the dimension configured for the - index. - - A vector can be represented in a variety of ways. - - .. code-block:: python - :caption: Upserting a dense vector using the Vector object - :emphasize-lines: 9-13 - - from pinecone import Pinecone, Vector - - pc = Pinecone() - idx = pc.Index(name="index-name") - - idx.upsert( - namespace = 'my-namespace', - vectors = [ - Vector( - id='id1', - values=[0.1, 0.2, 0.3, 0.4], - metadata={'metadata_key': 'metadata_value'} - ), - ] - ) - - .. code-block:: python - :caption: Upserting a dense vector as a two-element tuple (no metadata) - :emphasize-lines: 4 - - idx.upsert( - namespace = 'my-namespace', - vectors = [ - ('id1', [0.1, 0.2, 0.3, 0.4]), - ] - ) - - .. code-block:: python - :caption: Upserting a dense vector as a three-element tuple with metadata - :emphasize-lines: 4-8 - - idx.upsert( - namespace = 'my-namespace', - vectors = [ - ( - 'id1', - [0.1, 0.2, 0.3, 0.4], - {'metadata_key': 'metadata_value'} - ), - ] - ) - - .. code-block:: python - :caption: Upserting a dense vector using a vector dictionary - :emphasize-lines: 4-8 - - idx.upsert( - namespace = 'my-namespace', - vectors = [ - { - "id": 1, - "values": [0.1, 0.2, 0.3, 0.4], - "metadata": {"metadata_key": "metadata_value"} - }, - ] - - **Upserting sparse vectors** - - .. code-block:: python - :caption: Upserting a sparse vector - :emphasize-lines: 32-38 - - from pinecone import ( - Pinecone, - Metric, - Vector, - SparseValues, - VectorType, - ServerlessSpec, - CloudProvider, - AwsRegion - ) - - pc = Pinecone() # Reads PINECONE_API_KEY from environment variable - - # Create a sparse index - index_description = pc.create_index( - name="example-sparse", - metric=Metric.Dotproduct, - vector_type=VectorType.Sparse, - spec=ServerlessSpec( - cloud=CloudProvider.AWS, - region=AwsRegion.US_WEST_2, - ) - ) - - # Target the index created above - idx = pc.Index(host=index_description.host) - - # Upsert a sparse vector - idx.upsert( - namespace='my-namespace', - vectors=[ - Vector( - id='id1', - sparse_values=SparseValues( - indices=[1, 2], - values=[0.2, 0.4] - ) - ), - ] - ) - - .. code-block:: python - :caption: Upserting a sparse vector using a dictionary - :emphasize-lines: 4-10 - - idx.upsert( - namespace = 'my-namespace', - vectors = [ - { - "id": 1, - "sparse_values": { - "indices": [1, 2], - "values": [0.2, 0.4] - } - }, - ] - ) - - - **Batch upsert** - - If you have a large number of vectors, you can upsert them in batches. - - .. code-block:: python - :caption: Upserting in batches - :emphasize-lines: 19 - - from pinecone import Pinecone, Vector - import random - - pc = Pinecone() - idx = pc.Index(host="example-index-dojoi3u.svc.preprod-aws-0.pinecone.io") - - # Create some fake vector data for demonstration - num_vectors = 100000 - vectors = [ - Vector( - id=f'id{i}', - values=[random.random() for _ in range(1536)]) - for i in range(num_vectors) - ] - - idx.upsert( - namespace='my-namespace', - vectors=vectors, - batch_size=50 - ) - - - **Visual progress bar with tqdm** - - To see a progress bar when upserting in batches, you will need to separately install `tqdm `_. - If ``tqdm`` is present, the client will detect and use it to display progress when ``show_progress=True``. - - - """ - pass - - @abstractmethod - def upsert_from_dataframe( - self, df, namespace: str | None = None, batch_size: int = 500, show_progress: bool = True - ): - """Upsert vectors from a pandas DataFrame into the index. - - Args: - df: A pandas DataFrame with the following columns: id, values, sparse_values, and metadata. - namespace: The namespace to upsert into. If not specified, the default namespace is used. [optional] - batch_size: The number of rows to upsert in a single batch. Defaults to 500. - show_progress: Whether to show a progress bar. Defaults to True. - - Returns: - UpsertResponse: Object containing the number of vectors upserted. - - Examples: - - .. code-block:: python - - import pandas as pd - from pinecone import Pinecone - - pc = Pinecone() - idx = pc.Index(host="example-index-host") - - # Create a DataFrame with vector data - df = pd.DataFrame({ - 'id': ['id1', 'id2', 'id3'], - 'values': [ - [0.1, 0.2, 0.3], - [0.4, 0.5, 0.6], - [0.7, 0.8, 0.9] - ], - 'metadata': [ - {'key1': 'value1'}, - {'key2': 'value2'}, - {'key3': 'value3'} - ] - }) - - # Upsert from DataFrame - response = idx.upsert_from_dataframe( - df=df, - namespace='my-namespace', - batch_size=100, - show_progress=True - ) - - """ - pass - - @abstractmethod - def upsert_records(self, namespace: str, records: list[dict]) -> UpsertResponse: - """Upsert records to a namespace. - - A record is a dictionary that contains either an ``id`` or ``_id`` - field along with other fields that will be stored as metadata. The ``id`` or ``_id`` field is used - as the unique identifier for the record. At least one field in the record should correspond to - a field mapping in the index's embed configuration. - - When records are upserted, Pinecone converts mapped fields into embeddings and upserts them into - the specified namespace of the index. - - Args: - namespace: The namespace of the index to upsert records to. - records: The records to upsert into the index. Each record should contain an ``id`` or ``_id`` - field and fields that match the index's embed configuration field mappings. - - Returns: - UpsertResponse: Object which contains the number of records upserted. - - Examples: - - .. code-block:: python - :caption: Upserting records to be embedded with Pinecone's integrated inference models - - from pinecone import ( - Pinecone, - CloudProvider, - AwsRegion, - EmbedModel, - IndexEmbed - ) - - pc = Pinecone(api_key="<>") - - # Create an index configured for the multilingual-e5-large model - index_model = pc.create_index_for_model( - name="my-model-index", - cloud=CloudProvider.AWS, - region=AwsRegion.US_WEST_2, - embed=IndexEmbed( - model=EmbedModel.Multilingual_E5_Large, - field_map={"text": "my_text_field"} - ) - ) - - # Instantiate the index client - idx = pc.Index(host=index_model.host) - - # Upsert records - idx.upsert_records( - namespace="my-namespace", - records=[ - { - "_id": "test1", - "my_text_field": "Apple is a popular fruit known for its sweetness and crisp texture.", - }, - { - "_id": "test2", - "my_text_field": "The tech company Apple is known for its innovative products like the iPhone.", - }, - { - "_id": "test3", - "my_text_field": "Many people enjoy eating apples as a healthy snack.", - }, - ], - ) - - """ - pass - - @abstractmethod - def search( - self, - namespace: str, - query: SearchQueryTypedDict | SearchQuery, - rerank: (SearchRerankTypedDict | SearchRerank) | None = None, - fields: list[str] | None = ["*"], # Default to returning all fields - ) -> SearchRecordsResponse: - """Search for records in a namespace. - - This operation converts a query to a vector embedding and then searches a namespace. You - can optionally provide a reranking operation as part of the search. - - Args: - namespace: The namespace in the index to search. - query: The SearchQuery to use for the search. The query can include a ``match_terms`` field - to specify which terms must be present in the text of each search hit. The match_terms - should be a dict with ``strategy`` (str) and ``terms`` (list[str]) keys, e.g. - ``{"strategy": "all", "terms": ["term1", "term2"]}``. Currently only "all" strategy - is supported, which means all specified terms must be present. - **Note:** match_terms is only supported for sparse indexes with integrated embedding - configured to use the pinecone-sparse-english-v0 model. - rerank: The SearchRerank to use with the search request. [optional] - fields: List of fields to return in the response. Defaults to ["*"] to return all fields. [optional] - - Returns: - SearchRecordsResponse: The records that match the search. - - Examples: - - .. code-block:: python - - from pinecone import ( - Pinecone, - CloudProvider, - AwsRegion, - EmbedModel, - IndexEmbed, - SearchQuery, - SearchRerank, - RerankModel - ) - - pc = Pinecone(api_key="<>") - - # Create an index for your embedding model - index_model = pc.create_index_for_model( - name="my-model-index", - cloud=CloudProvider.AWS, - region=AwsRegion.US_WEST_2, - embed=IndexEmbed( - model=EmbedModel.Multilingual_E5_Large, - field_map={"text": "my_text_field"} - ) - ) - - # Instantiate the index client - idx = pc.Index(host=index_model.host) - - # Search for similar records - response = idx.search( - namespace="my-namespace", - query=SearchQuery( - inputs={ - "text": "Apple corporation", - }, - top_k=3, - ), - rerank=SearchRerank( - model=RerankModel.Bge_Reranker_V2_M3, - rank_fields=["my_text_field"], - top_n=3, - ), - ) - - """ - pass - - @abstractmethod - def search_records( - self, - namespace: str, - query: SearchQueryTypedDict | SearchQuery, - rerank: (SearchRerankTypedDict | SearchRerank) | None = None, - fields: list[str] | None = ["*"], # Default to returning all fields - ) -> SearchRecordsResponse: - """Alias of the search() method. - - See :meth:`search` for full documentation and examples. - - """ - pass - - @abstractmethod - def delete( - self, - ids: list[str] | None = None, - delete_all: bool | None = None, - namespace: str | None = None, - filter: FilterTypedDict | None = None, - **kwargs, - ) -> dict[str, Any]: - """Delete vectors from the index, from a single namespace. - - The Delete operation deletes vectors from the index, from a single namespace. - No error is raised if the vector id does not exist. - - Note: For any delete call, if namespace is not specified, the default namespace ``""`` is used. - Since the delete operation does not error when ids are not present, this means you may not receive - an error if you delete from the wrong namespace. - - Delete can occur in the following mutually exclusive ways: - - 1. Delete by ids from a single namespace - 2. Delete all vectors from a single namespace by setting delete_all to True - 3. Delete all vectors from a single namespace by specifying a metadata filter - (note that for this option delete_all must be set to False) - - Args: - ids: Vector ids to delete. [optional] - delete_all: This indicates that all vectors in the index namespace should be deleted. - Default is False. [optional] - namespace: The namespace to delete vectors from. If not specified, the default namespace is used. [optional] - filter: If specified, the metadata filter here will be used to select the vectors to delete. - This is mutually exclusive with specifying ids to delete in the ids param or using delete_all=True. - See `metadata filtering _` [optional] - **kwargs: Additional keyword arguments for the API call. - - Returns: - dict[str, Any]: An empty dictionary if the delete operation was successful. - - Examples: - - .. code-block:: python - - >>> # Delete specific vectors by ID - >>> index.delete(ids=['id1', 'id2'], namespace='my_namespace') - {} - - >>> # Delete all vectors from a namespace - >>> index.delete(delete_all=True, namespace='my_namespace') - {} - - >>> # Delete vectors matching a metadata filter - >>> index.delete(filter={'key': 'value'}, namespace='my_namespace') - {} - - """ - pass - - @abstractmethod - def fetch(self, ids: list[str], namespace: str | None = None, **kwargs) -> FetchResponse: - """Fetch vectors by ID from a single namespace. - - The fetch operation looks up and returns vectors, by ID, from a single namespace. - The returned vectors include the vector data and/or metadata. - - Args: - ids: The vector IDs to fetch. - namespace: The namespace to fetch vectors from. If not specified, the default namespace is used. [optional] - **kwargs: Additional keyword arguments for the API call. - - Returns: - FetchResponse: Object which contains the list of Vector objects, and namespace name. - - Examples: - - .. code-block:: python - - >>> # Fetch vectors from a specific namespace - >>> response = index.fetch(ids=['id1', 'id2'], namespace='my_namespace') - >>> for vector_id, vector in response.vectors.items(): - ... print(f"{vector_id}: {vector.values}") - - >>> # Fetch vectors from the default namespace - >>> response = index.fetch(ids=['id1', 'id2']) - - """ - pass - - @abstractmethod - def fetch_by_metadata( - self, - filter: FilterTypedDict, - namespace: str | None = None, - limit: int | None = None, - pagination_token: str | None = None, - **kwargs, - ) -> FetchByMetadataResponse: - """ - Fetch vectors by metadata filter. - - Look up and return vectors by metadata filter from a single namespace. - The returned vectors include the vector data and/or metadata. - - Examples: - - .. code-block:: python - - >>> index.fetch_by_metadata( - ... filter={'genre': {'$in': ['comedy', 'drama']}, 'year': {'$eq': 2019}}, - ... namespace='my_namespace', - ... limit=50 - ... ) - >>> index.fetch_by_metadata( - ... filter={'status': 'active'}, - ... pagination_token='token123' - ... ) - - Args: - filter (dict[str, Union[str, float, int, bool, List, dict]]): - Metadata filter expression to select vectors. - See `metadata filtering _` - namespace (str): The namespace to fetch vectors from. - If not specified, the default namespace is used. [optional] - limit (int): Max number of vectors to return. Defaults to 100. [optional] - pagination_token (str): Pagination token to continue a previous listing operation. [optional] - - Returns: - FetchByMetadataResponse: Object containing the fetched vectors, namespace, usage, and pagination token. - """ - pass - - @abstractmethod - def query( - self, - *args, - top_k: int, - vector: list[float] | None = None, - id: str | None = None, - namespace: str | None = None, - filter: FilterTypedDict | None = None, - include_values: bool | None = None, - include_metadata: bool | None = None, - sparse_vector: (SparseValues | SparseVectorTypedDict) | None = None, - **kwargs, - ) -> QueryResponse | ApplyResult: - """ - The Query operation searches a namespace, using a query vector. - It retrieves the ids of the most similar items in a namespace, along with their similarity scores. - - Examples: - - .. code-block:: python - - >>> index.query(vector=[1, 2, 3], top_k=10, namespace='my_namespace') - >>> index.query(id='id1', top_k=10, namespace='my_namespace') - >>> index.query(vector=[1, 2, 3], top_k=10, namespace='my_namespace', filter={'key': 'value'}) - >>> index.query(id='id1', top_k=10, namespace='my_namespace', include_metadata=True, include_values=True) - >>> index.query(vector=[1, 2, 3], sparse_vector={'indices': [1, 2], 'values': [0.2, 0.4]}, - >>> top_k=10, namespace='my_namespace') - >>> index.query(vector=[1, 2, 3], sparse_vector=SparseValues([1, 2], [0.2, 0.4]), - >>> top_k=10, namespace='my_namespace') - - Args: - vector (list[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] - id (str): The unique ID of the vector to be used as a query vector. - Each `query()` request can contain only one of the parameters - `vector` or `id`. [optional] - top_k (int): The number of results to return for each query. Must be an integer greater than 1. - namespace (str): The namespace to query vectors from. - If not specified, the default namespace is used. [optional] - filter: The filter to apply. You can use vector metadata to limit your search. - See `metadata filtering _` [optional] - include_values (bool): Indicates whether vector values are included in the response. - If omitted the server will use the default value of False [optional] - include_metadata (bool): Indicates whether metadata is included in the response as well as the ids. - If omitted the server will use the default value of False [optional] - 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. - - Returns: QueryResponse object which contains the list of the closest vectors as ScoredVector objects, - and namespace name. - """ - pass - - @abstractmethod - def query_namespaces( - self, - vector: list[float] | None, - namespaces: list[str], - metric: Literal["cosine", "euclidean", "dotproduct"], - top_k: int | None = None, - filter: FilterTypedDict | None = None, - include_values: bool | None = None, - include_metadata: bool | None = None, - sparse_vector: (SparseValues | SparseVectorTypedDict) | 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. - - :param vector: The query vector, must be the same length as the dimension of the index being queried. - :type vector: list[float] - :param namespaces: The list of namespaces to query. - :type namespaces: list[str] - :param top_k: The number of results you would like to request from each namespace. Defaults to 10. - :type top_k: Optional[int] - :param metric: Must be one of 'cosine', 'euclidean', 'dotproduct'. This is needed in order to merge results across namespaces, since the interpretation of score depends on the index metric type. - :type metric: str - :param filter: Pass an optional filter to filter results based on metadata. Defaults to None. - :type filter: Optional[dict[str, Union[str, float, int, bool, List, dict]]] - :param include_values: Boolean field indicating whether vector values should be included with results. Defaults to None. - :type include_values: Optional[bool] - :param include_metadata: Boolean field indicating whether vector metadata should be included with results. Defaults to None. - :type include_metadata: Optional[bool] - :param 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. - :type sparse_vector: Optional[ Union[SparseValues, dict[str, Union[list[float], list[int]]]] ] - :return: A QueryNamespacesResults object containing the combined results from all namespaces, as well as the combined usage cost in read units. - :rtype: QueryNamespacesResults - - .. admonition:: Note - - 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. - If these values are too small in relation to your workload, you will experience performance issues as - requests queue up while waiting for a request thread to become available. - - Examples: - - .. code-block:: python - - from pinecone import Pinecone - - pc = Pinecone() - - index = pc.Index( - host="index-name", - pool_threads=32, - connection_pool_maxsize=32 - ) - - query_vec = [0.1, 0.2, 0.3] # An embedding that matches the index dimension - combined_results = index.query_namespaces( - vector=query_vec, - namespaces=['ns1', 'ns2', 'ns3', 'ns4'], - metric="cosine", - top_k=10, - filter={'genre': {"$eq": "drama"}}, - include_values=True, - include_metadata=True - ) - - for vec in combined_results.matches: - print(vec.id, vec.score) - print(combined_results.usage) - - """ - pass - - @abstractmethod - def update( - self, - id: str | None = None, - values: list[float] | None = None, - set_metadata: VectorMetadataTypedDict | None = None, - namespace: str | None = None, - sparse_values: (SparseValues | SparseVectorTypedDict) | None = None, - filter: FilterTypedDict | None = None, - dry_run: bool | None = None, - **kwargs, - ) -> UpdateResponse: - """ - The Update operation updates vectors in a namespace. - - This method supports two update modes: - - 1. **Single vector update by ID**: Provide `id` to update a specific vector. - - Updates the vector with the given ID - - If `values` is included, it will overwrite the previous vector values - - If `set_metadata` is included, the metadata will be merged with existing metadata on the vector. - Fields specified in `set_metadata` will overwrite existing fields with the same key, while - fields not in `set_metadata` will remain unchanged. - - 2. **Bulk update by metadata filter**: Provide `filter` to update all vectors matching the filter criteria. - - Updates all vectors in the namespace that match the filter expression - - Useful for updating metadata across multiple vectors at once - - If `set_metadata` is included, the metadata will be merged with existing metadata on each vector. - Fields specified in `set_metadata` will overwrite existing fields with the same key, while - fields not in `set_metadata` will remain unchanged. - - The response includes `matched_records` indicating how many vectors were updated - - Either `id` or `filter` must be provided (but not both in the same call). - - Examples: - - **Single vector update by ID:** - - .. code-block:: python - - >>> # Update vector values - >>> index.update(id='id1', values=[1, 2, 3], namespace='my_namespace') - >>> # Update vector metadata - >>> index.update(id='id1', set_metadata={'key': 'value'}, namespace='my_namespace') - >>> # Update vector values and sparse values - >>> index.update(id='id1', values=[1, 2, 3], sparse_values={'indices': [1, 2], 'values': [0.2, 0.4]}, - >>> namespace='my_namespace') - >>> index.update(id='id1', values=[1, 2, 3], sparse_values=SparseValues(indices=[1, 2], values=[0.2, 0.4]), - >>> namespace='my_namespace') - - **Bulk update by metadata filter:** - - .. code-block:: python - - >>> # Update metadata for all vectors matching the filter - >>> response = index.update(set_metadata={'status': 'active'}, filter={'genre': {'$eq': 'drama'}}, - >>> namespace='my_namespace') - >>> print(f"Updated {response.matched_records} vectors") - >>> # Preview how many vectors would be updated (dry run) - >>> response = index.update(set_metadata={'status': 'active'}, filter={'genre': {'$eq': 'drama'}}, - >>> namespace='my_namespace', dry_run=True) - >>> print(f"Would update {response.matched_records} vectors") - - Args: - id: Vector unique id. Required for single vector updates. Must not be provided when using filter. [optional] - values: Vector values to set. [optional] - set_metadata: Metadata to merge with existing metadata on the vector(s). Fields specified will overwrite - existing fields with the same key, while fields not specified will remain unchanged. [optional] - namespace: Namespace name where to update the vector(s). [optional] - sparse_values: Sparse values to update for the 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] - filter: A metadata filter expression. When provided, updates all vectors in the namespace that match - the filter criteria. See `metadata filtering _`. - Must not be provided when using id. Either ``id`` or ``filter`` must be provided. [optional] - dry_run: If ``True``, return the number of records that match the ``filter`` without executing - the update. Only meaningful when using ``filter`` (not with ``id``). Useful for previewing - the impact of a bulk update before applying changes. Defaults to ``False``. [optional] - - Returns: - UpdateResponse: An UpdateResponse object. When using filter-based updates, the response includes - `matched_records` indicating the number of vectors that were updated (or would be updated if - `dry_run=True`). - """ - pass - - @abstractmethod - def describe_index_stats( - self, filter: FilterTypedDict | None = None, **kwargs - ) -> DescribeIndexStatsResponse: - """ - The DescribeIndexStats operation returns statistics about the index contents. - For example: The vector count per namespace and the number of dimensions. - - Args: - filter (dict[str, Union[str, float, int, bool, List, dict]]): - If this parameter is present, the operation only returns statistics for vectors that satisfy the filter. - See `metadata filtering _` [optional] - - Returns: DescribeIndexStatsResponse object which contains stats about the index. - - .. code-block:: python - - >>> pc = Pinecone() - >>> index = pc.Index(name="my-index") - >>> index.describe_index_stats() - {'dimension': 1536, - 'index_fullness': 0.0, - 'metric': 'cosine', - 'namespaces': {'ns0': {'vector_count': 700}, - 'ns1': {'vector_count': 700}, - 'ns2': {'vector_count': 500}, - 'ns3': {'vector_count': 100}, - 'ns4': {'vector_count': 100}, - 'ns5': {'vector_count': 50}, - 'ns6': {'vector_count': 50}}, - 'total_vector_count': 2200, - 'vector_type': 'dense'} - - """ - pass - - @abstractmethod - def list_paginated( - self, - prefix: str | None = None, - limit: int | None = None, - pagination_token: str | None = None, - namespace: str | None = None, - **kwargs, - ) -> ListResponse: - """ - The list_paginated operation finds vectors based on an id prefix within a single namespace. - It returns matching ids in a paginated form, with a pagination token to fetch the next page of results. - This id list can then be passed to fetch or delete operations, depending on your use case. - - Consider using the `list` method to avoid having to handle pagination tokens manually. - - Examples: - - .. code-block:: python - - >>> results = index.list_paginated(prefix='99', limit=5, namespace='my_namespace') - >>> [v.id for v in results.vectors] - ['99', '990', '991', '992', '993'] - >>> results.pagination.next - eyJza2lwX3Bhc3QiOiI5OTMiLCJwcmVmaXgiOiI5OSJ9 - >>> next_results = index.list_paginated(prefix='99', limit=5, namespace='my_namespace', pagination_token=results.pagination.next) - - Args: - prefix (Optional[str]): The id prefix to match. If unspecified, an empty string prefix will - be used with the effect of listing all ids in a namespace [optional] - limit (Optional[int]): The maximum number of ids to return. If unspecified, the server will use a default value. [optional] - pagination_token (Optional[str]): A token needed to fetch the next page of results. This token is returned - in the response if additional results are available. [optional] - namespace (Optional[str]): The namespace to fetch vectors from. If not specified, the default namespace is used. [optional] - - Returns: ListResponse object which contains the list of ids, the namespace name, pagination information, and usage showing the number of read_units consumed. - """ - pass - - @abstractmethod - def list(self, **kwargs): - """ - The list operation accepts all of the same arguments as list_paginated, and returns a generator that yields - a list of the matching vector ids in each page of results. It automatically handles pagination tokens on your - behalf. - - Examples: - >>> for ids in index.list(prefix='99', limit=5, namespace='my_namespace'): - >>> print(ids) - ['99', '990', '991', '992', '993'] - ['994', '995', '996', '997', '998'] - ['999'] - - Args: - prefix (Optional[str]): The id prefix to match. If unspecified, an empty string prefix will - be used with the effect of listing all ids in a namespace [optional] - limit (Optional[int]): The maximum number of ids to return. If unspecified, the server will use a default value. [optional] - pagination_token (Optional[str]): A token needed to fetch the next page of results. This token is returned - in the response if additional results are available. [optional] - namespace (Optional[str]): The namespace to fetch vectors from. If not specified, the default namespace is used. [optional] - """ - pass - - @abstractmethod - @require_kwargs - def create_namespace( - self, name: str, schema: dict[str, Any] | None = None, **kwargs - ) -> NamespaceDescription: - """Create a namespace in a serverless index. - - Args: - name (str): The name of the namespace to create - schema (Optional[dict[str, Any]]): Optional schema configuration for the namespace as a dictionary. [optional] - - Returns: - NamespaceDescription: Information about the created namespace including vector count - - Create a namespace in a serverless index. For guidance and examples, see - `Manage namespaces `_. - - **Note:** This operation is not supported for pod-based indexes. - - Examples: - - .. code-block:: python - - >>> # Create a namespace with just a name - >>> namespace = index.create_namespace(name="my-namespace") - >>> print(f"Created namespace: {namespace.name}, Vector count: {namespace.vector_count}") - - >>> # Create a namespace with schema configuration - >>> from pinecone.core.openapi.db_data.model.create_namespace_request_schema import CreateNamespaceRequestSchema - >>> schema = CreateNamespaceRequestSchema(fields={...}) - >>> namespace = index.create_namespace(name="my-namespace", schema=schema) - """ - pass - - @abstractmethod - @require_kwargs - def describe_namespace(self, namespace: str, **kwargs) -> NamespaceDescription: - """Describe a namespace within an index, showing the vector count within the namespace. - - Args: - namespace (str): The namespace to describe - - Returns: - NamespaceDescription: Information about the namespace including vector count - """ - pass - - @abstractmethod - @require_kwargs - def delete_namespace(self, namespace: str, **kwargs) -> dict[str, Any]: - """Delete a namespace from an index. - - Args: - namespace: The namespace to delete. - - Returns: - dict[str, Any]: Response from the delete operation. - """ - pass - - @abstractmethod - @require_kwargs - def list_namespaces( - self, limit: int | None = None, **kwargs - ) -> Iterator[ListNamespacesResponse]: - """List all namespaces in an index. This method automatically handles pagination to return all results. - - Args: - limit: The maximum number of namespaces to return. If unspecified, the server will use a default value. [optional] - - Returns: - Iterator[ListNamespacesResponse]: An iterator that yields ListNamespacesResponse objects containing the list of namespaces. - - Examples: - - .. code-block:: python - - >>> # Iterate over all namespaces - >>> for namespace_response in index.list_namespaces(limit=5): - ... for namespace in namespace_response.namespaces: - ... print(f"Namespace: {namespace.name}, Vector count: {namespace.vector_count}") - - >>> # Convert to list (be cautious with large datasets) - >>> results = list(index.list_namespaces(limit=5)) - >>> for namespace_response in results: - ... for namespace in namespace_response.namespaces: - ... print(f"Namespace: {namespace.name}, Vector count: {namespace.vector_count}") - - """ - pass - - @abstractmethod - @require_kwargs - def list_namespaces_paginated( - self, limit: int | None = None, pagination_token: str | None = None, **kwargs - ) -> ListNamespacesResponse: - """List all namespaces in an index with pagination support. The response includes pagination information if there are more results available. - - Consider using the ``list_namespaces`` method to avoid having to handle pagination tokens manually. - - Args: - limit: The maximum number of namespaces to return. If unspecified, the server will use a default value. [optional] - pagination_token: A token needed to fetch the next page of results. This token is returned - in the response if additional results are available. [optional] - **kwargs: Additional keyword arguments for the API call. - - Returns: - ListNamespacesResponse: Object containing the list of namespaces and pagination information. - - Examples: - - .. code-block:: python - - >>> # Get first page of namespaces - >>> results = index.list_namespaces_paginated(limit=5) - >>> for namespace in results.namespaces: - ... print(f"Namespace: {namespace.name}, Vector count: {namespace.vector_count}") - - >>> # Get next page if available - >>> if results.pagination and results.pagination.next: - ... next_results = index.list_namespaces_paginated( - ... limit=5, - ... pagination_token=results.pagination.next - ... ) - - """ - pass diff --git a/pinecone/pinecone_asyncio.py b/pinecone/pinecone_asyncio.py index dbdd5989..cb35523d 100644 --- a/pinecone/pinecone_asyncio.py +++ b/pinecone/pinecone_asyncio.py @@ -9,7 +9,6 @@ from pinecone.utils import normalize_host, require_kwargs, docslinks -from .pinecone_interface_asyncio import PineconeAsyncioDBControlInterface from .pinecone import check_realistic_host if TYPE_CHECKING: @@ -59,7 +58,7 @@ """ :meta private: """ -class PineconeAsyncio(PineconeAsyncioDBControlInterface): +class PineconeAsyncio: """ ``PineconeAsyncio`` is an asyncio client for interacting with Pinecone's control plane API. @@ -94,25 +93,205 @@ def __init__( **kwargs, ) -> None: """ - Initialize the ``PineconeAsyncio`` client. + The ``PineconeAsyncio`` class is the main entry point for interacting with Pinecone using asyncio. + It is used to create, delete, and manage your indexes and collections. Except for needing to use + ``async with`` when instantiating the client and ``await`` when calling its methods, the functionality + provided by this class is extremely similar to the functionality of the ``Pinecone`` class. :param api_key: The API key to use for authentication. If not passed via kwarg, the API key will be read from the environment variable ``PINECONE_API_KEY``. :type api_key: str, optional - :param host: The control plane host. If unspecified, the host ``api.pinecone.io`` will be used. + :param host: The control plane host to connect to. :type host: str, optional - :param proxy_url: The URL of the proxy to use for the connection. + :param proxy_url: The URL of the proxy to use for the connection. Default: ``None`` :type proxy_url: str, optional - :param ssl_ca_certs: The path to the SSL CA certificate bundle to use for the connection. This path should point to a file in PEM format. When not passed, the SDK will use the certificate bundle returned from ``certifi.where()``. + :param proxy_headers: Additional headers to pass to the proxy. Use this if your proxy setup requires authentication. Default: ``{}`` + :type proxy_headers: dict[str, str], optional + :param ssl_ca_certs: The path to the SSL CA certificate bundle to use for the connection. This path should point to a file in PEM format. Default: ``None`` :type ssl_ca_certs: str, optional - :param ssl_verify: SSL verification is performed by default, but can be disabled using the boolean flag when testing with Pinecone Local or troubleshooting a proxy setup. You should never run with SSL verification disabled in production. + :param ssl_verify: SSL verification is performed by default, but can be disabled using the boolean flag. Default: ``True`` :type ssl_verify: bool, optional - :param additional_headers: Additional headers to pass to the API. This is mainly to support internal testing at Pinecone. End users should not need to use this unless following specific instructions to do so. + :param config: A ``pinecone.config.Config`` object. If passed, the ``api_key`` and ``host`` parameters will be ignored. + :type config: pinecone.config.Config, optional + :param additional_headers: Additional headers to pass to the API. Default: ``{}`` :type additional_headers: dict[str, str], optional - .. note:: - The ``proxy_headers`` parameter is not currently supported for ``PineconeAsyncio``. + **Managing the async context** + The ``PineconeAsyncio`` class relies on an underlying ``aiohttp`` ``ClientSession`` to make asynchronous HTTP requests. To ensure that the session is properly closed, you + should use the ``async with`` syntax when creating a ``PineconeAsyncio`` object. This will ensure that the session is properly closed when the context is exited. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio(api_key='YOUR_API_KEY') as pc: + # Do async things + index_list = await pc.list_indexes() + + asyncio.run(main()) + + As an alternative, if you prefer to avoid code with a nested appearance and are willing to manage cleanup yourself, you can await the ``close()`` method to close the session when you are done. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + pc = PineconeAsyncio(api_key='YOUR_API_KEY') + + # Do async things + index_list = await pc.list_indexes() + + # You're responsible for calling this yourself + await pc.close() + + asyncio.run(main()) + + Failing to do this may result in error messages appearing from the underlying aiohttp library. + + **Configuration with environment variables** + + If you instantiate the PineconeAsyncio client with no arguments, it will attempt to read the API key from the environment variable ``PINECONE_API_KEY``. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio() as pc: + # Do async things + index_list = await pc.list_indexes() + + asyncio.run(main()) + + **Configuration with keyword arguments** + + If you prefer being more explicit in your code, you can also pass the API key as a keyword argument. + + .. code-block:: python + + import os + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio(api_key=os.environ.get("PINECONE_API_KEY")) as pc: + # Do async things + index_list = await pc.list_indexes() + + asyncio.run(main()) + + + **Environment variables** + + The Pinecone client supports the following environment variables: + + - ``PINECONE_API_KEY``: The API key to use for authentication. If not passed via + kwarg, the API key will be read from the environment variable ``PINECONE_API_KEY``. + + **Proxy configuration** + + If your network setup requires you to interact with Pinecone via a proxy, you will need + to pass additional configuration using optional keyword parameters. These optional parameters + are used to configure an SSL context and passed to ``aiohttp``, which is the underlying library + currently used by the PineconeAsyncio client to make HTTP requests. + + Here is a basic example: + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio( + api_key='YOUR_API_KEY', + proxy_url='https://your-proxy.com' + ) as pc: + # Do async things + index_list = await pc.list_indexes() + + asyncio.run(main()) + + + **Using proxies with self-signed certificates** + + By default the Pinecone Python client will perform SSL certificate verification + using the CA bundle maintained by Mozilla in the `certifi `_ package. + If your proxy server is using a self-signed certificate, you will need to pass the path to the certificate + in PEM format using the ``ssl_ca_certs`` parameter. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio( + api_key='YOUR_API_KEY', + proxy_url='https://your-proxy.com', + ssl_ca_certs='path/to/cert-bundle.pem' + ) as pc: + # Do async things + await pc.list_indexes() + + asyncio.run(main()) + + + **Disabling SSL verification** + + If you would like to disable SSL verification, you can pass the ``ssl_verify`` + parameter with a value of ``False``. We do not recommend going to production with SSL verification disabled + but there are situations where this is useful such as testing with Pinecone Local running in a docker + container. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio( + api_key='YOUR_API_KEY', + ssl_verify=False + ) as pc: + if not await pc.has_index('my_index'): + await pc.create_index( + name='my_index', + dimension=1536, + metric='cosine', + spec=ServerlessSpec(cloud='aws', region='us-west-2') + ) + + asyncio.run(main()) + + + **Passing additional headers** + + If you need to pass additional headers with each request to the Pinecone API, you can do so using the + ``additional_headers`` parameter. This is primarily for internal testing and end-users shouldn't need to + do this unless specifically instructed to do so. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio( + api_key='YOUR_API_KEY', + host='https://api-staging.pinecone.io', + additional_headers={'X-My-Header': 'my-value'} + ) as pc: + # Do async things + await pc.list_indexes() + + asyncio.run(main()) """ for deprecated_kwarg in {"config", "openapi_config"}: if deprecated_kwarg in kwargs: @@ -252,6 +431,125 @@ async def create_index( vector_type: ("VectorType" | str) | None = "dense", tags: dict[str, str] | None = None, ) -> "IndexModel": + """Creates a Pinecone index. + + :param name: The name of the index to create. Must be unique within your project and + cannot be changed once created. Allowed characters are lowercase letters, numbers, + and hyphens and the name may not begin or end with hyphens. Maximum length is 45 characters. + :type name: str + :param metric: Type of similarity metric used in the vector index when querying, one of ``{"cosine", "dotproduct", "euclidean"}``. + :type metric: str, optional + :param spec: A dictionary containing configurations describing how the index should be deployed. For serverless indexes, + specify region and cloud. Optionally, you can specify ``read_capacity`` to configure dedicated read capacity mode + (OnDemand or Dedicated) and ``schema`` to configure which metadata fields are filterable. For pod indexes, specify + replicas, shards, pods, pod_type, metadata_config, and source_collection. + Alternatively, use the ``ServerlessSpec`` or ``PodSpec`` objects to specify these configurations. + :type spec: Dict + :param dimension: If you are creating an index with ``vector_type="dense"`` (which is the default), you need to specify ``dimension`` to indicate the size of your vectors. + This should match the dimension of the embeddings you will be inserting. For example, if you are using + OpenAI's CLIP model, you should use ``dimension=1536``. Dimension is a required field when + creating an index with ``vector_type="dense"`` and should not be passed when ``vector_type="sparse"``. + :type dimension: int + :type timeout: int, optional + :param timeout: Specify the number of seconds to wait until index gets ready. If None, wait indefinitely; if >=0, time out after this many seconds; + if -1, return immediately and do not wait. + :param deletion_protection: If enabled, the index cannot be deleted. If disabled, the index can be deleted. + :type deletion_protection: Optional[Literal["enabled", "disabled"]] + :param vector_type: The type of vectors to be stored in the index. One of ``{"dense", "sparse"}``. + :type vector_type: str, optional + :param tags: Tags are key-value pairs you can attach to indexes to better understand, organize, and identify your resources. Some example use cases include tagging indexes with the name of the model that generated the embeddings, the date the index was created, or the purpose of the index. + :type tags: Optional[dict[str, str]] + :return: A ``IndexModel`` instance containing a description of the index that was created. + + **Creating a serverless index** + + .. code-block:: python + + import os + import asyncio + + from pinecone import ( + PineconeAsyncio, + ServerlessSpec, + CloudProvider, + AwsRegion, + Metric, + DeletionProtection, + VectorType + ) + + async def main(): + async with PineconeAsyncio(api_key=os.environ.get("PINECONE_API_KEY")) as pc: + await pc.create_index( + name="my_index", + dimension=1536, + metric=Metric.COSINE, + spec=ServerlessSpec( + cloud=CloudProvider.AWS, + region=AwsRegion.US_WEST_2, + read_capacity={ + "mode": "Dedicated", + "dedicated": { + "node_type": "t1", + "scaling": "Manual", + "manual": {"shards": 2, "replicas": 2}, + }, + }, + schema={ + "genre": {"filterable": True}, + "year": {"filterable": True}, + "rating": {"filterable": True}, + }, + ), + deletion_protection=DeletionProtection.DISABLED, + vector_type=VectorType.DENSE, + tags={ + "model": "clip", + "app": "image-search", + "env": "production" + } + ) + + asyncio.run(main()) + + + **Creating a pod index** + + .. code-block:: python + + import os + import asyncio + + from pinecone import ( + Pinecone, + PodSpec, + PodIndexEnvironment, + PodType, + Metric, + DeletionProtection, + VectorType + ) + + async def main(): + async with Pinecone(api_key=os.environ.get("PINECONE_API_KEY")) as pc: + await pc.create_index( + name="my_index", + dimension=1536, + metric=Metric.COSINE, + spec=PodSpec( + environment=PodIndexEnvironment.US_EAST4_GCP, + pod_type=PodType.P1_X1 + ), + deletion_protection=DeletionProtection.DISABLED, + tags={ + "model": "clip", + "app": "image-search", + "env": "testing" + } + ) + + asyncio.run(main()) + """ resp = await self.db.index.create( name=name, spec=spec, @@ -291,6 +589,123 @@ async def create_index_for_model( | None = None, timeout: int | None = None, ) -> "IndexModel": + """ + :param name: The name of the index to create. Must be unique within your project and + cannot be changed once created. Allowed characters are lowercase letters, numbers, + and hyphens and the name may not begin or end with hyphens. Maximum length is 45 characters. + :type name: str + :param cloud: The cloud provider to use for the index. One of ``{"aws", "gcp", "azure"}``. + :type cloud: str + :param region: The region to use for the index. Enum objects ``AwsRegion``, ``GcpRegion``, and ``AzureRegion`` are also available to help you quickly set these parameters, but may not be up to date as new regions become available. + :type region: str + :param embed: The embedding configuration for the index. This param accepts a dictionary or an instance of the ``IndexEmbed`` object. + :type embed: Union[Dict, IndexEmbed] + :param tags: Tags are key-value pairs you can attach to indexes to better understand, organize, and identify your resources. Some example use cases include tagging indexes with the name of the model that generated the embeddings, the date the index was created, or the purpose of the index. + :type tags: Optional[dict[str, str]] + :param deletion_protection: If enabled, the index cannot be deleted. If disabled, the index can be deleted. This setting can be changed with ``configure_index``. + :type deletion_protection: Optional[Literal["enabled", "disabled"]] + :param read_capacity: Optional read capacity configuration. You can specify ``read_capacity`` to configure dedicated read capacity mode + (OnDemand or Dedicated). See ``ServerlessSpec`` documentation for details on read capacity configuration. + :type read_capacity: Optional[Union[ReadCapacityDict, ReadCapacity, ReadCapacityOnDemandSpec, ReadCapacityDedicatedSpec]] + :param schema: Optional metadata schema configuration. You can specify ``schema`` to configure which metadata fields are filterable. + The schema can be provided as a dictionary mapping field names to their configurations (e.g., ``{"genre": {"filterable": True}}``) + or as a dictionary with a ``fields`` key (e.g., ``{"fields": {"genre": {"filterable": True}}}``). + :type schema: Optional[Union[dict[str, MetadataSchemaFieldConfig], dict[str, dict[str, Any]], BackupModelSchema]] + :type timeout: Optional[int] + :param timeout: Specify the number of seconds to wait until index is ready to receive data. If None, wait indefinitely; if >=0, time out after this many seconds; + if -1, return immediately and do not wait. + :return: A description of the index that was created. + :rtype: IndexModel + + This method is used to create a Serverless index that is configured for use with Pinecone's integrated inference models. + + The resulting index can be described, listed, configured, and deleted like any other Pinecone index with the ``describe_index``, ``list_indexes``, ``configure_index``, and ``delete_index`` methods. + + After the model is created, you can upsert records into the index with the ``upsert_records`` method, and search your records with the ``search`` method. + + .. code-block:: python + + import asyncio + + from pinecone import ( + PineconeAsyncio, + IndexEmbed, + CloudProvider, + AwsRegion, + EmbedModel, + Metric, + ) + + async def main(): + async with PineconeAsyncio() as pc: + if not await pc.has_index("book-search"): + desc = await pc.create_index_for_model( + name="book-search", + cloud=CloudProvider.AWS, + region=AwsRegion.US_EAST_1, + embed=IndexEmbed( + model=EmbedModel.Multilingual_E5_Large, + metric=Metric.COSINE, + field_map={ + "text": "description", + }, + ) + ) + + asyncio.run(main()) + + **Creating an index for model with schema and dedicated read capacity** + + .. code-block:: python + + import asyncio + + from pinecone import ( + PineconeAsyncio, + IndexEmbed, + CloudProvider, + AwsRegion, + EmbedModel, + Metric, + ) + + async def main(): + async with PineconeAsyncio() as pc: + if not await pc.has_index("book-search"): + desc = await pc.create_index_for_model( + name="book-search", + cloud=CloudProvider.AWS, + region=AwsRegion.US_EAST_1, + embed=IndexEmbed( + model=EmbedModel.Multilingual_E5_Large, + metric=Metric.COSINE, + field_map={ + "text": "description", + }, + ), + read_capacity={ + "mode": "Dedicated", + "dedicated": { + "node_type": "t1", + "scaling": "Manual", + "manual": {"shards": 2, "replicas": 2}, + }, + }, + schema={ + "genre": {"filterable": True}, + "year": {"filterable": True}, + "rating": {"filterable": True}, + }, + ) + + asyncio.run(main()) + + See also: + + * See `available cloud regions `_ + * See the `Model Gallery `_ to learn about available models + + """ return await self.db.index.create_for_model( name=name, cloud=cloud, @@ -313,6 +728,24 @@ async def create_index_from_backup( tags: dict[str, str] | None = None, timeout: int | None = None, ) -> "IndexModel": + """ + Create an index from a backup. + + Call ``list_backups`` to get a list of backups for your project. + + :param name: The name of the index to create. + :type name: str + :param backup_id: The ID of the backup to restore. + :type backup_id: str + :param deletion_protection: If enabled, the index cannot be deleted. If disabled, the index can be deleted. This setting can be changed with ``configure_index``. + :type deletion_protection: Optional[Literal["enabled", "disabled"]] + :param tags: Tags are key-value pairs you can attach to indexes to better understand, organize, and identify your resources. Some example use cases include tagging indexes with the name of the model that generated the embeddings, the date the index was created, or the purpose of the index. + :type tags: Optional[dict[str, str]] + :param timeout: Specify the number of seconds to wait until index is ready to receive data. If None, wait indefinitely; if >=0, time out after this many seconds; + if -1, return immediately and do not wait. + :return: A description of the index that was created. + :rtype: IndexModel + """ return await self.db.index.create_from_backup( name=name, backup_id=backup_id, @@ -322,15 +755,187 @@ async def create_index_from_backup( ) async def delete_index(self, name: str, timeout: int | None = None) -> None: + """ + :param name: the name of the index. + :type name: str + :param timeout: Number of seconds to poll status checking whether the index has been deleted. If None, + wait indefinitely; if >=0, time out after this many seconds; + if -1, return immediately and do not wait. + :type timeout: int, optional + + Deletes a Pinecone index. + + Deleting an index is an irreversible operation. All data in the index will be lost. + When you use this command, a request is sent to the Pinecone control plane to delete + the index, but the termination is not synchronous because resources take a few moments to + be released. + + By default the ``delete_index`` method will block until polling of the ``describe_index`` method + shows that the delete operation has completed. If you prefer to return immediately and not + wait for the index to be deleted, you can pass ``timeout=-1`` to the method. + + After the delete request is submitted, polling ``describe_index`` will show that the index + transitions into a ``Terminating`` state before eventually resulting in a 404 after it has been removed. + + This operation can fail if the index is configured with ``deletion_protection="enabled"``. + In this case, you will need to call ``configure_index`` to disable deletion protection before + you can delete the index. + + .. code-block:: python + + import asyncio + + from pinecone import PineconeAsyncio + + async def main(): + pc = PineconeAsyncio() + + index_name = "my_index" + desc = await pc.describe_index(name=index_name) + + if desc.deletion_protection == "enabled": + # If for some reason deletion protection is enabled, you will need to disable it first + # before you can delete the index. But use caution as this operation is not reversible + # and if somebody enabled deletion protection, they probably had a good reason. + await pc.configure_index(name=index_name, deletion_protection="disabled") + + await pc.delete_index(name=index_name) + await pc.close() + + asyncio.run(main()) + + """ return await self.db.index.delete(name=name, timeout=timeout) async def list_indexes(self) -> "IndexList": + """ + :return: Returns an ``IndexList`` object, which is iterable and contains a + list of ``IndexModel`` objects. The ``IndexList`` also has a convenience method ``names()`` + which returns a list of index names for situations where you just want to iterate over + all index names. + + Lists all indexes in your project. + + The results include a description of all indexes in your project, including the + index name, dimension, metric, status, and spec. + + If you simply want to check whether an index exists, see the ``has_index()`` convenience method. + + You can use the ``list_indexes()`` method to iterate over descriptions of every index in your project. + + .. code-block:: python + + import asyncio + + from pinecone import PineconeAsyncio + + async def main(): + pc = PineconeAsyncio() + + available_indexes = await pc.list_indexes() + for index in available_indexes: + print(index.name) + print(index.dimension) + print(index.metric) + print(index.status) + print(index.host) + print(index.spec) + + await pc.close() + + asyncio.run(main()) + + """ return await self.db.index.list() async def describe_index(self, name: str) -> "IndexModel": + """ + :param name: the name of the index to describe. + :return: Returns an ``IndexModel`` object + which gives access to properties such as the + index name, dimension, metric, host url, status, + and spec. + + Describes a Pinecone index. + + **Getting your index host url** + + In a real production situation, you probably want to + store the host url in an environment variable so you + don't have to call describe_index and re-fetch it + every time you want to use the index. But this example + shows how to get the value from the API using describe_index. + + .. code-block:: python + + import asyncio + from pinecone import Pinecone, PineconeAsyncio, Index + + async def main(): + pc = PineconeAsyncio() + + index_name="my_index" + description = await pc.describe_index(name=index_name) + print(description) + # { + # "name": "my_index", + # "metric": "cosine", + # "host": "my_index-dojoi3u.svc.aped-4627-b74a.pinecone.io", + # "spec": { + # "serverless": { + # "cloud": "aws", + # "region": "us-east-1" + # } + # }, + # "status": { + # "ready": true, + # "state": "Ready" + # }, + # "vector_type": "dense", + # "dimension": 1024, + # "deletion_protection": "enabled", + # "tags": { + # "environment": "production" + # } + # } + + print(f"Your index is hosted at {description.host}") + await pc.close() + + async with Pinecone().IndexAsyncio(host=description.host) as idx: + await idx.upsert(vectors=[...]) + + asyncio.run(main()) + + """ return await self.db.index.describe(name=name) async def has_index(self, name: str) -> bool: + """ + :param name: The name of the index to check for existence. + :return: Returns ``True`` if the index exists, ``False`` otherwise. + + Checks if a Pinecone index exists. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio, ServerlessSpec + + async def main(): + async with PineconeAsyncio() as pc: + index_name = "my_index" + if not await pc.has_index(index_name): + print("Index does not exist, creating...") + pc.create_index( + name=index_name, + dimension=768, + metric="cosine", + spec=ServerlessSpec(cloud="aws", region="us-west-2") + ) + + asyncio.run(main()) + """ return await self.db.index.has(name=name) async def configure_index( @@ -349,6 +954,149 @@ async def configure_index( ) | None = None, ) -> None: + """ + :param: name: the name of the Index + :param: replicas: the desired number of replicas, lowest value is 0. + :param: pod_type: the new pod_type for the index. To learn more about the + available pod types, please see `Understanding Indexes `_ + :param: deletion_protection: If set to 'enabled', the index cannot be deleted. If 'disabled', the index can be deleted. + :param: tags: A dictionary of tags to apply to the index. Tags are key-value pairs that can be used to organize and manage indexes. To remove a tag, set the value to "". Tags passed to configure_index will be merged with existing tags and any with the value empty string will be removed. + :param embed: configures the integrated inference embedding settings for the index. You can convert an existing index to an integrated index by specifying the embedding model and field_map. + The index vector type and dimension must match the model vector type and dimension, and the index similarity metric must be supported by the model. + You can later change the embedding configuration to update the field_map, read_parameters, or write_parameters. Once set, the model cannot be changed. + :type embed: Optional[Union[ConfigureIndexEmbed, Dict]], optional + :param read_capacity: Optional read capacity configuration for serverless indexes. You can specify ``read_capacity`` to configure dedicated read capacity mode + (OnDemand or Dedicated). See ``ServerlessSpec`` documentation for details on read capacity configuration. + Note that read capacity configuration is only available for serverless indexes. + :type read_capacity: Optional[Union[ReadCapacityDict, ReadCapacity, ReadCapacityOnDemandSpec, ReadCapacityDedicatedSpec]] + + This method is used to modify an index's configuration. It can be used to: + + - Configure read capacity for serverless indexes using ``read_capacity`` + - Scale a pod-based index horizontally using ``replicas`` + - Scale a pod-based index vertically using ``pod_type`` + - Enable or disable deletion protection using ``deletion_protection`` + - Add, change, or remove tags using ``tags`` + + **Configuring read capacity for serverless indexes** + + To configure read capacity for serverless indexes, pass the ``read_capacity`` parameter to the ``configure_index`` method. + You can configure either OnDemand or Dedicated read capacity mode. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio() as pc: + # Configure to OnDemand read capacity (default) + await pc.configure_index( + name="my_index", + read_capacity={"mode": "OnDemand"} + ) + + # Configure to Dedicated read capacity with manual scaling + await pc.configure_index( + name="my_index", + read_capacity={ + "mode": "Dedicated", + "dedicated": { + "node_type": "t1", + "scaling": "Manual", + "manual": {"shards": 1, "replicas": 1} + } + } + ) + + # Verify the configuration was applied + desc = await pc.describe_index("my_index") + assert desc.spec.serverless.read_capacity.mode == "Dedicated" + + asyncio.run(main()) + + **Scaling pod-based indexes** + + To scale your pod-based index, you pass a ``replicas`` and/or ``pod_type`` param to the ``configure_index`` method. ``pod_type`` may be a string or a value from the ``PodType`` enum. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio, PodType + + async def main(): + async with PineconeAsyncio() as pc: + await pc.configure_index( + name="my_index", + replicas=2, + pod_type=PodType.P1_X2 + ) + + asyncio.run(main()) + + + After providing these new configurations, you must call ``describe_index`` to see the status of the index as the changes are applied. + + **Enabling or disabling deletion protection** + + To enable or disable deletion protection, pass the ``deletion_protection`` parameter to the ``configure_index`` method. When deletion protection + is enabled, the index cannot be deleted with the ``delete_index`` method. + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio, DeletionProtection + + async def main(): + async with PineconeAsyncio() as pc: + # Enable deletion protection + await pc.configure_index( + name="my_index", + deletion_protection=DeletionProtection.ENABLED + ) + + # Call describe_index to see the change was applied. + desc = await pc.describe_index("my_index") + assert desc.deletion_protection == "enabled" + + # Disable deletion protection + await pc.configure_index( + name="my_index", + deletion_protection=DeletionProtection.DISABLED + ) + + asyncio.run(main()) + + + **Adding, changing, or removing tags** + + To add, change, or remove tags, pass the ``tags`` parameter to the ``configure_index`` method. When tags are passed using ``configure_index``, + they are merged with any existing tags already on the index. To remove a tag, set the value of the key to an empty string. + + .. code-block:: python + + import asyncio + + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio() as pc: + # Add a tag + await pc.configure_index(name="my_index", tags={"environment": "staging"}) + + # Change a tag + await pc.configure_index(name="my_index", tags={"environment": "production"}) + + # Remove a tag + await pc.configure_index(name="my_index", tags={"environment": ""}) + + # Call describe_index to view the tags are changed + await pc.describe_index("my_index") + print(desc.tags) + + asyncio.run(main()) + + """ return await self.db.index.configure( name=name, replicas=replicas, @@ -360,21 +1108,102 @@ async def configure_index( ) async def create_collection(self, name: str, source: str) -> None: + """Create a collection from a pod-based index + + :param name: Name of the collection + :param source: Name of the source index + """ return await self.db.collection.create(name=name, source=source) async def list_collections(self) -> "CollectionList": + """List all collections + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + pc = PineconeAsyncio() + + collections = await pc.list_collections() + for collection in collections: + print(collection.name) + print(collection.source) + + # You can also iterate specifically over + # a list of collection names by calling + # the .names() helper. + collection_name = "my_collection" + collections = await pc.list_collections() + if collection_name in collections.names(): + print('Collection exists') + + await pc.close() + + asyncio.run(main()) + + """ return await self.db.collection.list() async def delete_collection(self, name: str) -> None: + """Describes a collection. + :param: The name of the collection + :return: Description of the collection + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio() as pc: + + description = await pc.describe_collection("my_collection") + print(description.name) + print(description.source) + print(description.status) + print(description.size) + + asyncio.run(main()) + + """ return await self.db.collection.delete(name=name) async def describe_collection(self, name: str) -> dict[str, Any]: + """Describes a collection. + :param: The name of the collection + :return: Description of the collection + + .. code-block:: python + + import asyncio + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio() as pc: + description = await pc.describe_collection("my_collection") + print(description.name) + print(description.source) + print(description.status) + print(description.size) + + asyncio.run(main()) + + """ return await self.db.collection.describe(name=name) @require_kwargs async def create_backup( self, *, index_name: str, backup_name: str, description: str = "" ) -> "BackupModel": + """Create a backup of an index. + + Args: + index_name (str): The name of the index to backup. + backup_name (str): The name to give the backup. + description (str): Optional description of the backup. + """ return await self.db.backup.create( index_name=index_name, backup_name=backup_name, description=description ) @@ -387,29 +1216,123 @@ async def list_backups( limit: int | None = 10, pagination_token: str | None = None, ) -> "BackupList": + """List backups. + + If index_name is provided, the backups will be filtered by index. If no index_name is provided, all backups in the projectwill be returned. + + Args: + index_name (str): The name of the index to list backups for. + limit (int): The maximum number of backups to return. + pagination_token (str): The pagination token to use for pagination. + """ return await self.db.backup.list( index_name=index_name, limit=limit, pagination_token=pagination_token ) @require_kwargs async def describe_backup(self, *, backup_id: str) -> "BackupModel": + """Describe a backup. + + Args: + backup_id (str): The ID of the backup to describe. + """ return await self.db.backup.describe(backup_id=backup_id) @require_kwargs async def delete_backup(self, *, backup_id: str) -> None: + """Delete a backup. + + Args: + backup_id (str): The ID of the backup to delete. + """ return await self.db.backup.delete(backup_id=backup_id) @require_kwargs async def list_restore_jobs( self, *, limit: int | None = 10, pagination_token: str | None = None ) -> "RestoreJobList": + """List restore jobs. + + Args: + limit (int): The maximum number of restore jobs to return. + pagination_token (str): The pagination token to use for pagination. + """ return await self.db.restore_job.list(limit=limit, pagination_token=pagination_token) @require_kwargs async def describe_restore_job(self, *, job_id: str) -> "RestoreJobModel": + """Describe a restore job. + + Args: + job_id (str): The ID of the restore job to describe. + """ return await self.db.restore_job.describe(job_id=job_id) def IndexAsyncio(self, host: str, **kwargs) -> "_IndexAsyncio": + """ + Build an asyncio-compatible client for index data operations. + + :param host: The host url of the index. + + .. code-block:: python + + import os + import asyncio + + from pinecone import PineconeAsyncio + + api_key = os.environ.get("PINECONE_API_KEY") + index_host = os.environ.get("PINECONE_INDEX_HOST") + + async def main(): + async with Pinecone(api_key=api_key) as pc: + async with pc.Index(host=index_host) as idx: + # Now you're ready to perform data operations + await index.query(vector=[...], top_k=10) + + asyncio.run(main()) + + To find your host url, you can use the ``describe_index``. Or, alternatively, the + host is displayed in the Pinecone web console. + + .. code-block:: python + + import os + import asyncio + + from pinecone import PineconeAsyncio + + async def main(): + async with PineconeAsyncio( + api_key=os.environ.get("PINECONE_API_KEY") + ) as pc: + host = await pc.describe_index('index-name').host + + asyncio.run(main()) + + **Alternative setup** + + Like instances of the ``PineconeAsyncio`` class, instances of ``IndexAsyncio`` have async context that + needs to be cleaned up when you are done with it in order to avoid error messages about unclosed session from + aiohttp. Nesting these in code is a bit cumbersome, so if you are only planning to do data operations you + may prefer to setup the ``IndexAsyncio`` object via the ``Pinecone`` class which will avoid creating an outer async context. + + .. code-block:: python + + import os + import asyncio + from pinecone import Pinecone + + api_key = os.environ.get("PINECONE_API_KEY") + + async def main(): + pc = Pinecone(api_key=api_key) # sync client, so no async context to worry about + + async with pc.AsyncioIndex(host='your_index_host') as idx: + # Now you're ready to perform data operations + await idx.query(vector=[...], top_k=10) + + """ from pinecone.db_data import _IndexAsyncio api_key = self._config.api_key diff --git a/pinecone/pinecone_interface_asyncio.py b/pinecone/pinecone_interface_asyncio.py deleted file mode 100644 index d22f2531..00000000 --- a/pinecone/pinecone_interface_asyncio.py +++ /dev/null @@ -1,1157 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod - -from typing import Dict, TYPE_CHECKING, Any - -if TYPE_CHECKING: - from pinecone.config import Config - - from pinecone.core.openapi.db_control.api.manage_indexes_api import ManageIndexesApi - - from pinecone.db_control.models import ( - ServerlessSpec, - PodSpec, - ByocSpec, - IndexList, - CollectionList, - IndexModel, - IndexEmbed, - BackupModel, - BackupList, - RestoreJobModel, - RestoreJobList, - ) - from pinecone.db_control.enums import ( - Metric, - VectorType, - DeletionProtection, - PodType, - CloudProvider, - AwsRegion, - GcpRegion, - AzureRegion, - ) - from pinecone.db_control.types import ConfigureIndexEmbed, CreateIndexForModelEmbedTypedDict - from pinecone.db_control.models.serverless_spec import ( - ReadCapacityDict, - MetadataSchemaFieldConfig, - ) - from pinecone.core.openapi.db_control.model.read_capacity import ReadCapacity - from pinecone.core.openapi.db_control.model.read_capacity_on_demand_spec import ( - ReadCapacityOnDemandSpec, - ) - from pinecone.core.openapi.db_control.model.read_capacity_dedicated_spec import ( - ReadCapacityDedicatedSpec, - ) - from pinecone.core.openapi.db_control.model.backup_model_schema import BackupModelSchema - - -class PineconeAsyncioDBControlInterface(ABC): - @abstractmethod - def __init__( - self, - api_key: str | None = None, - host: str | None = None, - proxy_url: str | None = None, - proxy_headers: dict[str, str] | None = None, - ssl_ca_certs: str | None = None, - ssl_verify: bool | None = None, - config: "Config" | None = None, - additional_headers: dict[str, str] | None = {}, - pool_threads: int | None = 1, - index_api: "ManageIndexesApi" | None = None, - **kwargs, - ): - """ - The ``PineconeAsyncio`` class is the main entry point for interacting with Pinecone using asyncio. - It is used to create, delete, and manage your indexes and collections. Except for needing to use - ``async with`` when instantiating the client and ``await`` when calling its methods, the functionality - provided by this class is extremely similar to the functionality of the ``Pinecone`` class. - - :param api_key: The API key to use for authentication. If not passed via kwarg, the API key will be read from the environment variable ``PINECONE_API_KEY``. - :type api_key: str, optional - :param host: The control plane host to connect to. - :type host: str, optional - :param proxy_url: The URL of the proxy to use for the connection. Default: ``None`` - :type proxy_url: str, optional - :param proxy_headers: Additional headers to pass to the proxy. Use this if your proxy setup requires authentication. Default: ``{}`` - :type proxy_headers: dict[str, str], optional - :param ssl_ca_certs: The path to the SSL CA certificate bundle to use for the connection. This path should point to a file in PEM format. Default: ``None`` - :type ssl_ca_certs: str, optional - :param ssl_verify: SSL verification is performed by default, but can be disabled using the boolean flag. Default: ``True`` - :type ssl_verify: bool, optional - :param config: A ``pinecone.config.Config`` object. If passed, the ``api_key`` and ``host`` parameters will be ignored. - :type config: pinecone.config.Config, optional - :param additional_headers: Additional headers to pass to the API. Default: ``{}`` - :type additional_headers: dict[str, str], optional - - - **Managing the async context** - - The ``PineconeAsyncio`` class relies on an underlying ``aiohttp`` ``ClientSession`` to make asynchronous HTTP requests. To ensure that the session is properly closed, you - should use the ``async with`` syntax when creating a ``PineconeAsyncio`` object. This will ensure that the session is properly closed when the context is exited. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio(api_key='YOUR_API_KEY') as pc: - # Do async things - index_list = await pc.list_indexes() - - asyncio.run(main()) - - As an alternative, if you prefer to avoid code with a nested appearance and are willing to manage cleanup yourself, you can await the ``close()`` method to close the session when you are done. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - pc = PineconeAsyncio(api_key='YOUR_API_KEY') - - # Do async things - index_list = await pc.list_indexes() - - # You're responsible for calling this yourself - await pc.close() - - asyncio.run(main()) - - Failing to do this may result in error messages appearing from the underlying aiohttp library. - - **Configuration with environment variables** - - If you instantiate the PineconeAsyncio client with no arguments, it will attempt to read the API key from the environment variable ``PINECONE_API_KEY``. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio() as pc: - # Do async things - index_list = await pc.list_indexes() - - asyncio.run(main()) - - **Configuration with keyword arguments** - - If you prefer being more explicit in your code, you can also pass the API key as a keyword argument. - - .. code-block:: python - - import os - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio(api_key=os.environ.get("PINECONE_API_KEY")) as pc: - # Do async things - index_list = await pc.list_indexes() - - asyncio.run(main()) - - - **Environment variables** - - The Pinecone client supports the following environment variables: - - - ``PINECONE_API_KEY``: The API key to use for authentication. If not passed via - kwarg, the API key will be read from the environment variable ``PINECONE_API_KEY``. - - **Proxy configuration** - - If your network setup requires you to interact with Pinecone via a proxy, you will need - to pass additional configuration using optional keyword parameters. These optional parameters - are used to configure an SSL context and passed to ``aiohttp``, which is the underlying library - currently used by the PineconeAsyncio client to make HTTP requests. - - Here is a basic example: - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio( - api_key='YOUR_API_KEY', - proxy_url='https://your-proxy.com' - ) as pc: - # Do async things - index_list = await pc.list_indexes() - - asyncio.run(main()) - - - **Using proxies with self-signed certificates** - - By default the Pinecone Python client will perform SSL certificate verification - using the CA bundle maintained by Mozilla in the `certifi `_ package. - If your proxy server is using a self-signed certificate, you will need to pass the path to the certificate - in PEM format using the ``ssl_ca_certs`` parameter. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio( - api_key='YOUR_API_KEY', - proxy_url='https://your-proxy.com', - ssl_ca_certs='path/to/cert-bundle.pem' - ) as pc: - # Do async things - await pc.list_indexes() - - asyncio.run(main()) - - - **Disabling SSL verification** - - If you would like to disable SSL verification, you can pass the ``ssl_verify`` - parameter with a value of ``False``. We do not recommend going to production with SSL verification disabled - but there are situations where this is useful such as testing with Pinecone Local running in a docker - container. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio( - api_key='YOUR_API_KEY', - ssl_verify=False - ) as pc: - if not await pc.has_index('my_index'): - await pc.create_index( - name='my_index', - dimension=1536, - metric='cosine', - spec=ServerlessSpec(cloud='aws', region='us-west-2') - ) - - asyncio.run(main()) - - - **Passing additional headers** - - If you need to pass additional headers with each request to the Pinecone API, you can do so using the - ``additional_headers`` parameter. This is primarily for internal testing and end-users shouldn't need to - do this unless specifically instructed to do so. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio( - api_key='YOUR_API_KEY', - host='https://api-staging.pinecone.io', - additional_headers={'X-My-Header': 'my-value'} - ) as pc: - # Do async things - await pc.list_indexes() - - asyncio.run(main()) - """ - - pass - - @abstractmethod - async def create_index( - self, - name: str, - spec: Dict | "ServerlessSpec" | "PodSpec" | "ByocSpec", - dimension: int | None, - metric: ("Metric" | str) | None = "cosine", - timeout: int | None = None, - deletion_protection: ("DeletionProtection" | str) | None = "disabled", - vector_type: ("VectorType" | str) | None = "dense", - tags: dict[str, str] | None = None, - ): - """Creates a Pinecone index. - - :param name: The name of the index to create. Must be unique within your project and - cannot be changed once created. Allowed characters are lowercase letters, numbers, - and hyphens and the name may not begin or end with hyphens. Maximum length is 45 characters. - :type name: str - :param metric: Type of similarity metric used in the vector index when querying, one of ``{"cosine", "dotproduct", "euclidean"}``. - :type metric: str, optional - :param spec: A dictionary containing configurations describing how the index should be deployed. For serverless indexes, - specify region and cloud. Optionally, you can specify ``read_capacity`` to configure dedicated read capacity mode - (OnDemand or Dedicated) and ``schema`` to configure which metadata fields are filterable. For pod indexes, specify - replicas, shards, pods, pod_type, metadata_config, and source_collection. - Alternatively, use the ``ServerlessSpec`` or ``PodSpec`` objects to specify these configurations. - :type spec: Dict - :param dimension: If you are creating an index with ``vector_type="dense"`` (which is the default), you need to specify ``dimension`` to indicate the size of your vectors. - This should match the dimension of the embeddings you will be inserting. For example, if you are using - OpenAI's CLIP model, you should use ``dimension=1536``. Dimension is a required field when - creating an index with ``vector_type="dense"`` and should not be passed when ``vector_type="sparse"``. - :type dimension: int - :type timeout: int, optional - :param timeout: Specify the number of seconds to wait until index gets ready. If None, wait indefinitely; if >=0, time out after this many seconds; - if -1, return immediately and do not wait. - :param deletion_protection: If enabled, the index cannot be deleted. If disabled, the index can be deleted. - :type deletion_protection: Optional[Literal["enabled", "disabled"]] - :param vector_type: The type of vectors to be stored in the index. One of ``{"dense", "sparse"}``. - :type vector_type: str, optional - :param tags: Tags are key-value pairs you can attach to indexes to better understand, organize, and identify your resources. Some example use cases include tagging indexes with the name of the model that generated the embeddings, the date the index was created, or the purpose of the index. - :type tags: Optional[dict[str, str]] - :return: A ``IndexModel`` instance containing a description of the index that was created. - - **Creating a serverless index** - - .. code-block:: python - - import os - import asyncio - - from pinecone import ( - PineconeAsyncio, - ServerlessSpec, - CloudProvider, - AwsRegion, - Metric, - DeletionProtection, - VectorType - ) - - async def main(): - async with PineconeAsyncio(api_key=os.environ.get("PINECONE_API_KEY")) as pc: - await pc.create_index( - name="my_index", - dimension=1536, - metric=Metric.COSINE, - spec=ServerlessSpec( - cloud=CloudProvider.AWS, - region=AwsRegion.US_WEST_2, - read_capacity={ - "mode": "Dedicated", - "dedicated": { - "node_type": "t1", - "scaling": "Manual", - "manual": {"shards": 2, "replicas": 2}, - }, - }, - schema={ - "genre": {"filterable": True}, - "year": {"filterable": True}, - "rating": {"filterable": True}, - }, - ), - deletion_protection=DeletionProtection.DISABLED, - vector_type=VectorType.DENSE, - tags={ - "model": "clip", - "app": "image-search", - "env": "production" - } - ) - - asyncio.run(main()) - - - **Creating a pod index** - - .. code-block:: python - - import os - import asyncio - - from pinecone import ( - Pinecone, - PodSpec, - PodIndexEnvironment, - PodType, - Metric, - DeletionProtection, - VectorType - ) - - async def main(): - async with Pinecone(api_key=os.environ.get("PINECONE_API_KEY")) as pc: - await pc.create_index( - name="my_index", - dimension=1536, - metric=Metric.COSINE, - spec=PodSpec( - environment=PodIndexEnvironment.US_EAST4_GCP, - pod_type=PodType.P1_X1 - ), - deletion_protection=DeletionProtection.DISABLED, - tags={ - "model": "clip", - "app": "image-search", - "env": "testing" - } - ) - - asyncio.run(main()) - """ - pass - - @abstractmethod - async def create_index_for_model( - self, - name: str, - cloud: "CloudProvider" | str, - region: "AwsRegion" | "GcpRegion" | "AzureRegion" | str, - embed: "IndexEmbed" | "CreateIndexForModelEmbedTypedDict", - tags: dict[str, str] | None = None, - deletion_protection: ("DeletionProtection" | str) | None = "disabled", - read_capacity: ( - "ReadCapacityDict" - | "ReadCapacity" - | "ReadCapacityOnDemandSpec" - | "ReadCapacityDedicatedSpec" - ) - | None = None, - schema: ( - dict[ - str, "MetadataSchemaFieldConfig" - ] # Direct field mapping: {field_name: {filterable: bool}} - | dict[ - str, dict[str, Any] - ] # Dict with "fields" wrapper: {"fields": {field_name: {...}}, ...} - | "BackupModelSchema" # OpenAPI model instance - ) - | None = None, - timeout: int | None = None, - ) -> "IndexModel": - """ - :param name: The name of the index to create. Must be unique within your project and - cannot be changed once created. Allowed characters are lowercase letters, numbers, - and hyphens and the name may not begin or end with hyphens. Maximum length is 45 characters. - :type name: str - :param cloud: The cloud provider to use for the index. One of ``{"aws", "gcp", "azure"}``. - :type cloud: str - :param region: The region to use for the index. Enum objects ``AwsRegion``, ``GcpRegion``, and ``AzureRegion`` are also available to help you quickly set these parameters, but may not be up to date as new regions become available. - :type region: str - :param embed: The embedding configuration for the index. This param accepts a dictionary or an instance of the ``IndexEmbed`` object. - :type embed: Union[Dict, IndexEmbed] - :param tags: Tags are key-value pairs you can attach to indexes to better understand, organize, and identify your resources. Some example use cases include tagging indexes with the name of the model that generated the embeddings, the date the index was created, or the purpose of the index. - :type tags: Optional[dict[str, str]] - :param deletion_protection: If enabled, the index cannot be deleted. If disabled, the index can be deleted. This setting can be changed with ``configure_index``. - :type deletion_protection: Optional[Literal["enabled", "disabled"]] - :param read_capacity: Optional read capacity configuration. You can specify ``read_capacity`` to configure dedicated read capacity mode - (OnDemand or Dedicated). See ``ServerlessSpec`` documentation for details on read capacity configuration. - :type read_capacity: Optional[Union[ReadCapacityDict, ReadCapacity, ReadCapacityOnDemandSpec, ReadCapacityDedicatedSpec]] - :param schema: Optional metadata schema configuration. You can specify ``schema`` to configure which metadata fields are filterable. - The schema can be provided as a dictionary mapping field names to their configurations (e.g., ``{"genre": {"filterable": True}}``) - or as a dictionary with a ``fields`` key (e.g., ``{"fields": {"genre": {"filterable": True}}}``). - :type schema: Optional[Union[dict[str, MetadataSchemaFieldConfig], dict[str, dict[str, Any]], BackupModelSchema]] - :type timeout: Optional[int] - :param timeout: Specify the number of seconds to wait until index is ready to receive data. If None, wait indefinitely; if >=0, time out after this many seconds; - if -1, return immediately and do not wait. - :return: A description of the index that was created. - :rtype: IndexModel - - This method is used to create a Serverless index that is configured for use with Pinecone's integrated inference models. - - The resulting index can be described, listed, configured, and deleted like any other Pinecone index with the ``describe_index``, ``list_indexes``, ``configure_index``, and ``delete_index`` methods. - - After the model is created, you can upsert records into the index with the ``upsert_records`` method, and search your records with the ``search`` method. - - .. code-block:: python - - import asyncio - - from pinecone import ( - PineconeAsyncio, - IndexEmbed, - CloudProvider, - AwsRegion, - EmbedModel, - Metric, - ) - - async def main(): - async with PineconeAsyncio() as pc: - if not await pc.has_index("book-search"): - desc = await pc.create_index_for_model( - name="book-search", - cloud=CloudProvider.AWS, - region=AwsRegion.US_EAST_1, - embed=IndexEmbed( - model=EmbedModel.Multilingual_E5_Large, - metric=Metric.COSINE, - field_map={ - "text": "description", - }, - ) - ) - - asyncio.run(main()) - - **Creating an index for model with schema and dedicated read capacity** - - .. code-block:: python - - import asyncio - - from pinecone import ( - PineconeAsyncio, - IndexEmbed, - CloudProvider, - AwsRegion, - EmbedModel, - Metric, - ) - - async def main(): - async with PineconeAsyncio() as pc: - if not await pc.has_index("book-search"): - desc = await pc.create_index_for_model( - name="book-search", - cloud=CloudProvider.AWS, - region=AwsRegion.US_EAST_1, - embed=IndexEmbed( - model=EmbedModel.Multilingual_E5_Large, - metric=Metric.COSINE, - field_map={ - "text": "description", - }, - ), - read_capacity={ - "mode": "Dedicated", - "dedicated": { - "node_type": "t1", - "scaling": "Manual", - "manual": {"shards": 2, "replicas": 2}, - }, - }, - schema={ - "genre": {"filterable": True}, - "year": {"filterable": True}, - "rating": {"filterable": True}, - }, - ) - - asyncio.run(main()) - - See also: - - * See `available cloud regions `_ - * See the `Model Gallery `_ to learn about available models - - """ - pass - - @abstractmethod - async def create_index_from_backup( - self, - *, - name: str, - backup_id: str, - deletion_protection: ("DeletionProtection" | str) | None = "disabled", - tags: dict[str, str] | None = None, - timeout: int | None = None, - ) -> "IndexModel": - """ - Create an index from a backup. - - Call ``list_backups`` to get a list of backups for your project. - - :param name: The name of the index to create. - :type name: str - :param backup_id: The ID of the backup to restore. - :type backup_id: str - :param deletion_protection: If enabled, the index cannot be deleted. If disabled, the index can be deleted. This setting can be changed with ``configure_index``. - :type deletion_protection: Optional[Literal["enabled", "disabled"]] - :param tags: Tags are key-value pairs you can attach to indexes to better understand, organize, and identify your resources. Some example use cases include tagging indexes with the name of the model that generated the embeddings, the date the index was created, or the purpose of the index. - :type tags: Optional[dict[str, str]] - :param timeout: Specify the number of seconds to wait until index is ready to receive data. If None, wait indefinitely; if >=0, time out after this many seconds; - if -1, return immediately and do not wait. - :return: A description of the index that was created. - :rtype: IndexModel - """ - pass - - @abstractmethod - async def delete_index(self, name: str, timeout: int | None = None): - """ - :param name: the name of the index. - :type name: str - :param timeout: Number of seconds to poll status checking whether the index has been deleted. If None, - wait indefinitely; if >=0, time out after this many seconds; - if -1, return immediately and do not wait. - :type timeout: int, optional - - Deletes a Pinecone index. - - Deleting an index is an irreversible operation. All data in the index will be lost. - When you use this command, a request is sent to the Pinecone control plane to delete - the index, but the termination is not synchronous because resources take a few moments to - be released. - - By default the ``delete_index`` method will block until polling of the ``describe_index`` method - shows that the delete operation has completed. If you prefer to return immediately and not - wait for the index to be deleted, you can pass ``timeout=-1`` to the method. - - After the delete request is submitted, polling ``describe_index`` will show that the index - transitions into a ``Terminating`` state before eventually resulting in a 404 after it has been removed. - - This operation can fail if the index is configured with ``deletion_protection="enabled"``. - In this case, you will need to call ``configure_index`` to disable deletion protection before - you can delete the index. - - .. code-block:: python - - import asyncio - - from pinecone import PineconeAsyncio - - async def main(): - pc = PineconeAsyncio() - - index_name = "my_index" - desc = await pc.describe_index(name=index_name) - - if desc.deletion_protection == "enabled": - # If for some reason deletion protection is enabled, you will need to disable it first - # before you can delete the index. But use caution as this operation is not reversible - # and if somebody enabled deletion protection, they probably had a good reason. - await pc.configure_index(name=index_name, deletion_protection="disabled") - - await pc.delete_index(name=index_name) - await pc.close() - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def list_indexes(self) -> "IndexList": - """ - :return: Returns an ``IndexList`` object, which is iterable and contains a - list of ``IndexModel`` objects. The ``IndexList`` also has a convenience method ``names()`` - which returns a list of index names for situations where you just want to iterate over - all index names. - - Lists all indexes in your project. - - The results include a description of all indexes in your project, including the - index name, dimension, metric, status, and spec. - - If you simply want to check whether an index exists, see the ``has_index()`` convenience method. - - You can use the ``list_indexes()`` method to iterate over descriptions of every index in your project. - - .. code-block:: python - - import asyncio - - from pinecone import PineconeAsyncio - - async def main(): - pc = PineconeAsyncio() - - available_indexes = await pc.list_indexes() - for index in available_indexes: - print(index.name) - print(index.dimension) - print(index.metric) - print(index.status) - print(index.host) - print(index.spec) - - await pc.close() - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def describe_index(self, name: str) -> "IndexModel": - """ - :param name: the name of the index to describe. - :return: Returns an ``IndexModel`` object - which gives access to properties such as the - index name, dimension, metric, host url, status, - and spec. - - Describes a Pinecone index. - - **Getting your index host url** - - In a real production situation, you probably want to - store the host url in an environment variable so you - don't have to call describe_index and re-fetch it - every time you want to use the index. But this example - shows how to get the value from the API using describe_index. - - .. code-block:: python - - import asyncio - from pinecone import Pinecone, PineconeAsyncio, Index - - async def main(): - pc = PineconeAsyncio() - - index_name="my_index" - description = await pc.describe_index(name=index_name) - print(description) - # { - # "name": "my_index", - # "metric": "cosine", - # "host": "my_index-dojoi3u.svc.aped-4627-b74a.pinecone.io", - # "spec": { - # "serverless": { - # "cloud": "aws", - # "region": "us-east-1" - # } - # }, - # "status": { - # "ready": true, - # "state": "Ready" - # }, - # "vector_type": "dense", - # "dimension": 1024, - # "deletion_protection": "enabled", - # "tags": { - # "environment": "production" - # } - # } - - print(f"Your index is hosted at {description.host}") - await pc.close() - - async with Pinecone().IndexAsyncio(host=description.host) as idx: - await idx.upsert(vectors=[...]) - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def has_index(self, name: str) -> bool: - """ - :param name: The name of the index to check for existence. - :return: Returns ``True`` if the index exists, ``False`` otherwise. - - Checks if a Pinecone index exists. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio, ServerlessSpec - - async def main(): - async with PineconeAsyncio() as pc: - index_name = "my_index" - if not await pc.has_index(index_name): - print("Index does not exist, creating...") - pc.create_index( - name=index_name, - dimension=768, - metric="cosine", - spec=ServerlessSpec(cloud="aws", region="us-west-2") - ) - - asyncio.run(main()) - """ - pass - - @abstractmethod - async def configure_index( - self, - name: str, - replicas: int | None = None, - pod_type: ("PodType" | str) | None = None, - deletion_protection: ("DeletionProtection" | str) | None = None, - tags: dict[str, str] | None = None, - embed: ("ConfigureIndexEmbed" | Dict) | None = None, - read_capacity: ( - "ReadCapacityDict" - | "ReadCapacity" - | "ReadCapacityOnDemandSpec" - | "ReadCapacityDedicatedSpec" - ) - | None = None, - ): - """ - :param: name: the name of the Index - :param: replicas: the desired number of replicas, lowest value is 0. - :param: pod_type: the new pod_type for the index. To learn more about the - available pod types, please see `Understanding Indexes `_ - :param: deletion_protection: If set to 'enabled', the index cannot be deleted. If 'disabled', the index can be deleted. - :param: tags: A dictionary of tags to apply to the index. Tags are key-value pairs that can be used to organize and manage indexes. To remove a tag, set the value to "". Tags passed to configure_index will be merged with existing tags and any with the value empty string will be removed. - :param embed: configures the integrated inference embedding settings for the index. You can convert an existing index to an integrated index by specifying the embedding model and field_map. - The index vector type and dimension must match the model vector type and dimension, and the index similarity metric must be supported by the model. - You can later change the embedding configuration to update the field_map, read_parameters, or write_parameters. Once set, the model cannot be changed. - :type embed: Optional[Union[ConfigureIndexEmbed, Dict]], optional - :param read_capacity: Optional read capacity configuration for serverless indexes. You can specify ``read_capacity`` to configure dedicated read capacity mode - (OnDemand or Dedicated). See ``ServerlessSpec`` documentation for details on read capacity configuration. - Note that read capacity configuration is only available for serverless indexes. - :type read_capacity: Optional[Union[ReadCapacityDict, ReadCapacity, ReadCapacityOnDemandSpec, ReadCapacityDedicatedSpec]] - - This method is used to modify an index's configuration. It can be used to: - - - Configure read capacity for serverless indexes using ``read_capacity`` - - Scale a pod-based index horizontally using ``replicas`` - - Scale a pod-based index vertically using ``pod_type`` - - Enable or disable deletion protection using ``deletion_protection`` - - Add, change, or remove tags using ``tags`` - - **Configuring read capacity for serverless indexes** - - To configure read capacity for serverless indexes, pass the ``read_capacity`` parameter to the ``configure_index`` method. - You can configure either OnDemand or Dedicated read capacity mode. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio() as pc: - # Configure to OnDemand read capacity (default) - await pc.configure_index( - name="my_index", - read_capacity={"mode": "OnDemand"} - ) - - # Configure to Dedicated read capacity with manual scaling - await pc.configure_index( - name="my_index", - read_capacity={ - "mode": "Dedicated", - "dedicated": { - "node_type": "t1", - "scaling": "Manual", - "manual": {"shards": 1, "replicas": 1} - } - } - ) - - # Verify the configuration was applied - desc = await pc.describe_index("my_index") - assert desc.spec.serverless.read_capacity.mode == "Dedicated" - - asyncio.run(main()) - - **Scaling pod-based indexes** - - To scale your pod-based index, you pass a ``replicas`` and/or ``pod_type`` param to the ``configure_index`` method. ``pod_type`` may be a string or a value from the ``PodType`` enum. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio, PodType - - async def main(): - async with PineconeAsyncio() as pc: - await pc.configure_index( - name="my_index", - replicas=2, - pod_type=PodType.P1_X2 - ) - - asyncio.run(main()) - - - After providing these new configurations, you must call ``describe_index`` to see the status of the index as the changes are applied. - - **Enabling or disabling deletion protection** - - To enable or disable deletion protection, pass the ``deletion_protection`` parameter to the ``configure_index`` method. When deletion protection - is enabled, the index cannot be deleted with the ``delete_index`` method. - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio, DeletionProtection - - async def main(): - async with PineconeAsyncio() as pc: - # Enable deletion protection - await pc.configure_index( - name="my_index", - deletion_protection=DeletionProtection.ENABLED - ) - - # Call describe_index to see the change was applied. - desc = await pc.describe_index("my_index") - assert desc.deletion_protection == "enabled" - - # Disable deletion protection - await pc.configure_index( - name="my_index", - deletion_protection=DeletionProtection.DISABLED - ) - - asyncio.run(main()) - - - **Adding, changing, or removing tags** - - To add, change, or remove tags, pass the ``tags`` parameter to the ``configure_index`` method. When tags are passed using ``configure_index``, - they are merged with any existing tags already on the index. To remove a tag, set the value of the key to an empty string. - - .. code-block:: python - - import asyncio - - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio() as pc: - # Add a tag - await pc.configure_index(name="my_index", tags={"environment": "staging"}) - - # Change a tag - await pc.configure_index(name="my_index", tags={"environment": "production"}) - - # Remove a tag - await pc.configure_index(name="my_index", tags={"environment": ""}) - - # Call describe_index to view the tags are changed - await pc.describe_index("my_index") - print(desc.tags) - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def create_backup( - self, *, index_name: str, backup_name: str, description: str = "" - ) -> "BackupModel": - """Create a backup of an index. - - Args: - index_name (str): The name of the index to backup. - backup_name (str): The name to give the backup. - description (str): Optional description of the backup. - """ - pass - - @abstractmethod - async def list_backups( - self, - *, - index_name: str | None = None, - limit: int | None = 10, - pagination_token: str | None = None, - ) -> "BackupList": - """List backups. - - If index_name is provided, the backups will be filtered by index. If no index_name is provided, all backups in the projectwill be returned. - - Args: - index_name (str): The name of the index to list backups for. - limit (int): The maximum number of backups to return. - pagination_token (str): The pagination token to use for pagination. - """ - pass - - @abstractmethod - async def describe_backup(self, *, backup_id: str) -> "BackupModel": - """Describe a backup. - - Args: - backup_id (str): The ID of the backup to describe. - """ - pass - - @abstractmethod - async def delete_backup(self, *, backup_id: str) -> None: - """Delete a backup. - - Args: - backup_id (str): The ID of the backup to delete. - """ - pass - - @abstractmethod - async def list_restore_jobs( - self, *, limit: int | None = 10, pagination_token: str | None = None - ) -> "RestoreJobList": - """List restore jobs. - - Args: - limit (int): The maximum number of restore jobs to return. - pagination_token (str): The pagination token to use for pagination. - """ - pass - - @abstractmethod - async def describe_restore_job(self, *, job_id: str) -> "RestoreJobModel": - """Describe a restore job. - - Args: - job_id (str): The ID of the restore job to describe. - """ - pass - - @abstractmethod - async def create_collection(self, name: str, source: str): - """Create a collection from a pod-based index - - :param name: Name of the collection - :param source: Name of the source index - """ - pass - - @abstractmethod - async def list_collections(self) -> "CollectionList": - """List all collections - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - pc = PineconeAsyncio() - - collections = await pc.list_collections() - for collection in collections: - print(collection.name) - print(collection.source) - - # You can also iterate specifically over - # a list of collection names by calling - # the .names() helper. - collection_name = "my_collection" - collections = await pc.list_collections() - if collection_name in collections.names(): - print('Collection exists') - - await pc.close() - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def delete_collection(self, name: str): - """Describes a collection. - :param: The name of the collection - :return: Description of the collection - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio() as pc: - - description = await pc.describe_collection("my_collection") - print(description.name) - print(description.source) - print(description.status) - print(description.size) - - asyncio.run(main()) - - """ - pass - - @abstractmethod - async def describe_collection(self, name: str): - """Describes a collection. - :param: The name of the collection - :return: Description of the collection - - .. code-block:: python - - import asyncio - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio() as pc: - description = await pc.describe_collection("my_collection") - print(description.name) - print(description.source) - print(description.status) - print(description.size) - - asyncio.run(main()) - - """ - pass - - @abstractmethod - def IndexAsyncio(self, host, **kwargs): - """ - Build an asyncio-compatible client for index data operations. - - :param host: The host url of the index. - - .. code-block:: python - - import os - import asyncio - - from pinecone import PineconeAsyncio - - api_key = os.environ.get("PINECONE_API_KEY") - index_host = os.environ.get("PINECONE_INDEX_HOST") - - async def main(): - async with Pinecone(api_key=api_key) as pc: - async with pc.Index(host=index_host) as idx: - # Now you're ready to perform data operations - await index.query(vector=[...], top_k=10) - - asyncio.run(main()) - - To find your host url, you can use the ``describe_index``. Or, alternatively, the - host is displayed in the Pinecone web console. - - .. code-block:: python - - import os - import asyncio - - from pinecone import PineconeAsyncio - - async def main(): - async with PineconeAsyncio( - api_key=os.environ.get("PINECONE_API_KEY") - ) as pc: - host = await pc.describe_index('index-name').host - - asyncio.run(main()) - - **Alternative setup** - - Like instances of the ``PineconeAsyncio`` class, instances of ``IndexAsyncio`` have async context that - needs to be cleaned up when you are done with it in order to avoid error messages about unclosed session from - aiohttp. Nesting these in code is a bit cumbersome, so if you are only planning to do data operations you - may prefer to setup the ``IndexAsyncio`` object via the ``Pinecone`` class which will avoid creating an outer async context. - - .. code-block:: python - - import os - import asyncio - from pinecone import Pinecone - - api_key = os.environ.get("PINECONE_API_KEY") - - async def main(): - pc = Pinecone(api_key=api_key) # sync client, so no async context to worry about - - async with pc.AsyncioIndex(host='your_index_host') as idx: - # Now you're ready to perform data operations - await idx.query(vector=[...], top_k=10) - - """ - pass