Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "2.9.0"
".": "2.10.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 14
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/turbopuffer-benesch/turbopuffer-f382ef6b6b767722c39ffac9ef27b383f3192cdb180f1674f2969836c3cbda4f.yml
openapi_spec_hash: 96988a5a07baae1ac411e14b0eef8424
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/turbopuffer-benesch/turbopuffer-10add2e38f8fa0826d10992d6de7c4d51c42f2c862412b221527a5d08af43a22.yml
openapi_spec_hash: 9382f99c761306a6a1b943beb230a8d4
config_hash: e8f59500814fce6a9e1a14dc46e0dfc7
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 2.10.0 (2026-09-04)

Full Changelog: [v2.9.0...v2.10.0](https://github.com/turbopuffer/turbopuffer-python/compare/v2.9.0...v2.10.0)

### Features

* Add `read_only` namespace field ([691d1ee](https://github.com/turbopuffer/turbopuffer-python/commit/691d1ee1399f85e11cf37eff912f4f0b6fd5e83d))
* tpuf-engine: expose billed_replicas in pinning metadata ([0fd8f32](https://github.com/turbopuffer/turbopuffer-python/commit/0fd8f323217273d9a22e0f6765621b6dc237ce7b))

## 2.9.0 (2026-08-21)

Full Changelog: [v2.8.0...v2.9.0](https://github.com/turbopuffer/turbopuffer-python/compare/v2.8.0...v2.9.0)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "turbopuffer"
version = "2.9.0"
version = "2.10.0"
description = "The official Python library for the turbopuffer API"
dynamic = ["readme"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src/turbopuffer/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "turbopuffer"
__version__ = "2.9.0" # x-release-please-version
__version__ = "2.10.0" # x-release-please-version
22 changes: 20 additions & 2 deletions src/turbopuffer/resources/namespaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,7 @@ def update_metadata(
*,
namespace: str | None = None,
pinning: Optional[namespace_update_metadata_params.Pinning] | Omit = omit,
read_only: bool | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
Expand All @@ -662,6 +663,9 @@ def update_metadata(
- `true`: enable pinning with default configuration
- Object: set pinning configuration

read_only: Set to `true` to reject document and schema writes, or `false` to allow them.
Writes already in progress may still commit. Metadata updates remain available.

extra_headers: Send extra headers

extra_query: Add additional query parameters to the request
Expand All @@ -676,7 +680,13 @@ def update_metadata(
raise ValueError(f"Expected a non-empty value for `namespace` but received {namespace!r}")
return self._patch(
path_template("/v1/namespaces/{namespace}/metadata", namespace=namespace),
body=maybe_transform({"pinning": pinning}, namespace_update_metadata_params.NamespaceUpdateMetadataParams),
body=maybe_transform(
{
"pinning": pinning,
"read_only": read_only,
},
namespace_update_metadata_params.NamespaceUpdateMetadataParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
Expand Down Expand Up @@ -1430,6 +1440,7 @@ async def update_metadata(
*,
namespace: str | None = None,
pinning: Optional[namespace_update_metadata_params.Pinning] | Omit = omit,
read_only: bool | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
Expand All @@ -1448,6 +1459,9 @@ async def update_metadata(
- `true`: enable pinning with default configuration
- Object: set pinning configuration

read_only: Set to `true` to reject document and schema writes, or `false` to allow them.
Writes already in progress may still commit. Metadata updates remain available.

extra_headers: Send extra headers

extra_query: Add additional query parameters to the request
Expand All @@ -1463,7 +1477,11 @@ async def update_metadata(
return await self._patch(
path_template("/v1/namespaces/{namespace}/metadata", namespace=namespace),
body=await async_maybe_transform(
{"pinning": pinning}, namespace_update_metadata_params.NamespaceUpdateMetadataParams
{
"pinning": pinning,
"read_only": read_only,
},
namespace_update_metadata_params.NamespaceUpdateMetadataParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
Expand Down
11 changes: 11 additions & 0 deletions src/turbopuffer/types/namespace_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ class PinningStatus(BaseModel):
ready_replicas: int
"""The number of replicas that are warm and serving traffic."""

replicas: int
"""The number of running replicas for the namespace.

Replicas are billed once running, even before they finish warming their caches
and become ready to serve traffic. This count is updated independently and may
briefly disagree with the other status fields.
"""

updated_at: datetime
"""The timestamp of the latest pinning status snapshot."""

Expand Down Expand Up @@ -86,6 +94,9 @@ class NamespaceMetadata(BaseModel):
namespace.
"""

read_only: Optional[bool] = None
"""Whether document and schema writes are rejected. Omitted when `false`."""

sharding: Optional[ShardingConfig] = None
"""
Configuration for namespace sharding, which partitions a namespace's documents
Expand Down
6 changes: 6 additions & 0 deletions src/turbopuffer/types/namespace_update_metadata_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,11 @@ class NamespaceUpdateMetadataParams(TypedDict, total=False):
- Object: set pinning configuration
"""

read_only: bool
"""Set to `true` to reject document and schema writes, or `false` to allow them.

Writes already in progress may still commit. Metadata updates remain available.
"""


Pinning: TypeAlias = Union[bool, PinningConfigParam]
2 changes: 2 additions & 0 deletions tests/api_resources/test_namespaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ def test_method_update_metadata_with_all_params(self, client: Turbopuffer) -> No
namespace = client.namespace("namespace").update_metadata(
namespace="namespace",
pinning=True,
read_only=True,
)
assert_matches_type(NamespaceMetadata, namespace, path=["response"])

Expand Down Expand Up @@ -1123,6 +1124,7 @@ async def test_method_update_metadata(self, async_client: AsyncTurbopuffer) -> N
async def test_method_update_metadata_with_all_params(self, async_client: AsyncTurbopuffer) -> None:
namespace = await async_client.namespace("namespace").update_metadata(
pinning=True,
read_only=True,
)
assert_matches_type(NamespaceMetadata, namespace, path=["response"])

Expand Down
8 changes: 3 additions & 5 deletions tests/custom/test_vectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from turbopuffer.types import (
Row,
Vector,
QueryBilling,
VectorEncoding,
NamespaceWriteParams,
NamespaceQueryResponse,
Expand Down Expand Up @@ -203,10 +202,9 @@ def check_results(vector_set: NamespaceQueryResponse, expected: List[Row]):
include_attributes=["hello", "vector"],
)
check_results(vector_set, expected)
assert vector_set.billing == QueryBilling(
billable_logical_bytes_queried=256000000,
billable_logical_bytes_returned=105,
)
assert vector_set.billing.billable_logical_bytes_returned == 105
# 256MB was the old query floor; 1.28GB is current pricing.
assert vector_set.billing.billable_logical_bytes_queried in (256000000, 1280000000)

# Test query with dict
vector_set = ns.query(
Expand Down
Loading