Skip to content

Commit 954466d

Browse files
committed
fix(indexes): forward read_capacity and schema from ServerlessSpec at create time
## Purpose `ServerlessSpec.read_capacity` and `ServerlessSpec.schema` were silently dropped when building the POST /indexes request body. Users who set these fields on `ServerlessSpec` had no working path to configure serverless read capacity at index creation time. ## Solution Updated `build_create_body` in `pinecone/_internal/indexes_helpers.py` to conditionally include `read_capacity` and `schema` from `ServerlessSpec` when they are set (non-None). Follows the same pattern already used by `build_byoc_body`. Added unit tests in `test_indexes_helpers.py` covering presence and absence of both fields, and an integration test in `tests/integration/test_client.py` verifying the request body is correctly constructed end-to-end.
1 parent 4245565 commit 954466d

3 files changed

Lines changed: 89 additions & 2 deletions

File tree

pinecone/_internal/indexes_helpers.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,12 @@ def build_create_body(
146146
body["tags"] = tags
147147

148148
if isinstance(spec, ServerlessSpec):
149-
body["spec"] = {"serverless": {"cloud": spec.cloud, "region": spec.region}}
149+
serverless_dict: dict[str, Any] = {"cloud": spec.cloud, "region": spec.region}
150+
if spec.read_capacity is not None:
151+
serverless_dict["read_capacity"] = spec.read_capacity
152+
if spec.schema is not None:
153+
serverless_dict["schema"] = spec.schema
154+
body["spec"] = {"serverless": serverless_dict}
150155
elif isinstance(spec, PodSpec):
151156
body["spec"] = {"pod": msgspec.to_builtins(spec)}
152157
elif isinstance(spec, dict):

tests/integration/test_client.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,29 @@ def test_configure_index_serverless_read_capacity() -> None:
142142

143143
sent_body = json.loads(route.calls[0].request.content)
144144
assert sent_body == {"spec": {"serverless": {"read_capacity": {"mode": "OnDemand"}}}}
145+
146+
147+
@respx.mock
148+
def test_create_index_serverless_read_capacity_spec() -> None:
149+
"""ServerlessSpec.read_capacity is forwarded into spec.serverless.read_capacity in the request body."""
150+
from pinecone.models.indexes.specs import ServerlessSpec
151+
152+
response_body = make_index_response(name="rc-index")
153+
route = respx.post(f"{BASE_URL}/indexes").mock(
154+
return_value=httpx.Response(201, json=response_body),
155+
)
156+
157+
pc = Pinecone(api_key="test-key")
158+
result = pc.indexes.create(
159+
name="rc-index",
160+
spec=ServerlessSpec(cloud="aws", region="us-east-1", read_capacity={"mode": "OnDemand"}),
161+
dimension=128,
162+
metric="cosine",
163+
timeout=-1,
164+
)
165+
166+
assert result.name == "rc-index"
167+
sent_body = json.loads(route.calls[0].request.content)
168+
assert sent_body["spec"]["serverless"]["read_capacity"] == {"mode": "OnDemand"}
169+
assert result.spec.serverless is not None
170+
assert result.spec.serverless.read_capacity is not None

tests/unit/test_indexes_helpers.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
validate_read_capacity,
1111
)
1212
from pinecone.errors.exceptions import ValidationError
13-
from pinecone.models.indexes.specs import EmbedConfig, IntegratedSpec
13+
from pinecone.models.indexes.specs import EmbedConfig, IntegratedSpec, ServerlessSpec
1414

1515

1616
def test_build_create_body_dict_spec_not_mutated_by_schema() -> None:
@@ -126,3 +126,59 @@ def test_build_integrated_body_read_capacity_absent_when_none() -> None:
126126
read_capacity=None,
127127
)
128128
assert "read_capacity" not in body
129+
130+
131+
def test_build_create_body_serverless_includes_read_capacity() -> None:
132+
spec = ServerlessSpec(
133+
cloud="aws",
134+
region="us-east-1",
135+
read_capacity={
136+
"mode": "Dedicated",
137+
"dedicated": {
138+
"node_type": "t1",
139+
"scaling": "Manual",
140+
"manual": {"shards": 2, "replicas": 3},
141+
},
142+
},
143+
)
144+
body = build_create_body(
145+
name="test-index",
146+
spec=spec,
147+
dimension=128,
148+
metric="cosine",
149+
vector_type="dense",
150+
deletion_protection="disabled",
151+
tags=None,
152+
schema=None,
153+
)
154+
assert body["spec"]["serverless"]["read_capacity"]["mode"] == "Dedicated"
155+
156+
157+
def test_build_create_body_serverless_read_capacity_absent_when_none() -> None:
158+
spec = ServerlessSpec(cloud="aws", region="us-east-1")
159+
body = build_create_body(
160+
name="test-index",
161+
spec=spec,
162+
dimension=128,
163+
metric="cosine",
164+
vector_type="dense",
165+
deletion_protection="disabled",
166+
tags=None,
167+
schema=None,
168+
)
169+
assert "read_capacity" not in body["spec"]["serverless"]
170+
171+
172+
def test_build_create_body_serverless_spec_schema_included() -> None:
173+
spec = ServerlessSpec(cloud="aws", region="us-east-1", schema={"genre": {"type": "str"}})
174+
body = build_create_body(
175+
name="test-index",
176+
spec=spec,
177+
dimension=128,
178+
metric="cosine",
179+
vector_type="dense",
180+
deletion_protection="disabled",
181+
tags=None,
182+
schema=None,
183+
)
184+
assert body["spec"]["serverless"]["schema"] == {"genre": {"type": "str"}}

0 commit comments

Comments
 (0)