Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
AsyncCollection.py551 linesDownload Raw Back to models
1from typing import TYPE_CHECKING, Optional, Union, List, cast
2
3from chromadb.api.types import (
4    URI,
5    CollectionMetadata,
6    Embedding,
7    PyEmbedding,
8    Include,
9    IndexingStatus,
10    Metadata,
11    Document,
12    Image,
13    Where,
14    IDs,
15    GetResult,
16    QueryResult,
17    ID,
18    OneOrMany,
19    ReadLevel,
20    WhereDocument,
21    SearchResult,
22    DeleteResult,
23    maybe_cast_one_to_many,
24)
25
26from chromadb.api.models.CollectionCommon import CollectionCommon
27from chromadb.api.collection_configuration import UpdateCollectionConfiguration
28from chromadb.execution.expression.plan import Search
29
30if TYPE_CHECKING:
31    from chromadb.api import AsyncServerAPI  # noqa: F401
32
33
34class AsyncCollection(CollectionCommon["AsyncServerAPI"]):
35    async def add(
36        self,
37        ids: OneOrMany[ID],
38        embeddings: Optional[
39            Union[
40                OneOrMany[Embedding],
41                OneOrMany[PyEmbedding],
42            ]
43        ] = None,
44        metadatas: Optional[OneOrMany[Metadata]] = None,
45        documents: Optional[OneOrMany[Document]] = None,
46        images: Optional[OneOrMany[Image]] = None,
47        uris: Optional[OneOrMany[URI]] = None,
48    ) -> None:
49        """Add embeddings to the data store.
50        Args:
51            ids: The ids of the embeddings you wish to add
52            embeddings: The embeddings to add. If None, embeddings will be computed based on the documents or images using the embedding_function set for the Collection. Optional.
53            metadatas: The metadata to associate with the embeddings. When querying, you can filter on this metadata. Optional.
54            documents: The documents to associate with the embeddings. Optional.
55            images: The images to associate with the embeddings. Optional.
56            uris: The uris of the images to associate with the embeddings. Optional.
57
58        Returns:
59            None
60
61        Raises:
62            ValueError: If you don't provide either embeddings or documents
63            ValueError: If the length of ids, embeddings, metadatas, or documents don't match
64            ValueError: If you don't provide an embedding function and don't provide embeddings
65            ValueError: If you provide both embeddings and documents
66            ValueError: If you provide an id that already exists
67
68        """
69        add_request = self._validate_and_prepare_add_request(
70            ids=ids,
71            embeddings=embeddings,
72            metadatas=metadatas,
73            documents=documents,
74            images=images,
75            uris=uris,
76        )
77
78        await self._client._add(
79            collection_id=self.id,
80            ids=add_request["ids"],
81            embeddings=add_request["embeddings"],
82            metadatas=add_request["metadatas"],
83            documents=add_request["documents"],
84            uris=add_request["uris"],
85            tenant=self.tenant,
86            database=self.database,
87        )
88
89    async def count(self, read_level: ReadLevel = ReadLevel.INDEX_AND_WAL) -> int:
90        """Return the number of records in the collection.
91
92        Args:
93            read_level: Controls whether to read from the write-ahead log (WAL):
94                - ReadLevel.INDEX_AND_WAL: Read from both the compacted index and WAL (default).
95                  All committed writes will be visible.
96                - ReadLevel.INDEX_ONLY: Read only from the compacted index, skipping the WAL.
97                  Faster, but recent writes that haven't been compacted may not be visible.
98                - ReadLevel.INDEX_AND_BOUNDED_WAL: Read from the index and up to a
99                  server-configured number of WAL entries for bounded query latency.
100
101        Returns:
102            int: The total number of embeddings added to the database
103        """
104        return await self._client._count(
105            collection_id=self.id,
106            tenant=self.tenant,
107            database=self.database,
108            read_level=read_level,
109        )
110
111    async def get_indexing_status(self) -> IndexingStatus:
112        """Get the indexing status of this collection.
113
114        Returns:
115            IndexingStatus: An object containing:
116                - num_indexed_ops: Number of user operations that have been indexed
117                - num_unindexed_ops: Number of user operations pending indexing
118                - total_ops: Total number of user operations in collection
119                - op_indexing_progress: Proportion of user operations that have been indexed as a float between 0 and 1
120        """
121        return await self._client._get_indexing_status(
122            collection_id=self.id,
123            tenant=self.tenant,
124            database=self.database,
125        )
126
127    async def get(
128        self,
129        ids: Optional[OneOrMany[ID]] = None,
130        where: Optional[Where] = None,
131        limit: Optional[int] = None,
132        offset: Optional[int] = None,
133        where_document: Optional[WhereDocument] = None,
134        include: Include = ["metadatas", "documents"],
135    ) -> GetResult:
136        """Get embeddings and their associate data from the data store. If no ids or where filter is provided returns
137        all embeddings up to limit starting at offset.
138
139        Args:
140            ids: The ids of the embeddings to get. Optional.
141            where: A Where type dict used to filter results by. E.g. `{"$and": [{"color" : "red"}, {"price": {"$gte": 4.20}}]}`. Optional.
142            limit: The number of documents to return. Optional.
143            offset: The offset to start returning results from. Useful for paging results with limit. Optional.
144            where_document: A WhereDocument type dict used to filter by the documents. E.g. `{"$contains": "hello"}`. Optional.
145            include: A list of what to include in the results. Can contain `"embeddings"`, `"metadatas"`, `"documents"`. Ids are always included. Defaults to `["metadatas", "documents"]`. Optional.
146
147        Returns:
148            GetResult: A GetResult object containing the results.
149
150        """
151        get_request = self._validate_and_prepare_get_request(
152            ids=ids,
153            where=where,
154            where_document=where_document,
155            include=include,
156        )
157
158        get_results = await self._client._get(
159            collection_id=self.id,
160            ids=get_request["ids"],
161            where=get_request["where"],
162            where_document=get_request["where_document"],
163            include=get_request["include"],
164            limit=limit,
165            offset=offset,
166            tenant=self.tenant,
167            database=self.database,
168        )
169
170        return self._transform_get_response(
171            response=get_results, include=get_request["include"]
172        )
173
174    async def peek(self, limit: int = 10) -> GetResult:
175        """Get the first few results in the database up to limit
176
177        Args:
178            limit: The number of results to return.
179
180        Returns:
181            GetResult: A GetResult object containing the results.
182        """
183        return self._transform_peek_response(
184            await self._client._peek(
185                collection_id=self.id,
186                n=limit,
187                tenant=self.tenant,
188                database=self.database,
189            )
190        )
191
192    async def query(
193        self,
194        query_embeddings: Optional[
195            Union[
196                OneOrMany[Embedding],
197                OneOrMany[PyEmbedding],
198            ]
199        ] = None,
200        query_texts: Optional[OneOrMany[Document]] = None,
201        query_images: Optional[OneOrMany[Image]] = None,
202        query_uris: Optional[OneOrMany[URI]] = None,
203        ids: Optional[OneOrMany[ID]] = None,
204        n_results: int = 10,
205        where: Optional[Where] = None,
206        where_document: Optional[WhereDocument] = None,
207        include: Include = [
208            "metadatas",
209            "documents",
210            "distances",
211        ],
212    ) -> QueryResult:
213        """Get the n_results nearest neighbor embeddings for provided query_embeddings or query_texts.
214
215        Args:
216            query_embeddings: The embeddings to get the closes neighbors of. Optional.
217            query_texts: The document texts to get the closes neighbors of. Optional.
218            query_images: The images to get the closes neighbors of. Optional.
219            ids: A subset of ids to search within. Optional.
220            n_results: The number of neighbors to return for each query_embedding or query_texts. Optional.
221            where: A Where type dict used to filter results by. E.g. `{"$and": [{"color" : "red"}, {"price": {"$gte": 4.20}}]}`. Optional.
222            where_document: A WhereDocument type dict used to filter by the documents. E.g. `{"$contains": "hello"}`. Optional.
223            include: A list of what to include in the results. Can contain `"embeddings"`, `"metadatas"`, `"documents"`, `"distances"`. Ids are always included. Defaults to `["metadatas", "documents", "distances"]`. Optional.
224
225        Returns:
226            QueryResult: A QueryResult object containing the results.
227
228        Raises:
229            ValueError: If you don't provide either query_embeddings, query_texts, or query_images
230            ValueError: If you provide both query_embeddings and query_texts
231            ValueError: If you provide both query_embeddings and query_images
232            ValueError: If you provide both query_texts and query_images
233
234        """
235
236        query_request = self._validate_and_prepare_query_request(
237            query_embeddings=query_embeddings,
238            query_texts=query_texts,
239            query_images=query_images,
240            query_uris=query_uris,
241            ids=ids,
242            n_results=n_results,
243            where=where,
244            where_document=where_document,
245            include=include,
246        )
247
248        query_results = await self._client._query(
249            collection_id=self.id,
250            ids=query_request["ids"],
251            query_embeddings=query_request["embeddings"],
252            n_results=query_request["n_results"],
253            where=query_request["where"],
254            where_document=query_request["where_document"],
255            include=query_request["include"],
256            tenant=self.tenant,
257            database=self.database,
258        )
259
260        return self._transform_query_response(
261            response=query_results, include=query_request["include"]
262        )
263
264    async def modify(
265        self,
266        name: Optional[str] = None,
267        metadata: Optional[CollectionMetadata] = None,
268        configuration: Optional[UpdateCollectionConfiguration] = None,
269    ) -> None:
270        """Modify the collection name or metadata
271
272        Args:
273            name: The updated name for the collection. Optional.
274            metadata: The updated metadata for the collection. Optional.
275
276        Returns:
277            None
278        """
279
280        self._validate_modify_request(metadata)
281
282        # Note there is a race condition here where the metadata can be updated
283        # but another thread sees the cached local metadata.
284        # TODO: fixme
285        await self._client._modify(
286            id=self.id,
287            new_name=name,
288            new_metadata=metadata,
289            new_configuration=configuration,
290            tenant=self.tenant,
291            database=self.database,
292        )
293
294        self._update_model_after_modify_success(name, metadata, configuration)
295
296    async def fork(
297        self,
298        new_name: str,
299    ) -> "AsyncCollection":
300        """Fork the current collection under a new name. The returning collection should contain identical data to the current collection.
301        This only works for Hosted Chroma for now.
302
303        Args:
304            new_name: The name of the new collection.
305
306        Returns:
307            Collection: A new collection with the specified name and containing identical data to the current collection.
308        """
309        model = await self._client._fork(
310            collection_id=self.id,
311            new_name=new_name,
312            tenant=self.tenant,
313            database=self.database,
314        )
315        return AsyncCollection(
316            client=self._client,
317            model=model,
318            embedding_function=self._embedding_function,
319            data_loader=self._data_loader,
320        )
321
322    async def fork_count(self) -> int:
323        """Get the number of forks that exist for this collection.
324        This only works for Hosted Chroma for now.
325
326        Returns:
327            int: The number of forks for this collection.
328        """
329        return await self._client._fork_count(
330            collection_id=self.id,
331            tenant=self.tenant,
332            database=self.database,
333        )
334
335    async def search(
336        self,
337        searches: OneOrMany[Search],
338        read_level: ReadLevel = ReadLevel.INDEX_AND_WAL,
339    ) -> SearchResult:
340        """Perform hybrid search on the collection.
341        This is an experimental API that only works for Hosted Chroma for now.
342
343        Args:
344            searches: A single Search object or a list of Search objects, each containing:
345                - where: Where expression for filtering
346                - rank: Ranking expression for hybrid search (defaults to Val(0.0))
347                - limit: Limit configuration for pagination (defaults to no limit)
348                - select: Select configuration for keys to return (defaults to empty)
349            read_level: Controls whether to read from the write-ahead log (WAL):
350                - ReadLevel.INDEX_AND_WAL: Read from both the compacted index and WAL (default).
351                  All committed writes will be visible.
352                - ReadLevel.INDEX_ONLY: Read only from the compacted index, skipping the WAL.
353                  Faster, but recent writes that haven't been compacted may not be visible.
354                - ReadLevel.INDEX_AND_BOUNDED_WAL: Read from the index and up to a
355                  server-configured number of WAL entries for bounded query latency.
356
357        Returns:
358            SearchResult: Column-major format response with:
359                - ids: List of result IDs for each search payload
360                - documents: Optional documents for each payload
361                - embeddings: Optional embeddings for each payload
362                - metadatas: Optional metadata for each payload
363                - scores: Optional scores for each payload
364                - select: List of selected keys for each payload
365
366        Raises:
367            NotImplementedError: For local/segment API implementations
368
369        Examples:
370            # Using builder pattern with Key constants
371            from chromadb.execution.expression import (
372                Search, Key, K, Knn, Val
373            )
374
375            # Note: K is an alias for Key, so K.DOCUMENT == Key.DOCUMENT
376            search = (Search()
377                .where((K("category") == "science") & (K("score") > 0.5))
378                .rank(Knn(query=[0.1, 0.2, 0.3]) * 0.8 + Val(0.5) * 0.2)
379                .limit(10, offset=0)
380                .select(K.DOCUMENT, K.SCORE, "title"))
381
382            # Direct construction
383            from chromadb.execution.expression import (
384                Search, Eq, And, Gt, Knn, Limit, Select, Key
385            )
386
387            search = Search(
388                where=And([Eq("category", "science"), Gt("score", 0.5)]),
389                rank=Knn(query=[0.1, 0.2, 0.3]),
390                limit=Limit(offset=0, limit=10),
391                select=Select(keys={Key.DOCUMENT, Key.SCORE, "title"})
392            )
393
394            # Single search
395            result = await collection.search(search)
396
397            # Multiple searches at once
398            searches = [
399                Search().where(K("type") == "article").rank(Knn(query=[0.1, 0.2])),
400                Search().where(K("type") == "paper").rank(Knn(query=[0.3, 0.4]))
401            ]
402            results = await collection.search(searches)
403
404            # Skip WAL for faster queries (may miss recent uncommitted writes)
405            from chromadb.api.types import ReadLevel
406            result = await collection.search(search, read_level=ReadLevel.INDEX_ONLY)
407        """
408        # Convert single search to list for consistent handling
409        searches_list = maybe_cast_one_to_many(searches)
410        if searches_list is None:
411            searches_list = []
412
413        # Embed any string queries in Knn objects
414        embedded_searches = [
415            self._embed_search_string_queries(search) for search in searches_list
416        ]
417
418        return await self._client._search(
419            collection_id=self.id,
420            searches=cast(List[Search], embedded_searches),
421            tenant=self.tenant,
422            database=self.database,
423            read_level=read_level,
424        )
425
426    async def update(
427        self,
428        ids: OneOrMany[ID],
429        embeddings: Optional[
430            Union[
431                OneOrMany[Embedding],
432                OneOrMany[PyEmbedding],
433            ]
434        ] = None,
435        metadatas: Optional[OneOrMany[Metadata]] = None,
436        documents: Optional[OneOrMany[Document]] = None,
437        images: Optional[OneOrMany[Image]] = None,
438        uris: Optional[OneOrMany[URI]] = None,
439    ) -> None:
440        """Update the embeddings, metadatas or documents for provided ids.
441
442        Args:
443            ids: The ids of the embeddings to update
444            embeddings: The embeddings to update. If None, embeddings will be computed based on the documents or images using the embedding_function set for the Collection. Optional.
445            metadatas:  The metadata to associate with the embeddings. When querying, you can filter on this metadata. Optional.
446            documents: The documents to associate with the embeddings. Optional.
447            images: The images to associate with the embeddings. Optional.
448        Returns:
449            None
450        """
451        update_request = self._validate_and_prepare_update_request(
452            ids=ids,
453            embeddings=embeddings,
454            metadatas=metadatas,
455            documents=documents,
456            images=images,
457            uris=uris,
458        )
459
460        await self._client._update(
461            collection_id=self.id,
462            ids=update_request["ids"],
463            embeddings=update_request["embeddings"],
464            metadatas=update_request["metadatas"],
465            documents=update_request["documents"],
466            uris=update_request["uris"],
467            tenant=self.tenant,
468            database=self.database,
469        )
470
471    async def upsert(
472        self,
473        ids: OneOrMany[ID],
474        embeddings: Optional[
475            Union[
476                OneOrMany[Embedding],
477                OneOrMany[PyEmbedding],
478            ]
479        ] = None,
480        metadatas: Optional[OneOrMany[Metadata]] = None,
481        documents: Optional[OneOrMany[Document]] = None,
482        images: Optional[OneOrMany[Image]] = None,
483        uris: Optional[OneOrMany[URI]] = None,
484    ) -> None:
485        """Update the embeddings, metadatas or documents for provided ids, or create them if they don't exist.
486
487        Args:
488            ids: The ids of the embeddings to update
489            embeddings: The embeddings to add. If None, embeddings will be computed based on the documents using the embedding_function set for the Collection. Optional.
490            metadatas:  The metadata to associate with the embeddings. When querying, you can filter on this metadata. Optional.
491            documents: The documents to associate with the embeddings. Optional.
492
493        Returns:
494            None
495        """
496        upsert_request = self._validate_and_prepare_upsert_request(
497            ids=ids,
498            embeddings=embeddings,
499            metadatas=metadatas,
500            documents=documents,
501            images=images,
502            uris=uris,
503        )
504
505        await self._client._upsert(
506            collection_id=self.id,
507            ids=upsert_request["ids"],
508            embeddings=upsert_request["embeddings"],
509            metadatas=upsert_request["metadatas"],
510            documents=upsert_request["documents"],
511            uris=upsert_request["uris"],
512            tenant=self.tenant,
513            database=self.database,
514        )
515
516    async def delete(
517        self,
518        ids: Optional[IDs] = None,
519        where: Optional[Where] = None,
520        where_document: Optional[WhereDocument] = None,
521        limit: Optional[int] = None,
522    ) -> DeleteResult:
523        """Delete the embeddings based on ids and/or a where filter
524
525        Args:
526            ids: The ids of the embeddings to delete
527            where: A Where type dict used to filter the delection by. E.g. `{"$and": [{"color" : "red"}, {"price": {"$gte": 4.20}}]}`. Optional.
528            where_document: A WhereDocument type dict used to filter the deletion by the document content. E.g. `{"$contains": "hello"}`. Optional.
529            limit: Maximum number of records to delete. Can only be used with where or where_document filters.
530
531        Returns:
532            DeleteResult: A dict containing the number of records deleted.
533
534        Raises:
535            ValueError: If you don't provide either ids, where, or where_document
536            ValueError: If limit is specified without a where or where_document clause.
537        """
538        delete_request = self._validate_and_prepare_delete_request(
539            ids, where, where_document, limit=limit
540        )
541
542        return await self._client._delete(
543            collection_id=self.id,
544            ids=delete_request["ids"],
545            where=delete_request["where"],
546            where_document=delete_request["where_document"],
547            limit=delete_request["limit"],
548            tenant=self.tenant,
549            database=self.database,
550        )
551