Instructions Intake Do not block on a long questionnaire. Ask only for details that are missing and required to choose the right path: - Dense only or hybrid search - Whether , , and are already configured - Existing embedding choice, if any If the user has no embedding preference, default to Chroma Cloud Qwen. If hybrid search is required, use and . If the task is narrow, such as fixing an existing query, reviewing code, or answering an API question, proceed with the repo context instead of forcing intake. What to validate - Correct client import ( vs ) - Environment variables are set for Cl…

,\n },\n});\n```\n\n### Combining regex with metadata filters\n\nRegex filters can be combined with metadata filters using `$and` and `$or` operators. This is powerful for narrowing results by both content patterns and structured metadata. Note however regex can not be used on metadata string values.\n\n```typescript\nawait collection.query({\n queryTexts: ['query1', 'query2'],\n whereDocument: {\n $and: [{ $contains: 'search_string_1' }, { $regex: '[a-z]+' }],\n },\n});\n```\n\n### Performance considerations\n\nRegex filtering happens after the initial vector search retrieves candidates. For best performance:\n- Keep regex patterns simple when possible\n- Use metadata filters to reduce the candidate set before regex matching\n- Consider whether a metadata field with pre-extracted values would be faster than runtime regex\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2420,"content_sha256":"083401976ce682c285d9b1c51faef945c2008c58af92fe6fce0cf1ebbaee70d1"},{"filename":"schema/python.md","content":"---\nname: Schema\ndescription: Schema() configures collections with multiple indexes\n---\n\n## Schema\n\nThe Schema API configures collections with advanced indexing options, including multiple indexes on the same collection. This enables hybrid search strategies that combine different retrieval methods.\n\n**Note:** The Schema API is only available on Chroma Cloud.\n\n### Why use Schema?\n\nWithout Schema, collections have a single dense embedding index. With Schema, you can:\n\n- Add sparse indexes (BM25, SPLADE) alongside dense embeddings for hybrid search\n- Configure multiple embedding functions on the same collection\n- Fine-tune index parameters for your specific use case\n\nHybrid search (combining dense and sparse) often outperforms either method alone, especially for queries that mix conceptual meaning with specific keywords.\n\n### Important: Schema vs embedding function\n\nWhen using the Schema API, you cannot pass an embedding function directly to `getOrCreateCollection`. Instead, you pass the embedding function to `schema.indexConfig()`. This gives you explicit control over which index uses which embedding.\n\n### Imports\n\n```python\nimport os\nfrom typing import cast\nimport chromadb\nfrom chromadb import Schema, VectorIndexConfig, SparseVectorIndexConfig, K\nfrom chromadb.utils.embedding_functions import ChromaCloudSpladeEmbeddingFunction\nfrom chromadb.utils.embedding_functions import ChromaBm25EmbeddingFunction\nfrom chromadb.utils.embedding_functions import ChromaCloudQwenEmbeddingFunction\nfrom chromadb.utils.embedding_functions.chroma_cloud_qwen_embedding_function import ChromaCloudQwenEmbeddingModel\n\nclient = chromadb.CloudClient(\n tenant=os.getenv(\"CHROMA_TENANT\"),\n database=os.getenv(\"CHROMA_DATABASE\"),\n api_key=os.getenv(\"CHROMA_API_KEY\"),\n)\n```\n\n## BM25 sparse index\n\nBM25 is a traditional keyword-based ranking algorithm. It works well when:\n- Exact keyword matches are important\n- Users search with specific terms they expect to find verbatim\n- You want a lightweight sparse index without neural embeddings\n\nBM25 doesn't understand semantics, so \"car\" won't match \"automobile\". Use it as a complement to dense embeddings, not a replacement.\n\n```python\nbm25_schema = Schema()\nSPARSE_BM25_KEY = \"sparse_bm25\"\n\n# Configure vector index with custom embedding function\ndense_embedding_function = ChromaCloudQwenEmbeddingFunction(\n model=ChromaCloudQwenEmbeddingModel.QWEN3_EMBEDDING_0p6B,\n task=None,\n api_key_env_var=\"CHROMA_API_KEY\"\n)\n\nbm25_schema.create_index(config=VectorIndexConfig(\n space=\"cosine\",\n embedding_function=dense_embedding_function\n))\n\nbm25_embedding_function = ChromaBm25EmbeddingFunction()\n\nbm25_schema.create_index(config=SparseVectorIndexConfig(\n\tsource_key=cast(str, K.DOCUMENT),\n\tembedding_function=bm25_embedding_function\n), key=SPARSE_BM25_KEY)\n\ncollection = client.get_or_create_collection(name=\"my_collection\", schema=bm25_schema)\n```\n\n## SPLADE sparse index\n\nSPLADE (Sparse Lexical and Expansion) is a neural sparse embedding model. It combines the efficiency of sparse retrieval with learned term expansion.\n\n**SPLADE vs BM25:**\n- SPLADE understands synonyms and related terms (like dense embeddings)\n- SPLADE produces sparse vectors (efficient like BM25)\n- SPLADE generally outperforms BM25 for most use cases\n- BM25 is simpler and doesn't require a neural model\n\nFor hybrid search, SPLADE + dense embeddings is typically the best combination. Use BM25 only if you have specific requirements for traditional keyword matching or want to avoid the neural model dependency.\n\n```python\nsplade_schema = Schema()\nSPARSE_SPLADE_KEY = \"sparse_splade\"\n\n# Configure vector index with custom embedding function\ndense_embedding_function = ChromaCloudQwenEmbeddingFunction(\n model=ChromaCloudQwenEmbeddingModel.QWEN3_EMBEDDING_0p6B,\n task=None,\n api_key_env_var=\"CHROMA_API_KEY\"\n)\n\nsplade_schema.create_index(config=VectorIndexConfig(\n space=\"cosine\",\n embedding_function=dense_embedding_function\n))\n\nsplade_embedding_function = ChromaCloudSpladeEmbeddingFunction()\n\nsplade_schema.create_index(config=SparseVectorIndexConfig(\n\tsource_key=cast(str, K.DOCUMENT),\n\tembedding_function=splade_embedding_function\n), key=SPARSE_SPLADE_KEY)\n\ncollection = client.get_or_create_collection(name=\"my_collection\", schema=splade_schema)\n```\n\n## Choosing an index strategy\n\n| Use case | Recommended setup |\n|----------|-------------------|\n| General semantic search | Dense embeddings only (default) |\n| Search with important keywords | Dense + BM25 hybrid |\n| Best quality hybrid search | Dense + SPLADE hybrid |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4579,"content_sha256":"ef6d2a3e7d3a690194510fa49e032a66beba6ab0db1b00c556f1bc57edeba821"},{"filename":"schema/typescript.md","content":"---\nname: Schema\ndescription: Schema() configures collections with multiple indexes\n---\n\n## Schema\n\nThe Schema API configures collections with advanced indexing options, including multiple indexes on the same collection. This enables hybrid search strategies that combine different retrieval methods.\n\n**Note:** The Schema API is only available on Chroma Cloud.\n\n### Why use Schema?\n\nWithout Schema, collections have a single dense embedding index. With Schema, you can:\n\n- Add sparse indexes (BM25, SPLADE) alongside dense embeddings for hybrid search\n- Configure multiple embedding functions on the same collection\n- Fine-tune index parameters for your specific use case\n\nHybrid search (combining dense and sparse) often outperforms either method alone, especially for queries that mix conceptual meaning with specific keywords.\n\n### Important: Schema vs embedding function\n\nWhen using the Schema API, you cannot pass an embedding function directly to `getOrCreateCollection`. Instead, you pass the embedding function to `schema.indexConfig()`. This gives you explicit control over which index uses which embedding.\n\n### Imports\n\n```typescript\nimport { ChromaBm25EmbeddingFunction } from '@chroma-core/chroma-bm25';\nimport {\n ChromaCloudQwenEmbeddingFunction,\n ChromaCloudQwenEmbeddingModel,\n} from '@chroma-core/chroma-cloud-qwen';\nimport { ChromaCloudSpladeEmbeddingFunction } from '@chroma-core/chroma-cloud-splade';\nimport {\n CloudClient,\n K,\n Schema,\n SparseVectorIndexConfig,\n VectorIndexConfig,\n} from 'chromadb';\n\nconst client = new CloudClient({\n apiKey: process.env.CHROMA_API_KEY,\n tenant: process.env.CHROMA_TENANT,\n database: process.env.CHROMA_DATABASE,\n});\n```\n\n## BM25 sparse index\n\nBM25 is a traditional keyword-based ranking algorithm. It works well when:\n- Exact keyword matches are important\n- Users search with specific terms they expect to find verbatim\n- You want a lightweight sparse index without neural embeddings\n\nBM25 doesn't understand semantics, so \"car\" won't match \"automobile\". Use it as a complement to dense embeddings, not a replacement.\n\n```typescript\nconst bm25Schema = new Schema();\nconst SPARSE_BM25_KEY = 'bm25_key';\n\n// Configure vector index with both sparse and dense embeddings\nconst bm25ExampleEmbeddingFunction = new ChromaCloudQwenEmbeddingFunction({\n model: ChromaCloudQwenEmbeddingModel.QWEN3_EMBEDDING_0p6B,\n task: null,\n apiKeyEnvVar: 'CHROMA_API_KEY',\n});\n\nbm25Schema.createIndex(\n new VectorIndexConfig({\n space: 'cosine',\n embeddingFunction: bm25ExampleEmbeddingFunction,\n })\n);\n\nconst bm25EmbeddingFunction = new ChromaBm25EmbeddingFunction();\n\nbm25Schema.createIndex(\n new SparseVectorIndexConfig({\n sourceKey: K.DOCUMENT,\n embeddingFunction: bm25EmbeddingFunction,\n }),\n SPARSE_BM25_KEY\n);\n\n// create the collection with the schema\nconst bm25CollectionExample = await client.getOrCreateCollection({\n name: 'my_collection',\n schema: bm25Schema,\n});\n```\n\n## SPLADE sparse index\n\nSPLADE (Sparse Lexical and Expansion) is a neural sparse embedding model. It combines the efficiency of sparse retrieval with learned term expansion.\n\n**SPLADE vs BM25:**\n- SPLADE understands synonyms and related terms (like dense embeddings)\n- SPLADE produces sparse vectors (efficient like BM25)\n- SPLADE generally outperforms BM25 for most use cases\n- BM25 is simpler and doesn't require a neural model\n\nFor hybrid search, SPLADE + dense embeddings is typically the best combination. Use BM25 only if you have specific requirements for traditional keyword matching or want to avoid the neural model dependency.\n\n```typescript\nconst spladeSchema = new Schema();\nconst SPARSE_SPLADE_KEY = 'splade_key';\n\n// Configure vector index with both sparse and dense embeddings\nconst denseEmbeddingFunction = new ChromaCloudQwenEmbeddingFunction({\n model: ChromaCloudQwenEmbeddingModel.QWEN3_EMBEDDING_0p6B,\n task: null,\n apiKeyEnvVar: 'CHROMA_API_KEY',\n});\n\nspladeSchema.createIndex(\n new VectorIndexConfig({\n space: 'cosine',\n embeddingFunction: denseEmbeddingFunction,\n })\n);\n\nconst spladeEmbeddingFunction = new ChromaCloudSpladeEmbeddingFunction();\n\nspladeSchema.createIndex(\n new SparseVectorIndexConfig({\n sourceKey: K.DOCUMENT,\n embeddingFunction: spladeEmbeddingFunction,\n }),\n SPARSE_SPLADE_KEY\n);\n\n// create the collection with the schema\nconst spladeCollectionExample = await client.getOrCreateCollection({\n name: 'my_collection',\n schema: spladeSchema,\n});\n```\n\n## Choosing an index strategy\n\n| Use case | Recommended setup |\n|----------|-------------------|\n| General semantic search | Dense embeddings only (default) |\n| Search with important keywords | Dense + BM25 hybrid |\n| Best quality hybrid search | Dense + SPLADE hybrid |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4728,"content_sha256":"e2964f2eb499c432b4014a22a2a1ecf79f037eddbb14bd249adb266012a3b38a"},{"filename":"search-api/python.md","content":"---\nname: Search() API\ndescription: An expressive and flexible API for doing dense and sparse vector search on collections, as well as hybrid search\n---\n\n## Search() API\n\nThe Search API provides a fluent, composable interface for building complex queries. It's more expressive than the basic `query` method and supports advanced features like hybrid search with rank fusion.\n\n**Note:** The Search API is only available on Chroma Cloud and is designed to work with Collection Schemas.\n\nThe Search() API acts as both query() and get() because the search expression that is being passed in ultimately decides what type of query to issue.\n\n### When to use Search() vs query()\n\n**Use `query()` when:**\n- You need simple semantic search\n- You want the most straightforward API\n\n**Use `search()` when:**\n- You need hybrid search combining dense and sparse indexes\n- You want fine-grained control over ranking and filtering\n- You're building complex queries with multiple conditions\n- You need to select specific fields to return\n\nNote that the Search() class uses a builder pattern, so if you call a method on it, it does not mutate that instance, it returns a copy with that mutation, so it needs re-assignging to the variable that is referencing it.\n\nThe `search()` method on a collection is able to take a single Search class instance or an arry of them, so the return value of the `search()` method on a collection is a SearchResult class, which has a `rows()` method, which will give you an array of array of results. So index 0 of the return value of `rows()` will be the array of the first Search class instance results.\n\n### Ranking and Scoring\n\nRanking expressions score and order results. Lower scores = better matches (distance-based). When `rank` is omitted, results are returned in index/insertion order.\n\nDocument inclusion rules when combining multiple `Knn` expressions:\n- A document must appear in at least one `Knn`'s top-`limit` results to be scored\n- It must also appear in every `Knn` where `default=None` (the default); otherwise it's excluded\n- Set a `default` value on a `Knn` to assign a fallback score for documents missing from its results, keeping them in the pool. This is usually what you want when combining multiple `Knn` expressions, otherwise the intersection rule often filters out too many candidates.\n\nExpressions support arithmetic (`+`, `-`, `*`, `/`, unary `-`) and math functions (`exp`, `log`, `abs`, `min`, `max`) for combining and transforming scores. Numbers are auto-converted to constants, or use `Val(x)` explicitly. A common pattern is a weighted sum like `Knn(query=q) * 0.7 + Knn(query=q, key=\"sparse_embedding\") * 0.3` — but note this mixes two raw distance spaces, which may have very different scales. For robust hybrid ranking across dense + sparse, prefer RRF (below).\n\nTwo limits to keep straight:\n- `Knn(limit=N)` — how many candidates that `Knn` scores (default 16). Raise it for better recall at the cost of latency.\n- `Search.limit(N)` — how many results are returned after ranking.\n\nFor rank fusion (RRF), pass `return_rank=True` (Python) / `returnRank: true` (TypeScript) on each `Knn` so it emits rank positions (0, 1, 2…) instead of raw distances — see the Hybrid Search section below.\n\n### Setup\n\n```python\nfrom chromadb import Search, K, Knn, CloudClient, Rrf\nimport os\n\nclient = CloudClient(\n tenant=os.getenv(\"CHROMA_TENANT\"),\n database=os.getenv(\"CHROMA_DATABASE\"),\n api_key=os.getenv(\"CHROMA_API_KEY\"),\n)\n\ncollection = client.get_or_create_collection(name=\"my_collection\")\n```\n\n### Filtering with Key (K)\n\nThe `Key` class (aliased as `K` for brevity) provides a fluent interface for building filter expressions. Think of it like a query builder for metadata, document content, and IDs.\n\n```python\n# K is an alias for Key - use K for more concise code\n# Filter by metadata field\nK(\"status\") == \"active\"\n\n# Filter by document content\nK.DOCUMENT.contains(\"machine learning\")\n\n# Filter by document IDs\nK.ID.is_in([\"doc1\", \"doc2\", \"doc3\"])\n\n# Equality and inequality (all types)\nK(\"status\") == \"published\" # String equality\nK(\"views\") != 0 # Numeric inequality\nK(\"featured\") == True # Boolean equality\n\n# Numeric comparisons (numbers only)\nK(\"price\") > 100 # Greater than\nK(\"rating\") >= 4.5 # Greater than or equal\nK(\"stock\") \u003c 10 # Less than\nK(\"discount\") \u003c= 0.25 # Less than or equal\n\n# Set membership operators (works on all fields)\nK.ID.is_in([\"doc1\", \"doc2\", \"doc3\"]) # Match any ID in list\nK(\"category\").is_in([\"tech\", \"science\"]) # Match any category\nK(\"status\").not_in([\"draft\", \"deleted\"]) # Exclude specific values\n\n# String content operators (currently K.DOCUMENT only)\nK.DOCUMENT.contains(\"machine learning\") # Substring search in document\nK.DOCUMENT.not_contains(\"deprecated\") # Exclude documents with text\nK.DOCUMENT.regex(r\"\\bAPI\\b\") # Match whole word \"API\" in document\n```\n\n### Ranking with Knn\n\n`Knn` (k-nearest neighbors) is how you specify which embeddings to search and how to score results. Each `Knn` finds the nearest neighbors for a given query in a specific index.\n\nThe `limit` parameter controls how many candidates each `Knn` considers. A higher limit means more candidates are scored, which can improve recall but increases latency.\n\n```python\n# Example 1: Single Knn - scores top 16 documents\nrank = Knn(query=\"machine learning research\")\n# Only the 16 nearest documents get scored (default limit)\n\n# Example 2: Multiple Knn with default=None\nrank = Knn(query=\"research papers\", limit=100) + Knn(query=\"academic publications\", limit=100, key=\"sparse_embedding\")\n# Both Knn have default=None (the default)\n# Documents must appear in BOTH top-100 lists to be scored\n# Documents in only one list are excluded\n\n# Example 3: Mixed default values\nrank = Knn(query=\"AI research\", limit=100) * 0.5 + Knn(query=\"scientific papers\", limit=50, default=1000.0, key=\"sparse_embedding\") * 0.5\n# First Knn has default=None, second has default=1000.0\n# Documents in first top-100 but not in second top-50:\n# - Get first distance * 0.5 + 1000.0 * 0.5 (second's default)\n# Documents in second top-50 but not in first top-100:\n# - Excluded (must appear in all Knn where default=None)\n# Documents in both lists:\n# - Get first distance * 0.5 + second distance * 0.5\n\n\nfrom chromadb import Knn\n\n# Basic search on default embedding field\nKnn(query=\"What is machine learning?\")\n\n# Search with custom parameters\nKnn(\n query=\"What is machine learning?\",\n key=\"#embedding\", # Field to search (default: \"#embedding\")\n limit=100, # Max candidates to consider (default: 16)\n return_rank=False # Return rank position vs distance (default: False)\n)\n\n# Search custom sparse embedding field in metadata\nKnn(query=\"machine learning\", key=\"sparse_embedding\")\n```\n\n### Basic search example\n\nHere's a complete example showing the typical flow: create a collection, add documents, and search.\n\n```python\n# Build the base search with filtering\nsearch = (\n Search()\n .where(K(\"category\") == \"science\")\n .limit(10)\n .select(K.DOCUMENT, K.SCORE)\n)\n\n# Option 1: Pass pre-computed embeddings directly\nquery_embedding = [0.25, -0.15, 0.33, ...]\nresult = collection.search(search.rank(Knn(query=query_embedding)))\n\n# Option 2: Pass text query (embedding created using collection's schema configuration)\nquery_text = \"What are the latest advances in quantum computing?\"\nresult = collection.search(search.rank(Knn(query=query_text)))\n```\n\n### Hybrid search with Reciprocal Rank Fusion (RRF)\n\nHybrid search combines results from multiple indexes (typically dense + sparse) to get better results than either alone. RRF is a rank fusion algorithm that merges ranked lists without needing score normalization.\n\n**How RRF works:**\n1. Each `Knn` produces a ranked list of candidates\n2. Documents are scored based on their rank position in each list: `1 / (k + rank)`\n3. Scores are weighted and summed across all lists\n4. Final results are sorted by combined score\n\nThe `k` parameter (default 60) controls how much weight top-ranked documents get relative to lower-ranked ones. Higher `k` values make rankings more uniform.\n\n```python\n# Dense semantic embeddings\ndense_rank = Knn(\n query=\"machine learning research\", # Text query for dense embeddings\n key=\"#embedding\", # Default embedding field\n return_rank=True,\n limit=200 # Consider top 200 candidates\n)\n\n# Sparse keyword embeddings\nsparse_rank = Knn(\n query=\"machine learning research\", # Text query for sparse embeddings\n key=\"sparse_embedding\", # Metadata field for sparse vectors\n return_rank=True,\n limit=200\n)\n\n# Combine with RRF\nhybrid_rank = Rrf(\n ranks=[dense_rank, sparse_rank],\n weights=[0.7, 0.3], # 70% semantic, 30% keyword\n k=60\n)\n\n# Use in search\nsearch = (Search()\n .where(K(\"status\") == \"published\") # Optional filtering\n .rank(hybrid_rank)\n .limit(20)\n .select(K.DOCUMENT, K.SCORE, \"title\")\n)\n\nresults = collection.search(search)\n```\n\n### Building effective hybrid search\n\nFor best results with hybrid search:\n\n1. **Use comparable limits** for each `Knn` so both indexes contribute meaningfully\n2. **Weight based on your data**: keyword-heavy content might favor sparse; conceptual content might favor dense\n3. **Start with 0.7/0.3 weighting** (dense/sparse) and adjust based on evaluation\n4. **Use `returnRank: true`** when combining with RRF, as RRF operates on ranks, not distances\n\nNote that return ranks from RRF are netagive and the value furthest from 0 is the closest to the original query.","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9690,"content_sha256":"b084494ac8a80bc7ccdc491dfd97597e638a52fa7bc4cfcda1cb981dcb8ad2fc"},{"filename":"search-api/typescript.md","content":"---\nname: Search() API\ndescription: An expressive and flexible API for doing dense and sparse vector search on collections, as well as hybrid search\n---\n\n## Search() API\n\nThe Search API provides a fluent, composable interface for building complex queries. It's more expressive than the basic `query` method and supports advanced features like hybrid search with rank fusion.\n\n**Note:** The Search API is only available on Chroma Cloud and is designed to work with Collection Schemas.\n\nThe Search() API acts as both query() and get() because the search expression that is being passed in ultimately decides what type of query to issue.\n\n### When to use Search() vs query()\n\n**Use `query()` when:**\n- You need simple semantic search\n- You want the most straightforward API\n\n**Use `search()` when:**\n- You need hybrid search combining dense and sparse indexes\n- You want fine-grained control over ranking and filtering\n- You're building complex queries with multiple conditions\n- You need to select specific fields to return\n\nNote that the Search() class uses a builder pattern, so if you call a method on it, it does not mutate that instance, it returns a copy with that mutation, so it needs re-assignging to the variable that is referencing it.\n\nThe `search()` method on a collection is able to take a single Search class instance or an arry of them, so the return value of the `search()` method on a collection is a SearchResult class, which has a `rows()` method, which will give you an array of array of results. So index 0 of the return value of `rows()` will be the array of the first Search class instance results.\n\n### Ranking and Scoring\n\nRanking expressions score and order results. Lower scores = better matches (distance-based). When `rank` is omitted, results are returned in index/insertion order.\n\nDocument inclusion rules when combining multiple `Knn` expressions:\n- A document must appear in at least one `Knn`'s top-`limit` results to be scored\n- It must also appear in every `Knn` where `default=None` (the default); otherwise it's excluded\n- Set a `default` value on a `Knn` to assign a fallback score for documents missing from its results, keeping them in the pool. This is usually what you want when combining multiple `Knn` expressions, otherwise the intersection rule often filters out too many candidates.\n\nExpressions support arithmetic (`+`, `-`, `*`, `/`, unary `-`) and math functions (`exp`, `log`, `abs`, `min`, `max`) for combining and transforming scores. Numbers are auto-converted to constants, or use `Val(x)` explicitly. A common pattern is a weighted sum like `Knn(query=q) * 0.7 + Knn(query=q, key=\"sparse_embedding\") * 0.3` — but note this mixes two raw distance spaces, which may have very different scales. For robust hybrid ranking across dense + sparse, prefer RRF (below).\n\nTwo limits to keep straight:\n- `Knn(limit=N)` — how many candidates that `Knn` scores (default 16). Raise it for better recall at the cost of latency.\n- `Search.limit(N)` — how many results are returned after ranking.\n\nFor rank fusion (RRF), pass `return_rank=True` (Python) / `returnRank: true` (TypeScript) on each `Knn` so it emits rank positions (0, 1, 2…) instead of raw distances — see the Hybrid Search section below.\n\n### Setup\n\n```typescript\nimport { CloudClient, type IncludeEnum, Rrf, K, Knn, Search } from 'chromadb';\nimport { DefaultEmbeddingFunction } from '@chroma-core/default-embed';\n\nconst client = new CloudClient({\n apiKey: process.env.CHROMA_API_KEY,\n tenant: process.env.CHROMA_TENANT,\n database: process.env.CHROMA_DATABASE,\n});\n```\n\n### Filtering with Key (K)\n\nThe `Key` class (aliased as `K` for brevity) provides a fluent interface for building filter expressions. Think of it like a query builder for metadata, document content, and IDs.\n\n```typescript\n// K is an alias for Key - use K for more concise code\n// Filter by metadata field\nK('status').eq('active');\n\n// Filter by document content\nK.DOCUMENT.contains('machine learning');\n\n// Filter by document IDs\nK.ID.isIn(['doc1', 'doc2', 'doc3']);\n\n// Equality and inequality (all types)\nK('status').eq('published'); // String equality\nK('views').ne(0); // Numeric inequality\nK('featured').eq(true); // Boolean equality\n\n// Numeric comparisons (numbers only)\nK('price').gt(100); // Greater than\nK('rating').gte(4.5); // Greater than or equal\nK('stock').lt(10); // Less than\nK('discount').lte(0.25); // Less than or equal\n\nK.ID.isIn(['doc1', 'doc2', 'doc3']); // Match any ID in list\nK('category').isIn(['tech', 'science']); // Match any category\nK('status').notIn(['draft', 'deleted']); // Exclude specific values\n\n// String content operators (currently K.DOCUMENT only)\nK.DOCUMENT.contains('machine learning'); // Substring search in document\nK.DOCUMENT.notContains('deprecated'); // Exclude documents with text\nK.DOCUMENT.regex('\\\\bAPI\\\\b'); // Match whole word \"API\" in document\n```\n\n### Ranking with Knn\n\n`Knn` (k-nearest neighbors) is how you specify which embeddings to search and how to score results. Each `Knn` finds the nearest neighbors for a given query in a specific index.\n\nThe `limit` parameter controls how many candidates each `Knn` considers. A higher limit means more candidates are scored, which can improve recall but increases latency.\n\n```typescript\n// Example 1: Single Knn - scores top 16 documents\nconst rank1 = Knn({ query: 'machine learning research' });\n// Only the 16 nearest documents get scored (default limit)\n\n// Example 2: Multiple Knn with default undefined\nconst rank2 = Knn({ query: 'research papers', limit: 100 }).add(\n Knn({ query: 'academic publications', limit: 100, key: 'sparse_embedding' })\n);\n// Both Knn have default undefined (the default)\n// Documents must appear in BOTH top-100 lists to be scored\n// Documents in only one list are excluded\n\n// Example 3: Mixed default values\nconst rank3 = Knn({ query: 'AI research', limit: 100 })\n .multiply(0.5)\n .add(\n Knn({\n query: 'scientific papers',\n limit: 50,\n default: 1000.0,\n key: 'sparse_embedding',\n }).multiply(0.5)\n );\n// First Knn has default undefined, second has default 1000.0\n// Documents in first top-100 but not in second top-50:\n// - Get first distance * 0.5 + 1000.0 * 0.5 (second's default)\n// Documents in second top-50 but not in first top-100:\n// - Excluded (must appear in all Knn where default is undefined)\n// Documents in both lists:\n// - Get first distance * 0.5 + second distance * 0.5\n\n// Basic search on default embedding field\nKnn({ query: 'What is machine learning?' });\n\n// Search with custom parameters\nKnn({\n query: 'What is machine learning?',\n key: '#embedding', // Field to search (default: \"#embedding\")\n limit: 100, // Max candidates to consider (default: 16)\n returnRank: false, // Return rank position vs distance (default: false)\n});\n\n// Search custom sparse embedding field in metadata\nKnn({ query: 'machine learning', key: 'sparse_embedding' });\n```\n\n### Basic search example\n\nHere's a complete example showing the typical flow: create a collection, add documents, and search.\n\n```typescript\nconst embeddingFunction = new DefaultEmbeddingFunction();\n\nconst collection = await client.getOrCreateCollection({\n name: 'my_collection',\n embeddingFunction,\n});\n\nawait collection.add({\n ids: ['doc1', 'doc2'],\n documents: [\n 'Apples are really good red fruit',\n 'Red cars tend to get more speeding tickets',\n ],\n});\n\nconst results1 = await collection.query({\n queryTexts: ['I like red apples'],\n});\n\nconst firstResult = results1.documents[0];\n```\n\n### Hybrid search with Reciprocal Rank Fusion (RRF)\n\nHybrid search combines results from multiple indexes (typically dense + sparse) to get better results than either alone. RRF is a rank fusion algorithm that merges ranked lists without needing score normalization.\n\n**How RRF works:**\n1. Each `Knn` produces a ranked list of candidates\n2. Documents are scored based on their rank position in each list: `1 / (k + rank)`\n3. Scores are weighted and summed across all lists\n4. Final results are sorted by combined score\n\nThe `k` parameter (default 60) controls how much weight top-ranked documents get relative to lower-ranked ones. Higher `k` values make rankings more uniform.\n\n```typescript\n// Dense semantic embeddings\nconst denseRank = Knn({\n query: 'machine learning research', // Text query for dense embeddings\n key: '#embedding', // Default embedding field\n returnRank: true,\n limit: 200, // Consider top 200 candidates\n});\n\n// Sparse keyword embeddings\nconst sparseRank = Knn({\n query: 'machine learning research', // Text query for sparse embeddings\n key: 'sparse_embedding', // Metadata field for sparse vectors\n returnRank: true,\n limit: 200,\n});\n\n// Combine with RRF\nconst hybridRank = Rrf({\n ranks: [denseRank, sparseRank],\n weights: [0.7, 0.3], // 70% semantic, 30% keyword\n k: 60,\n});\n\n// Use in search\nconst search = new Search()\n .where(K('status').eq('published')) // Optional filtering\n .rank(hybridRank)\n .limit(20)\n .select(K.DOCUMENT, K.SCORE, 'title');\n\nconst results = await collection.search(search);\n```\n\n### Building effective hybrid search\n\nFor best results with hybrid search:\n\n1. **Use comparable limits** for each `Knn` so both indexes contribute meaningfully\n2. **Weight based on your data**: keyword-heavy content might favor sparse; conceptual content might favor dense\n3. **Start with 0.7/0.3 weighting** (dense/sparse) and adjust based on evaluation\n4. **Use `returnRank: true`** when combining with RRF, as RRF operates on ranks, not distances\n\nNote that return ranks from RRF are netagive and the value furthest from 0 is the closest to the original query.","content_type":"text/markdown; charset=utf-8","language":"markdown","size":9650,"content_sha256":"ead78daa6a75eabb13425804229187f49e14b6d8eb46d6bec1fad61ceda653c9"},{"filename":"understanding-a-codebase.md","content":"---\nname: Integrating Chroma into an existing system\ndescription: Guidance for adding Chroma search to an existing application\n---\n\n## Integrating Chroma into an existing system\n\nAdding search to an existing application requires understanding the data flow and planning both the initial import and ongoing synchronization. This guide helps identify the key questions to answer.\n\n### Key questions to ask the user\n\nBefore writing any code, clarify:\n\n1. **What data should be searchable?** (documents, products, messages, etc.)\n2. **Where does that data currently live?** (database, S3, API, files)\n3. **How is the data structured?** (helps determine chunking strategy)\n4. **How often does the data change?** (informs sync strategy)\n5. **What latency is acceptable for updates to appear in search?**\n\n## Understanding the source data\n\nBefore designing the import pipeline, ask the user if you can look at a sample of the data that will be made searchable. Seeing real records is far more useful than a description of the schema.\n\n**If the data is in a database:** Write a short script that connects to the database and prints a few records. For example, a script that queries 3-5 rows from the relevant table and prints them to the terminal. This lets you see the actual field names, content lengths, and metadata available.\n\n**If the data is on disk:** Read a few of the files directly to understand their structure, format, and size. For example, if indexing markdown files, read 2-3 of them to see how they're organized.\n\nWhat to look for:\n- **Which field(s) contain the searchable text** — this becomes the document content in Chroma\n- **How long the content is** — determines whether chunking is needed\n- **What metadata is available** — fields like category, author, date, or tenant ID that could be useful for filtering\n- **How records are identified** — the primary key or filename that will link Chroma documents back to the source\n\nThis step prevents guesswork and leads to better chunking and metadata design decisions.\n\n## Initial data import (offline ingest)\n\nThe first step is getting existing data into Chroma. This typically involves:\n\n1. **Reading from the source** - database queries, S3 listing, API pagination\n2. **Chunking** - breaking large documents into searchable pieces (see data-model.md)\n3. **Embedding** - converting text chunks to vectors\n4. **Writing to Chroma** - batching for efficiency\n\nBuild this as a reusable pipeline, not a one-off script. The same chunking and embedding logic will be needed for ongoing updates.\n\n**Progress tracking:** For large imports, track which records have been processed. This allows resuming after failures and re-running for updates. A simple approach is storing the last processed ID or timestamp.\n\n## Keeping data in sync (online writes)\n\nAfter the initial import, new and updated data must flow to Chroma. There are two main patterns:\n\n### Asynchronous (recommended)\n\nUse a message queue (SQS, RabbitMQ, Redis streams, etc.) to decouple the primary write path from Chroma updates:\n\n1. Application writes to primary database\n2. Application publishes an event with the record ID\n3. Queue consumer fetches the record, chunks, embeds, and writes to Chroma\n\n**Benefits:** Primary writes aren't slowed by embedding latency. Retries are handled by the queue. Search updates can lag slightly without affecting the main application.\n\n### Synchronous\n\nIf no queue infrastructure exists and slight latency is acceptable, update Chroma in the same request:\n\n1. Application writes to primary database\n2. Application chunks, embeds, and writes to Chroma\n3. Request completes\n\n**Tradeoffs:** Simpler infrastructure but adds latency to every write. Failures in Chroma can affect the primary write path unless carefully handled.\n\n**Ask the user:** Do they have an async queue? If not, is synchronous acceptable, or should we set one up?\n\n## Handling updates and deletes\n\n- **Updates:** Re-chunk and re-embed the document, then use `upsert` to replace existing chunks\n- **Deletes:** Delete all chunks for the document by ID prefix or metadata filter\n\nStoring the source record ID in chunk metadata makes this straightforward. For example, if a blog post with ID `post-123` has 3 chunks, store `{\"source_id\": \"post-123\", \"chunk_index\": 0}` etc. on each chunk.","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4315,"content_sha256":"de253c412a1c5899774606a6728cea15693b385d283022a68cbded083f3b9864"},{"filename":"updating-deleting/python.md","content":"---\nname: Updating and Deleting\ndescription: Update existing documents and delete data from collections\n---\n\n## Updating and Deleting\n\nChroma provides `update`, `upsert`, and `delete` methods for modifying data after initial insertion. Understanding when to use each is important for building reliable data sync pipelines.\n\n### Method overview\n\n| Method | Behavior | Use when |\n|--------|----------|----------|\n| `update` | Modifies existing documents, fails if ID doesn't exist | You know the document exists |\n| `upsert` | Updates if exists, inserts if not | Syncing from external data source |\n| `delete` | Removes documents by ID or filter | Removing stale or unwanted data |\n\n### Imports\n\n```python\nimport time\nfrom typing import TypedDict\n\nimport chromadb\n\nclient = chromadb.CloudClient()\n```\n\n## Update\n\nUpdate modifies existing documents. If an ID doesn't exist, the operation fails silently for that ID (no error thrown, but nothing is updated).\n\n**Important:** When you update a document's text, Chroma re-computes the embedding automatically using the collection's embedding function.\n\n```python\ncollection = client.get_or_create_collection(name=\"my_collection\")\n\ncollection.add(\n ids=[\"doc1\", \"doc2\"],\n documents=[\"Original text for doc1\", \"Original text for doc2\"],\n metadatas=[{\"category\": \"draft\"}, {\"category\": \"draft\"}],\n)\n\ncollection.update(\n ids=[\"doc1\"],\n documents=[\"Updated text for doc1\"],\n)\n\ncollection.update(\n ids=[\"doc1\", \"doc2\"],\n metadatas=[{\"category\": \"published\"}, {\"category\": \"published\"}],\n)\n\ncollection.update(\n ids=[\"doc2\"],\n documents=[\"Completely revised doc2 content\"],\n metadatas=[{\"category\": \"published\", \"revision\": 2}],\n)\n```\n\n## Upsert\n\nUpsert is the preferred method for syncing data from an external source. It inserts new documents and updates existing ones in a single operation.\n\n**When to use upsert vs update:**\n- Use `upsert` when syncing from a primary database (you don't know which records are new)\n- Use `update` when you're certain the document already exists\n\n```python\ncollection2 = client.get_or_create_collection(name=\"articles\")\n\ncollection2.upsert(\n ids=[\"article-123\", \"article-456\", \"article-789\"],\n documents=[\n \"Content of article 123\",\n \"Content of article 456\",\n \"Content of article 789\",\n ],\n metadatas=[\n {\"source_id\": \"123\", \"updated_at\": int(time.time())},\n {\"source_id\": \"456\", \"updated_at\": int(time.time())},\n {\"source_id\": \"789\", \"updated_at\": int(time.time())},\n ],\n)\n\ncollection2.upsert(\n ids=[\"article-123\", \"article-456\"],\n documents=[\n \"Updated content of article 123\",\n \"Updated content of article 456\",\n ],\n metadatas=[\n {\"source_id\": \"123\", \"updated_at\": int(time.time())},\n {\"source_id\": \"456\", \"updated_at\": int(time.time())},\n ],\n)\n```\n\n## Delete by ID\n\nThe simplest way to delete documents is by their IDs.\n\n```python\nawaiting_cleanup = [\"article-789\", \"article-456\"]\ncollection2.delete(ids=awaiting_cleanup)\n```\n\n## Delete by filter\n\nDelete documents matching metadata or content filters without knowing specific IDs. Useful for bulk cleanup operations.\n\n```python\ncollection2.delete(\n where={\"source_id\": \"123\"},\n)\n```\n\n## Syncing from an external data source\n\nA common pattern is keeping Chroma in sync with a primary database. This example shows how to handle creates, updates, and deletes.\n\n```python\nclass SourceRecord(TypedDict):\n id: str\n content: str\n deleted: bool\n updated_at: int\n\n\ndef sync_records(records: list[SourceRecord]) -> None:\n active_records = [record for record in records if not record[\"deleted\"]]\n deleted_ids = [record[\"id\"] for record in records if record[\"deleted\"]]\n\n if active_records:\n collection2.upsert(\n ids=[record[\"id\"] for record in active_records],\n documents=[record[\"content\"] for record in active_records],\n metadatas=[\n {\n \"source_id\": record[\"id\"],\n \"updated_at\": record[\"updated_at\"],\n }\n for record in active_records\n ],\n )\n\n if deleted_ids:\n collection2.delete(ids=deleted_ids)\n```\n\n### Sync strategy tips\n\n**Track source IDs:** Always store the primary database ID in metadata so you can find and update documents later.\n\n**Batch operations:** Process updates in batches of 100-500 to balance throughput and memory usage.\n\n**Handle deletes:** When records are deleted from your primary database, delete them from Chroma too. Use metadata filters if you track `source_id`.\n\n**Idempotent syncs:** Use `upsert` so re-running a sync doesn't create duplicates.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4700,"content_sha256":"e21c2c64c1b6aff1471a55f02f5497078511d9d48589f91f0b3edc166eb1e732"},{"filename":"updating-deleting/typescript.md","content":"---\nname: Updating and Deleting\ndescription: Update existing documents and delete data from collections\n---\n\n## Updating and Deleting\n\nChroma provides `update`, `upsert`, and `delete` methods for modifying data after initial insertion. Understanding when to use each is important for building reliable data sync pipelines.\n\n### Method overview\n\n| Method | Behavior | Use when |\n|--------|----------|----------|\n| `update` | Modifies existing documents, fails if ID doesn't exist | You know the document exists |\n| `upsert` | Updates if exists, inserts if not | Syncing from external data source |\n| `delete` | Removes documents by ID or filter | Removing stale or unwanted data |\n\n### Imports\n\n```typescript\nimport { CloudClient } from 'chromadb';\nimport { DefaultEmbeddingFunction } from '@chroma-core/default-embed';\n\nconst client = new CloudClient({});\nconst embeddingFunction = new DefaultEmbeddingFunction();\n```\n\n## Update\n\nUpdate modifies existing documents. If an ID doesn't exist, the operation fails silently for that ID (no error thrown, but nothing is updated).\n\n**Important:** When you update a document's text, Chroma re-computes the embedding automatically using the collection's embedding function.\n\n```typescript\nconst collection = await client.getOrCreateCollection({\n name: 'my_collection',\n embeddingFunction,\n});\n\nawait collection.add({\n ids: ['doc1', 'doc2'],\n documents: ['Original text for doc1', 'Original text for doc2'],\n metadatas: [{ category: 'draft' }, { category: 'draft' }],\n});\n\nawait collection.update({\n ids: ['doc1'],\n documents: ['Updated text for doc1'],\n});\n\nawait collection.update({\n ids: ['doc1', 'doc2'],\n metadatas: [{ category: 'published' }, { category: 'published' }],\n});\n\nawait collection.update({\n ids: ['doc2'],\n documents: ['Completely revised doc2 content'],\n metadatas: [{ category: 'published', revision: 2 }],\n});\n```\n\n## Upsert\n\nUpsert is the preferred method for syncing data from an external source. It inserts new documents and updates existing ones in a single operation.\n\n**When to use upsert vs update:**\n- Use `upsert` when syncing from a primary database (you don't know which records are new)\n- Use `update` when you're certain the document already exists\n\n```typescript\nconst collection2 = await client.getOrCreateCollection({\n name: 'articles',\n embeddingFunction,\n});\n\nawait collection2.upsert({\n ids: ['article-123', 'article-456', 'article-789'],\n documents: [\n 'Content of article 123',\n 'Content of article 456',\n 'Content of article 789',\n ],\n metadatas: [\n { source_id: '123', updated_at: Date.now() },\n { source_id: '456', updated_at: Date.now() },\n { source_id: '789', updated_at: Date.now() },\n ],\n});\n\nawait collection2.upsert({\n ids: ['article-123', 'article-456'],\n documents: [\n 'Updated content of article 123',\n 'Updated content of article 456',\n ],\n metadatas: [\n { source_id: '123', updated_at: Date.now() },\n { source_id: '456', updated_at: Date.now() },\n ],\n});\n```\n\n## Delete by ID\n\nThe simplest way to delete documents is by their IDs.\n\n```typescript\nconst collection3 = await client.getOrCreateCollection({\n name: 'my_collection',\n embeddingFunction,\n});\n\nawait collection3.delete({\n ids: ['doc1', 'doc2'],\n});\n\nawait collection3.delete({\n ids: ['doc3'],\n});\n```\n\n## Delete by filter\n\nDelete documents matching metadata or content filters without knowing specific IDs. Useful for bulk cleanup operations.\n\n```typescript\nconst collection4 = await client.getOrCreateCollection({\n name: 'my_collection',\n embeddingFunction,\n});\n\nawait collection4.delete({\n where: { status: 'archived' },\n});\n\nawait collection4.delete({\n where: { source_id: 'old-source-123' },\n});\n\nawait collection4.delete({\n whereDocument: { $contains: 'DEPRECATED' },\n});\n\nawait collection4.delete({\n ids: ['doc1', 'doc2', 'doc3', 'doc4'],\n where: { category: 'temp' },\n});\n```\n\n## Syncing from an external data source\n\nA common pattern is keeping Chroma in sync with a primary database. This example shows how to handle creates, updates, and deletes.\n\n```typescript\ninterface SourceRecord {\n id: string;\n content: string;\n updated_at: number;\n category: string;\n}\n\nasync function syncToChroma(\n collectionName: string,\n records: SourceRecord[],\n deletedIds: string[]\n) {\n const collection = await client.getOrCreateCollection({\n name: collectionName,\n embeddingFunction,\n });\n\n if (records.length > 0) {\n const batchSize = 100;\n\n for (let i = 0; i \u003c records.length; i += batchSize) {\n const batch = records.slice(i, i + batchSize);\n\n await collection.upsert({\n ids: batch.map((record) => `source-${record.id}`),\n documents: batch.map((record) => record.content),\n metadatas: batch.map((record) => ({\n source_id: record.id,\n updated_at: record.updated_at,\n category: record.category,\n })),\n });\n }\n }\n\n if (deletedIds.length > 0) {\n await collection.delete({\n ids: deletedIds.map((id) => `source-${id}`),\n });\n }\n\n return { synced: records.length, deleted: deletedIds.length };\n}\n\nconst changedRecords: SourceRecord[] = [\n {\n id: '1',\n content: 'Article about TypeScript',\n updated_at: Date.now(),\n category: 'tech',\n },\n {\n id: '2',\n content: 'Guide to vector databases',\n updated_at: Date.now(),\n category: 'tech',\n },\n];\n\nconst deletedRecordIds = ['old-1', 'old-2'];\n\nawait syncToChroma('articles', changedRecords, deletedRecordIds);\n```\n\n### Sync strategy tips\n\n**Track source IDs:** Always store the primary database ID in metadata so you can find and update documents later.\n\n**Batch operations:** Process updates in batches of 100-500 to balance throughput and memory usage.\n\n**Handle deletes:** When records are deleted from your primary database, delete them from Chroma too. Use metadata filters if you track `source_id`.\n\n**Idempotent syncs:** Use `upsert` so re-running a sync doesn't create duplicates.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5978,"content_sha256":"3e15d36d6e998457e78cdafb764da8af71c4deafe314acf67be9f3daa0332802"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":2},"content":[{"text":"Instructions","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Intake","type":"text"}]},{"type":"paragraph","content":[{"text":"Do not block on a long questionnaire. Ask only for details that are missing and required to choose the right path:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dense only or hybrid search","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Whether ","type":"text"},{"text":"CHROMA_API_KEY","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"CHROMA_TENANT","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"CHROMA_DATABASE","type":"text","marks":[{"type":"code_inline"}]},{"text":" are already configured","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Existing embedding choice, if any","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If the user has no embedding preference, default to Chroma Cloud Qwen. If hybrid search is required, use ","type":"text"},{"text":"Schema()","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"Search()","type":"text","marks":[{"type":"code_inline"}]},{"text":". If the task is narrow, such as fixing an existing query, reviewing code, or answering an API question, proceed with the repo context instead of forcing intake.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"What to validate","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Correct client import (","type":"text"},{"text":"CloudClient","type":"text","marks":[{"type":"code_inline"}]},{"text":" vs ","type":"text"},{"text":"Client","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Environment variables are set for Cloud deployments","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Embedding function package is installed when the selected TypeScript embedding requires one","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Schema()","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"Search()","type":"text","marks":[{"type":"code_inline"}]},{"text":" are only used for Cloud workflows","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Important:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"get_or_create_collection()","type":"text","marks":[{"type":"code_inline"}]},{"text":" accepts either an ","type":"text"},{"text":"embedding_function","type":"text","marks":[{"type":"code_inline"}]},{"text":" OR a ","type":"text"},{"text":"schema","type":"text","marks":[{"type":"code_inline"}]},{"text":", but not both. Use ","type":"text"},{"text":"schema","type":"text","marks":[{"type":"code_inline"}]},{"text":" when you need multiple indexes, hybrid search, or sparse embeddings; use ","type":"text"},{"text":"embedding_function","type":"text","marks":[{"type":"code_inline"}]},{"text":" for simple dense-only search.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the CLI topic to authenticate and write Cloud credentials:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"chroma login\nchroma db create \u003cdb_name>\nchroma db connect \u003cdb_name> --env-file","type":"text"}]},{"type":"paragraph","content":[{"text":"Then create a ","type":"text"},{"text":"CloudClient","type":"text","marks":[{"type":"code_inline"}]},{"text":" and choose the API based on the search mode:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"typescript"},"content":[{"text":"import { CloudClient } from 'chromadb';\n\nconst client = new CloudClient();\nconst collection = await client.getOrCreateCollection({ name: 'my_collection' });","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"collection.query()","type":"text","marks":[{"type":"code_inline"}]},{"text":" for dense-only search. Use ","type":"text"},{"text":"Schema()","type":"text","marks":[{"type":"code_inline"}]},{"text":" plus ","type":"text"},{"text":"Search()","type":"text","marks":[{"type":"code_inline"}]},{"text":" only when the user needs hybrid retrieval, multiple indexes, or more expressive ranking/query composition.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Cloud Guidance","type":"text"}]},{"type":"paragraph","content":[{"text":"Collections are the main isolation boundary in Chroma Cloud, and metadata is the main filtering mechanism inside a collection. Reach for ","type":"text"},{"text":"Schema()","type":"text","marks":[{"type":"code_inline"}]},{"text":" only when you need explicit dense+sparse or multi-index configuration, and reach for ","type":"text"},{"text":"Search()","type":"text","marks":[{"type":"code_inline"}]},{"text":" only when ","type":"text"},{"text":"query()","type":"text","marks":[{"type":"code_inline"}]},{"text":" is not expressive enough.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Learn More","type":"text"}]},{"type":"paragraph","content":[{"text":"If you need more detailed information about Chroma beyond what's covered in this skill, fetch Chroma's llms.txt for comprehensive documentation: https://docs.trychroma.com/llms.txt","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Available Topics","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Typescript","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Chroma Regex Filtering","type":"text","marks":[{"type":"link","attrs":{"href":"./regex/typescript.md","title":null}}]},{"text":" - Learn how to use regex filters in Chroma queries","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Query and Get","type":"text","marks":[{"type":"link","attrs":{"href":"./querying/typescript.md","title":null}}]},{"text":" - Query and Get Data from Chroma Collections","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Metadata","type":"text","marks":[{"type":"link","attrs":{"href":"./metadata/typescript.md","title":null}}]},{"text":" - Store and query metadata, including filters and array values","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Updating and Deleting","type":"text","marks":[{"type":"link","attrs":{"href":"./updating-deleting/typescript.md","title":null}}]},{"text":" - Update existing documents and delete data from collections","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Schema","type":"text","marks":[{"type":"link","attrs":{"href":"./schema/typescript.md","title":null}}]},{"text":" - Schema() configures collections with multiple indexes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Chroma Cloud Qwen","type":"text","marks":[{"type":"link","attrs":{"href":"./chroma-cloud-qwen/typescript.md","title":null}}]},{"text":" - Chroma's hosted Qwen embedding service","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Error Handling","type":"text","marks":[{"type":"link","attrs":{"href":"./error-handling/typescript.md","title":null}}]},{"text":" - Handling errors and failures when working with Chroma","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Collection Forking","type":"text","marks":[{"type":"link","attrs":{"href":"./forking/typescript.md","title":null}}]},{"text":" - Instantly duplicate collections using copy-on-write forking in Chroma Cloud","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Search() API","type":"text","marks":[{"type":"link","attrs":{"href":"./search-api/typescript.md","title":null}}]},{"text":" - An expressive and flexible API for doing dense and sparse vector search on collections, as well as hybrid search","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Python","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Chroma Regex Filtering","type":"text","marks":[{"type":"link","attrs":{"href":"./regex/python.md","title":null}}]},{"text":" - Learn how to use regex filters in Chroma queries","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Query and Get","type":"text","marks":[{"type":"link","attrs":{"href":"./querying/python.md","title":null}}]},{"text":" - Query and Get Data from Chroma Collections","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Metadata","type":"text","marks":[{"type":"link","attrs":{"href":"./metadata/python.md","title":null}}]},{"text":" - Store and query metadata, including filters and array values","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Updating and Deleting","type":"text","marks":[{"type":"link","attrs":{"href":"./updating-deleting/python.md","title":null}}]},{"text":" - Update existing documents and delete data from collections","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Schema","type":"text","marks":[{"type":"link","attrs":{"href":"./schema/python.md","title":null}}]},{"text":" - Schema() configures collections with multiple indexes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Chroma Cloud Qwen","type":"text","marks":[{"type":"link","attrs":{"href":"./chroma-cloud-qwen/python.md","title":null}}]},{"text":" - Chroma's hosted Qwen embedding service","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Error Handling","type":"text","marks":[{"type":"link","attrs":{"href":"./error-handling/python.md","title":null}}]},{"text":" - Handling errors and failures when working with Chroma","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Collection Forking","type":"text","marks":[{"type":"link","attrs":{"href":"./forking/python.md","title":null}}]},{"text":" - Instantly duplicate collections using copy-on-write forking in Chroma Cloud","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Search() API","type":"text","marks":[{"type":"link","attrs":{"href":"./search-api/python.md","title":null}}]},{"text":" - An expressive and flexible API for doing dense and sparse vector search on collections, as well as hybrid search","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"General","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Data Model","type":"text","marks":[{"type":"link","attrs":{"href":"./data-model.md","title":null}}]},{"text":" - An overview of how Chroma stores data","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Integrating Chroma into an existing system","type":"text","marks":[{"type":"link","attrs":{"href":"./understanding-a-codebase.md","title":null}}]},{"text":" - Guidance for adding Chroma search to an existing application","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Chroma CLI","type":"text","marks":[{"type":"link","attrs":{"href":"./cli.md","title":null}}]},{"text":" - Getting started with the Chroma CLI for Chroma Cloud authentication and database management","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Caching Collection References","type":"text","marks":[{"type":"link","attrs":{"href":"./caching-collections.md","title":null}}]},{"text":" - Reduce repeated collection lookup requests in high-traffic Chroma Cloud applications","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Quotas and Limits","type":"text","marks":[{"type":"link","attrs":{"href":"./quotas.md","title":null}}]},{"text":" - Chroma Cloud quotas and request limits","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"chroma-cloud","author":"@skillopedia","source":{"stars":18,"repo_name":"agent-skills","origin_url":"https://github.com/chroma-core/agent-skills/blob/HEAD/skills/chroma-cloud/SKILL.md","repo_owner":"chroma-core","body_sha256":"0a5537b28e46ac90efa3b4a1fc9556e5a96505be97ae79ba496169b904df924e","cluster_key":"a5af933781beba896899dbe6e7dd6674e13d2b98d9e14fad1fdf1c75af592dee","clean_bundle":{"format":"clean-skill-bundle-v1","source":"chroma-core/agent-skills/skills/chroma-cloud/SKILL.md","attachments":[{"id":"c83a5655-ee24-5537-b85a-7bd924be5965","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c83a5655-ee24-5537-b85a-7bd924be5965/attachment.md","path":"caching-collections.md","size":2728,"sha256":"f22d5fe74188481d35aebab615a0f45016a62c727280cc9e4bd957f6b45881d1","contentType":"text/markdown; charset=utf-8"},{"id":"4fcd986a-c473-585d-af67-d39faddc7332","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4fcd986a-c473-585d-af67-d39faddc7332/attachment.md","path":"chroma-cloud-qwen/python.md","size":617,"sha256":"e17cd750d715b5f4eb33c229c0a4098b41868de8413c03b0e7f396c5b7416451","contentType":"text/markdown; charset=utf-8"},{"id":"093fac67-6e01-5e99-95b9-4a62cdbe8849","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/093fac67-6e01-5e99-95b9-4a62cdbe8849/attachment.md","path":"chroma-cloud-qwen/typescript.md","size":790,"sha256":"fddf7f6340b93c7683e4138defa5e8b003dc9828bda669ce72de12e749a6e1ae","contentType":"text/markdown; charset=utf-8"},{"id":"a8c01c74-e23c-5a64-9379-81d78647d5e7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a8c01c74-e23c-5a64-9379-81d78647d5e7/attachment.md","path":"cli.md","size":3314,"sha256":"1dac97be73ba7d43a2ef53c41b699bd26e3da917ae5819b78fa794cdc713cd0a","contentType":"text/markdown; charset=utf-8"},{"id":"63179c4e-f771-5a64-9003-ca79cd922f67","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/63179c4e-f771-5a64-9003-ca79cd922f67/attachment.md","path":"data-model.md","size":4079,"sha256":"829d2e66b2bfeb4b6b09e90288092d05b1d5b1e28fb73f85f73224ed230a0b2d","contentType":"text/markdown; charset=utf-8"},{"id":"8dde455d-c594-56df-81da-0eb65797bff9","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8dde455d-c594-56df-81da-0eb65797bff9/attachment.md","path":"error-handling/python.md","size":6081,"sha256":"55e2091fa0d356cd68f6eeed085c790528fb58c48b2c6f9c8324c4a1cce3e184","contentType":"text/markdown; charset=utf-8"},{"id":"cf245049-709a-5567-a2a0-32731c780306","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cf245049-709a-5567-a2a0-32731c780306/attachment.md","path":"error-handling/typescript.md","size":7104,"sha256":"6509b582fc7f300d827507533cbdc0d702757c2bb838e4071ffb82190698d768","contentType":"text/markdown; charset=utf-8"},{"id":"048c9026-00fd-535d-a13b-18614a1f5606","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/048c9026-00fd-535d-a13b-18614a1f5606/attachment.md","path":"forking/python.md","size":3312,"sha256":"fcfe50731072bc823c80c6aa438720ab8f3d92f1096681ce69926cb83b660764","contentType":"text/markdown; charset=utf-8"},{"id":"4391112a-82a7-5106-b797-58713bce8966","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4391112a-82a7-5106-b797-58713bce8966/attachment.md","path":"forking/typescript.md","size":2991,"sha256":"63ec183620717f7b07ca77086faba26d0b3b5e558ec9e683663698fafd7a63b8","contentType":"text/markdown; charset=utf-8"},{"id":"cf4c3c50-b1da-5ebb-86fa-1ee327f060dc","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/cf4c3c50-b1da-5ebb-86fa-1ee327f060dc/attachment.md","path":"metadata/python.md","size":4382,"sha256":"d10897194a3724bb2c1301b5088e9b2abd92ca47a508ed5a34532990dee66c8f","contentType":"text/markdown; charset=utf-8"},{"id":"0925d02f-f6a6-52ec-8962-736d8a542585","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0925d02f-f6a6-52ec-8962-736d8a542585/attachment.md","path":"metadata/typescript.md","size":4456,"sha256":"e94f5ef71d092faccca3c0dea2dbc791fb70c3b59cf689a0b654125dbd51ade5","contentType":"text/markdown; charset=utf-8"},{"id":"29d99ea1-ac8f-599c-a803-6038eabfaa0b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/29d99ea1-ac8f-599c-a803-6038eabfaa0b/attachment.md","path":"querying/python.md","size":4012,"sha256":"ed5af96b1be223fc8969cad281d2489f8eb0c46a7de88b7195b5b6bf71debd0f","contentType":"text/markdown; charset=utf-8"},{"id":"90646245-57db-590d-9298-fbbf644c6ad0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/90646245-57db-590d-9298-fbbf644c6ad0/attachment.md","path":"querying/typescript.md","size":4382,"sha256":"3165af04663f391f20a6255746c4ea77f106cfdc2a5296e501b00fca6b4c20f2","contentType":"text/markdown; charset=utf-8"},{"id":"4ab5a80f-62f7-5d18-8f5f-0e250da34a02","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4ab5a80f-62f7-5d18-8f5f-0e250da34a02/attachment.md","path":"quotas.md","size":775,"sha256":"b2362f1e788de68ab04ae3ca4066dfb150eca8c7995a6959dea1e1102f94a2c7","contentType":"text/markdown; charset=utf-8"},{"id":"b661020c-58f8-5045-adb5-83fd24b946f4","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b661020c-58f8-5045-adb5-83fd24b946f4/attachment.md","path":"regex/python.md","size":2201,"sha256":"5f84f17d84520ee00da9ec4fb0d003227d941da22adaf24382d40d02b21032de","contentType":"text/markdown; charset=utf-8"},{"id":"03c4004a-d99c-5365-becf-f76915735130","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/03c4004a-d99c-5365-becf-f76915735130/attachment.md","path":"regex/typescript.md","size":2420,"sha256":"083401976ce682c285d9b1c51faef945c2008c58af92fe6fce0cf1ebbaee70d1","contentType":"text/markdown; charset=utf-8"},{"id":"830753ff-7add-5f21-8b89-e85b4bb1b8b7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/830753ff-7add-5f21-8b89-e85b4bb1b8b7/attachment.md","path":"schema/python.md","size":4579,"sha256":"ef6d2a3e7d3a690194510fa49e032a66beba6ab0db1b00c556f1bc57edeba821","contentType":"text/markdown; charset=utf-8"},{"id":"22915390-fcae-5e76-83b3-6923b4c3cd59","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/22915390-fcae-5e76-83b3-6923b4c3cd59/attachment.md","path":"schema/typescript.md","size":4728,"sha256":"e2964f2eb499c432b4014a22a2a1ecf79f037eddbb14bd249adb266012a3b38a","contentType":"text/markdown; charset=utf-8"},{"id":"ed757625-e930-5a3c-b902-462dad0ab5d2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ed757625-e930-5a3c-b902-462dad0ab5d2/attachment.md","path":"search-api/python.md","size":9690,"sha256":"b084494ac8a80bc7ccdc491dfd97597e638a52fa7bc4cfcda1cb981dcb8ad2fc","contentType":"text/markdown; charset=utf-8"},{"id":"6825b7a9-bd5c-5d87-8b59-6076ac598d9c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6825b7a9-bd5c-5d87-8b59-6076ac598d9c/attachment.md","path":"search-api/typescript.md","size":9650,"sha256":"ead78daa6a75eabb13425804229187f49e14b6d8eb46d6bec1fad61ceda653c9","contentType":"text/markdown; charset=utf-8"},{"id":"395f45e2-d74c-5e2c-b9b4-9642f7f789f2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/395f45e2-d74c-5e2c-b9b4-9642f7f789f2/attachment.md","path":"understanding-a-codebase.md","size":4315,"sha256":"de253c412a1c5899774606a6728cea15693b385d283022a68cbded083f3b9864","contentType":"text/markdown; charset=utf-8"},{"id":"8263c58c-b31f-5e0b-a0b2-cef23c66b17e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8263c58c-b31f-5e0b-a0b2-cef23c66b17e/attachment.md","path":"updating-deleting/python.md","size":4700,"sha256":"e21c2c64c1b6aff1471a55f02f5497078511d9d48589f91f0b3edc166eb1e732","contentType":"text/markdown; charset=utf-8"},{"id":"82e6f85b-e6cf-5cfe-8bce-9968f8807a5e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/82e6f85b-e6cf-5cfe-8bce-9968f8807a5e/attachment.md","path":"updating-deleting/typescript.md","size":5978,"sha256":"3e15d36d6e998457e78cdafb764da8af71c4deafe314acf67be9f3daa0332802","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"2d6750b6b3c402083023cc43d0e1772a15e1a024b55ec59ea824256dc0caffda","attachment_count":23,"text_attachments":23,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/chroma-cloud/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"integrations-apis","category_label":"Integrations"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"integrations-apis","import_tag":"clean-skills-v1","description":"Provides expertise on Chroma Cloud integration for semantic search and hybrid search applications. Use when the user is working with Chroma Cloud, CloudClient, managed collections, Schema(), Search(), hybrid search, or Chroma Cloud CLI workflows."}},"renderedAt":1782986777492}

Instructions Intake Do not block on a long questionnaire. Ask only for details that are missing and required to choose the right path: - Dense only or hybrid search - Whether , , and are already configured - Existing embedding choice, if any If the user has no embedding preference, default to Chroma Cloud Qwen. If hybrid search is required, use and . If the task is narrow, such as fixing an existing query, reviewing code, or answering an API question, proceed with the repo context instead of forcing intake. What to validate - Correct client import ( vs ) - Environment variables are set for Cl…