Skip to content

Commit 3246627

Browse files
fix: warn when unsupported parameters are silently ignored (#212)
* fix: warn when unsupported parameters are silently ignored Fixes #210 Add UnsupportedParameterWarning emitted via warnings.warn() when a user passes a parameter that has no mapper for the current provider/model. * refactor: use pop pattern in _build_request for cleaner unsupported param detection Each mapper claims its parameter via pop(). Unclaimed params are naturally what remains, removing the need for a separate set + loop.
1 parent 231a355 commit 3246627

4 files changed

Lines changed: 85 additions & 3 deletions

File tree

src/celeste/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
StreamNotExhaustedError,
3030
UnsupportedCapabilityError,
3131
UnsupportedParameterError,
32+
UnsupportedParameterWarning,
3233
UnsupportedProviderError,
3334
ValidationError,
3435
)
@@ -272,6 +273,7 @@ def create_client(
272273
"StrictRefResolvingJsonSchemaGenerator",
273274
"UnsupportedCapabilityError",
274275
"UnsupportedParameterError",
276+
"UnsupportedParameterWarning",
275277
"UnsupportedProviderError",
276278
"Usage",
277279
"UsageField",

src/celeste/client.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Base client for modality-specific AI operations."""
22

3+
import warnings
34
from abc import ABC, abstractmethod
45
from collections.abc import AsyncIterator
56
from json import JSONDecodeError
@@ -10,7 +11,7 @@
1011

1112
from celeste.auth import Authentication
1213
from celeste.core import Modality, Provider
13-
from celeste.exceptions import StreamingNotSupportedError
14+
from celeste.exceptions import StreamingNotSupportedError, UnsupportedParameterWarning
1415
from celeste.http import HTTPClient, get_http_client
1516
from celeste.io import Chunk as ChunkBase
1617
from celeste.io import FinishReason, Input, Output, Usage
@@ -408,9 +409,18 @@ def _build_request(
408409
request = self._init_request(inputs)
409410

410411
for mapper in self.parameter_mappers():
411-
value = parameters.get(mapper.name)
412+
value = parameters.pop(mapper.name, None)
412413
request = mapper.map(request, value, self.model)
413414

415+
for name, value in parameters.items():
416+
if value is not None:
417+
warnings.warn(
418+
f"Parameter '{name}' is not supported by model "
419+
f"'{self.model.id}' and will be ignored.",
420+
UnsupportedParameterWarning,
421+
stacklevel=4,
422+
)
423+
414424
if extra_body:
415425
self._deep_merge(request, extra_body)
416426

src/celeste/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ def __init__(self, parameter: str, model_id: str) -> None:
245245
)
246246

247247

248+
class UnsupportedParameterWarning(UserWarning):
249+
"""Emitted when a parameter is not supported by a provider and will be ignored."""
250+
251+
248252
__all__ = [
249253
"ClientNotFoundError",
250254
"ConstraintViolationError",
@@ -259,5 +263,6 @@ def __init__(self, parameter: str, model_id: str) -> None:
259263
"StreamingNotSupportedError",
260264
"UnsupportedCapabilityError",
261265
"UnsupportedParameterError",
266+
"UnsupportedParameterWarning",
262267
"UnsupportedProviderError",
263268
]

tests/unit_tests/test_client.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from celeste.auth import APIKey
1212
from celeste.client import ModalityClient
1313
from celeste.core import Modality, Provider
14-
from celeste.exceptions import StreamingNotSupportedError
14+
from celeste.exceptions import StreamingNotSupportedError, UnsupportedParameterWarning
1515
from celeste.io import Chunk, Input, Output, Usage
1616
from celeste.models import Model, Operation
1717
from celeste.parameters import ParameterMapper, Parameters
@@ -222,6 +222,71 @@ def parameter_mappers(cls) -> list[ParameterMapper[str]]:
222222
assert request["first_param"] == "first"
223223
assert request["second_param"] == "second"
224224

225+
def test_build_request_warns_on_unsupported_parameter(
226+
self, text_model: Model, api_key: str
227+
) -> None:
228+
"""_build_request emits UnsupportedParameterWarning for unmapped parameters."""
229+
230+
class ClientWithOneMapper(ConcreteModalityClient):
231+
@classmethod
232+
def parameter_mappers(cls) -> list[ParameterMapper[str]]:
233+
return [_create_test_mapper(ParamEnum.FIRST_PARAM)]
234+
235+
client = ClientWithOneMapper(
236+
modality=Modality.TEXT,
237+
model=text_model,
238+
provider=text_model.provider,
239+
auth=APIKey(secret=SecretStr(api_key)),
240+
)
241+
242+
inputs = _TestInput(prompt="test")
243+
244+
with pytest.warns(UnsupportedParameterWarning, match="second_param.*gpt-4"):
245+
client._build_request(inputs, first_param="ok", second_param="unsupported")
246+
247+
def test_build_request_no_warning_for_supported_parameters(
248+
self, text_model: Model, api_key: str
249+
) -> None:
250+
"""_build_request does not warn when all parameters have mappers."""
251+
import warnings
252+
253+
class ClientWithMapper(ConcreteModalityClient):
254+
@classmethod
255+
def parameter_mappers(cls) -> list[ParameterMapper[str]]:
256+
return [_create_test_mapper(ParamEnum.TEST_PARAM)]
257+
258+
client = ClientWithMapper(
259+
modality=Modality.TEXT,
260+
model=text_model,
261+
provider=text_model.provider,
262+
auth=APIKey(secret=SecretStr(api_key)),
263+
)
264+
265+
inputs = _TestInput(prompt="test")
266+
267+
with warnings.catch_warnings():
268+
warnings.simplefilter("error", UnsupportedParameterWarning)
269+
client._build_request(inputs, test_param="supported")
270+
271+
def test_build_request_no_warning_for_none_unsupported_parameter(
272+
self, text_model: Model, api_key: str
273+
) -> None:
274+
"""_build_request does not warn when unsupported parameter value is None."""
275+
import warnings
276+
277+
client = ConcreteModalityClient(
278+
modality=Modality.TEXT,
279+
model=text_model,
280+
provider=text_model.provider,
281+
auth=APIKey(secret=SecretStr(api_key)),
282+
)
283+
284+
inputs = _TestInput(prompt="test")
285+
286+
with warnings.catch_warnings():
287+
warnings.simplefilter("error", UnsupportedParameterWarning)
288+
client._build_request(inputs, test_param=None)
289+
225290
@pytest.mark.parametrize(
226291
"param_value,expected_output",
227292
[

0 commit comments

Comments
 (0)