# Upgrade guide

This document describes the notable breaking changes, if any, in each version of
the Python client. See [CHANGELOG.md](./CHANGELOG.md) for a comprehensive
list of changes.

## v0.5

v0.5 is a complete rewrite of the Python client. The new client is
automatically generated by [Stainless](https://www.stainless.com/) from
a specification of the turbopuffer API. This unlocks several benefits:

  * An async client (`AsyncTurbopuffer`).

  * Improved ergonomics. The Stainless-generated clients have been carefully
    designed to follow language best practices. In Python in particular, the
    client library now sports stricter type hints for use with Mypy, Pyright,
    and other type checkers.

  * Support for additional languages. Client libraries for
    [Go](https://github.com/turbopuffer/turbopuffer-go) and
    [Java](https://github.com/turbopuffer/turbopuffer-java) are already
    available. A client library for
    [Rust](https://github.com/turbopuffer/turbopuffer-rust) is expected by
    the end of 2025.

  * Faster releases. New API features will be available in all client libraries
    within a few days of their release.

There are, however, several breaking changes as a result of this rewrite:

- A `Turbopuffer` client constructor has been introduced. This allows
  accessing multiple turbopuffer regions from the same Python program.

  Old:

  ```ts
  import turbopuffer as tpuf

  tpuf.api_key = os.getenv("TURBOPUFFER_API_KEY")
  tpuf.api_base_url = "https://gcp-us-central1.turbopuffer.com"
  ```

  New:

  ```py
  import turbopuffer
  import os

  tpuf = turbopuffer.Turbopuffer(
      api_key=os.getenv('TURBOPUFFER_API_KEY') # this is the default and can be omitted,
      # Use `region` instead of `baseUrl` if possible.
      region='gcp-us-central1',
      # You can still override the base URL entirely when necessary.
      # base_url='https://my-turbopuffer-proxy.corp.com'
  )

  # You can construct different clients for different turbopuffer regions.
  tpuf_eu = turbopuffer.Turbopuffer(region='gcp-eu-west1')
  ```

- The `Namespace` model has been replaced with a `namespace` method on
  the `Turbopuffer` client.

  Old:

  ```py
  import turbopuffer as tpuf

  ns = tpuf.Namespace("ns")
  ```

  New:

  ```py
  import turbopuffer

  tpuf = turbopuffer.Turbopuffer(region='...')
  ns = tpuf.namespace("ns")
  ```

- The `VectorRow` and `VectorColumns` type have been renamed to
  `Row`/`RowParam` and `ColumnsParam`, respectively.

  Old:

  ```py
  import turbopuffer as tpuf

  row = tpuf.VectorRow(...)
  columns = tpuf.VectorColumns(...)
  ```

  New:

  ```py
  from turbopuffer.types import Row, RowParam, ColumnsParam

  row_param: RowParam = { ... }          # when used in a request
  row: Row = { ... }                     # when used in a response
  columns_param: ColumnsParam = { ... }  # when used in a request
  ```

- The `Row` and `Columns` types flatten attributes:

  Old:

  ```py
  import turbopuffer as tpuf

  rows = [
      tpuf.VectorRow(id=1, vector=[0.1, 0.1], attributes={'attr1': 'one', 'attr2': 'a'}),
      tpuf.VectorRow(id=2, vector=[0.2, 0.2], attributes={'attr1': 'two', 'attr2': 'b'}),
  ]

  columns = tpuf.VectorColumns(
      ids=[1, 2],
      vectors=[[0.1, 0.1], [0.2, 0.2]],
      attributes={
          "attr1": ["one", "two"],
          "attr2": ["a", "b"],
      },
  )
  ```

  New:

  ```py
  from typing import List
  from turbopuffer.types import RowParam, ColumnsParam

  rows: List[RowParam] = [
      {'id': 1, 'vector': [0.1, 0.1], 'attr1': 'one', 'attr2': 'a'},
      {'id': 2, 'vector': [0.2, 0.2], 'attr1': 'two', 'attr2': 'b'},
  ]

  columns: ColumnsParam = {
      'id': [1, 2],
      'vector': [[0.1, 0.1], [0.2, 0.2]],
      'attr1': ["one", "two"],
      "attr2": ["a", "b"],
  }
  ```

- The `filters` and `rank_by` parameters to the `query` method now require
  tuples rather than lists, if using a type checker like Mypy or Pyright.

  Old:

  ```py
  tpuf.namespace("ns").query(
      rank_by=["vector", "ANN", [0.1, 0.2]],
      top_k=10,
      filters=["And", [
          ["name", "Eq", "foo"],
          ["public", "Eq", 1],
      ]],
  )
  ```

  New:

  ```py
  tpuf.namespace("ns").query(
      rank_by=("vector", "ANN", [0.1, 0.2]),
      top_k=10,
      filters=("And", (
          ("name", "Eq", "foo"),
          ("public", "Eq", 1),
      )),
  )
  ```

  The old syntax is still accepted if you are not using a type checker.

- The `update_schema` method now takes a `schema` parameter instead of
  taking the schema directly.

  Old:

  ```py
  tpuf.Namespace('ns').update_schema({
      attr1: 'val1',
      attr2: 'val2',
      # ...
  })
  ```

  New:

  ```py
  tpuf.namespace('ns').update_schema(
      schema={
          attr1: 'val1',
          attr2: 'val2',
          # ...
      },
  )
  ```

- The `metadata` method has been removed.

  - To query for the number of documents in a namespace, use a `count` aggregate
    query instead.

    ```py
    results = tpuf.namespace('ns').query(
        aggregate_by={'count': ('Count', 'id')},
    )
    print(results.aggregations['count'])
    ```

  - To determine the dimensionality of the vectors in a namespace, use
    `schema` instead and inspect the type of the `vector` attribute.

  - There is no replacement for accessing the `created_at` field. [Contact us]
    if you need this field.

- The `export` method has been removed.

  Instead, use the `query` method to page through documents by advancing a
  filter on the `id` attribute.

  See <https://turbopuffer.com/docs/export> for sample code.

- The `rows` field returned by the `query` method is now nullable. If using a
  type checker like Mypy or Pyright, you may need to explicitly check whether `rows` is
  `None`:

  Old:

  ```py
  results = tpuf.namespace('ns').query(top_k=10)
  top_doc = results.rows[0]
  ```

  New:

  ```py
  results = tpuf.namespace('ns').query(top_k=10)
  assert results.rows is not None  # only necessary if using a type checker
  top_doc = results.rows[0]
  ```

  The reason for this change is that queries that specify an `aggregate_by`
  parameter will return data in the `aggregations` field rather than the
  `rows` field.

- Error types have been refactored. The new types are reasonably self
  describing, but [contact us] if you need help.

[contact us]: https://turbopuffer.com/contact
