> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zespan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Chroma

> Auto-trace Chroma collection query, add, and upsert calls with zespan.autopatch() — retrieval spans and RAG context capture with no code changes to your Chroma client calls.

<Note>
  Available for: **Python** and **TypeScript**.
</Note>

Calling Chroma directly (no LangChain/LlamaIndex retriever abstraction in between) previously got zero automatic tracing — only the manual `recordRetrieval`/`record_retrieval` helper. Zespan now patches Chroma's `Collection` so every `query()`, `add()`, and `upsert()` call is captured automatically, the same way LLM provider calls already are.

<Note>
  Patched automatically as part of `zespan.autopatch()`, which both SDKs run on `init()` unless you pass `autopatch: false` (`autopatch=False` in Python). There's no separate opt-in call for vector-DB tracing.
</Note>

## What gets traced

* **Reads** — `collection.query()` emits a `retriever` span
* **Writes** — `collection.add()` **and** `collection.upsert()` both emit an `embedding` span — `add()` is patched because it's the more common ingestion call in RAG tutorials, not just `upsert()`

<Tabs>
  <Tab title="Python">
    ### Installation

    ```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    pip install zespan chromadb
    ```

    ### Usage

    ```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    import zespan

    zespan.init(api_key="zsp_your_api_key_here")  # patches Chroma automatically

    import chromadb

    client = chromadb.Client()
    collection = client.get_or_create_collection("docs")

    # Traced automatically -- emits an "embedding" span
    collection.add(
        ids=["doc1"],
        documents=["chunk text"],
        metadatas=[{"source": "handbook.pdf"}],
    )

    # Traced automatically -- emits a "retriever" span
    results = collection.query(query_texts=["What's in the handbook?"], n_results=5)
    ```

    <Note>
      Chroma's `query()` supports batching multiple queries in one call (`query_texts=["q1", "q2"]`). The wrapper only traces the **first** query's results (index 0 of the returned per-query lists) — multi-query batching in a single call is uncommon enough in RAG apps that per-query span splitting is deferred until real usage shows it's needed.
    </Note>

    <Note>
      Only the **sync** Chroma client (`chromadb.api.models.Collection`) is auto-traced. The async client (`AsyncHttpClient`'s `AsyncCollection`) is not yet covered — if you're on `AsyncHttpClient`, use the manual [`record_retrieval`/`record_vector_search`](/sdk/manual-spans) helpers instead.
    </Note>
  </Tab>

  <Tab title="TypeScript">
    ### Installation

    ```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    npm install @zespan/sdk chromadb
    ```

    ### Usage

    ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    import { zespan } from "@zespan/sdk";

    zespan.init({ apiKey: process.env.ZESPAN_API_KEY! }); // patches Chroma automatically

    import { ChromaClient } from "chromadb";

    const client = new ChromaClient();
    const collection = await client.getOrCreateCollection({ name: "docs" });

    // Traced automatically -- emits an "embedding" span
    await collection.add({
      ids: ["doc1"],
      documents: ["chunk text"],
      metadatas: [{ source: "handbook.pdf" }],
    });

    // Traced automatically -- emits a "retriever" span
    const results = await collection.query({ queryTexts: ["What's in the handbook?"], nResults: 5 });
    ```

    <Note>
      Chroma's Node client returns collections from **plain factory functions**, not a class with a shared `prototype` — patching `Collection.prototype.query` once (the pattern every other Node wrapper uses) would do nothing here. Zespan instead patches `ChromaClient.prototype.{getCollection, createCollection, getOrCreateCollection, getCollectionByCrn, getCollectionById}` so that, after each real call returns a collection object, that specific object's own `query`/`add`/`upsert` methods are patched directly. This is transparent to your code — every collection you obtain through any of those five methods is traced, whichever one you use.
    </Note>
  </Tab>
</Tabs>

## What gets captured

### Read span (`query()`) — `span_kind: "retriever"`, `operation: "vector_search"`

| Field                   | Details                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `provider`              | `"chroma"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `latency_ms`            | Time spent in the real `query()` call                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `status`                | `"success"` or `"error"` (with `error_message`, truncated to 500 characters)                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `rag_context_count`     | Number of results returned (first query only, if batched)                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `metadata.rag_contexts` | One entry per result: `{ content, document_id, score, source }` — `content` comes from the returned document text (or its metadata's text field as a fallback), `document_id` from the result id, `score` from the returned distance. Same shape [`recordRetrieval`/`record_retrieval`](/sdk/manual-spans#recording-retrieved-documents) produces, so the trace-detail **Retrieval** panel and the [RAG evaluators](/dashboard/evaluations#evaluating-rag-pipelines) work with no extra setup |
| `metadata.top_k`        | Requested result count, read from the call's `n_results`/`nResults` option — only present when you actually pass one, since `n_results`/`nResults` is optional in Chroma's client. Not gated by `storePrompts` when present, since it's a count, not query content. Only observable nested under `metadata` — the ingest pipeline persists the `metadata` object verbatim but drops other unrecognized top-level fields                                                                       |
| `metadata.filter`       | The query's `where` option, redacted and stringified — only present when `storePrompts`/`store_prompts` is `true` (its default). Filter values commonly carry user IDs, emails, or other PII, so unlike `top_k` this is dropped entirely, not just redacted, when `storePrompts` is off. (Same `metadata`-only visibility as `top_k` above.)                                                                                                                                                  |

`content` follows your `storePrompts`/`store_prompts` setting and the same redaction rules as prompt text — set `storePrompts: false` and document text is dropped while `document_id`/`source`/`score` are still kept.

### Write span (`add()`/`upsert()`) — `span_kind: "embedding"`, `operation: "vector_upsert"`

| Field                   | Details                                                                                                                                                                                           |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `provider`              | `"chroma"`                                                                                                                                                                                        |
| `metadata.vector_count` | Number of ids in the `add()`/`upsert()` call. Only observable nested under `metadata` — the ingest pipeline persists the `metadata` object verbatim but drops other unrecognized top-level fields |
| `latency_ms`            | Time spent in the real call                                                                                                                                                                       |
| `input_tokens`          | Always `0` — the wrapper doesn't generate embeddings, it only records that vectors were written                                                                                                   |
| `status`                | `"success"` or `"error"` (with `error_message`)                                                                                                                                                   |

<Warning>
  **Linking a Chroma query to a following LLM call.** Trace context (`trace_id`) is picked up from whatever `with_zespan_context()`/`withZespanContext()` scope is active when `query()`/`add()`/`upsert()` runs — the same rule every other Zespan wrapper follows. If your Chroma call and your LLM call are two independent, unwrapped top-level calls, they land on **two different traces**, and RAG evaluators/the Retrieval panel won't see the connection between them. Wrap both in the same `with_zespan_context()`/`withZespanContext()` block, exactly like the [complete RAG pipeline example](/sdk/manual-spans#complete-rag-pipeline-example) in Manual spans.
</Warning>

## Next steps

* [Manual spans](/sdk/manual-spans) — the `record_vector_search()`/`recordVectorSearch()` helper for pgvector, and the full `with_zespan_context()`/`withZespanContext()` trace-linking pattern
* [Evaluating RAG pipelines](/dashboard/evaluations#evaluating-rag-pipelines) — score retrieval quality once `rag_contexts` is on the trace
* [Pinecone](/sdk/integrations/pinecone), [Weaviate](/sdk/integrations/weaviate), [Qdrant](/sdk/integrations/qdrant) — the other auto-traced vector DBs
