diff --git a/src/celeste/exceptions.py b/src/celeste/exceptions.py index 4db48b9..83f74ac 100644 --- a/src/celeste/exceptions.py +++ b/src/celeste/exceptions.py @@ -233,6 +233,15 @@ def __init__(self, provider: str) -> None: ) +class InvalidToolError(ValidationError): + """Raised when a tool item is not a Tool instance or dict.""" + + def __init__(self, item: object) -> None: + """Initialize with the invalid item.""" + self.item = item + super().__init__(f"Expected Tool instance or dict, got {type(item).__name__}") + + class UnsupportedParameterError(ValidationError): """Raised when a parameter is not supported by a model.""" @@ -253,6 +262,7 @@ class UnsupportedParameterWarning(UserWarning): "ClientNotFoundError", "ConstraintViolationError", "Error", + "InvalidToolError", "MissingCredentialsError", "MissingDependencyError", "ModalityNotFoundError", diff --git a/src/celeste/protocols/chatcompletions/parameters.py b/src/celeste/protocols/chatcompletions/parameters.py index 44e15be..d6f2493 100644 --- a/src/celeste/protocols/chatcompletions/parameters.py +++ b/src/celeste/protocols/chatcompletions/parameters.py @@ -5,6 +5,7 @@ from pydantic import BaseModel, TypeAdapter +from celeste.exceptions import InvalidToolError from celeste.models import Model from celeste.parameters import FieldMapper, ParameterMapper from celeste.structured_outputs import StrictJsonSchemaGenerator @@ -105,6 +106,8 @@ def map( tools.append(self._map_user_tool(item)) elif isinstance(item, dict): tools.append(item) + else: + raise InvalidToolError(item) return request diff --git a/src/celeste/protocols/openresponses/parameters.py b/src/celeste/protocols/openresponses/parameters.py index 5ccfb34..1761e2a 100644 --- a/src/celeste/protocols/openresponses/parameters.py +++ b/src/celeste/protocols/openresponses/parameters.py @@ -5,6 +5,7 @@ from pydantic import BaseModel, TypeAdapter +from celeste.exceptions import InvalidToolError from celeste.models import Model from celeste.parameters import FieldMapper, ParameterMapper from celeste.structured_outputs import StrictJsonSchemaGenerator @@ -159,6 +160,8 @@ def map( tools.append(self._map_user_tool(item)) elif isinstance(item, dict): tools.append(item) + else: + raise InvalidToolError(item) return request diff --git a/src/celeste/providers/anthropic/messages/parameters.py b/src/celeste/providers/anthropic/messages/parameters.py index 191ee17..8248978 100644 --- a/src/celeste/providers/anthropic/messages/parameters.py +++ b/src/celeste/providers/anthropic/messages/parameters.py @@ -5,6 +5,7 @@ from pydantic import BaseModel, TypeAdapter +from celeste.exceptions import InvalidToolError from celeste.models import Model from celeste.parameters import FieldMapper, ParameterMapper from celeste.structured_outputs import StrictJsonSchemaGenerator @@ -98,6 +99,8 @@ def map( tools.append(self._map_user_tool(item)) elif isinstance(item, dict): tools.append(item) + else: + raise InvalidToolError(item) return request diff --git a/src/celeste/providers/google/generate_content/parameters.py b/src/celeste/providers/google/generate_content/parameters.py index 5a4d93e..0f44828 100644 --- a/src/celeste/providers/google/generate_content/parameters.py +++ b/src/celeste/providers/google/generate_content/parameters.py @@ -7,6 +7,7 @@ from pydantic import BaseModel, TypeAdapter from celeste.artifacts import ImageArtifact +from celeste.exceptions import InvalidToolError from celeste.mime_types import ApplicationMimeType from celeste.models import Model from celeste.parameters import ParameterMapper @@ -213,6 +214,8 @@ def map( fn_declarations.append(self._map_user_tool(item)) elif isinstance(item, dict): tools.append(item) + else: + raise InvalidToolError(item) if fn_declarations: tools.append({"functionDeclarations": fn_declarations}) diff --git a/tests/unit_tests/test_exceptions.py b/tests/unit_tests/test_exceptions.py index 22b5307..12126c2 100644 --- a/tests/unit_tests/test_exceptions.py +++ b/tests/unit_tests/test_exceptions.py @@ -7,6 +7,7 @@ ClientNotFoundError, ConstraintViolationError, Error, + InvalidToolError, MissingCredentialsError, ModalityNotFoundError, ModelNotFoundError, @@ -16,6 +17,7 @@ UnsupportedCapabilityError, UnsupportedParameterError, UnsupportedProviderError, + ValidationError, ) from celeste.models import Model @@ -368,3 +370,24 @@ def test_inherits_from_credentials_error(self) -> None: exc = UnsupportedProviderError("test_provider") assert isinstance(exc, CredentialsError) assert isinstance(exc, Error) + + +class TestInvalidToolError: + """Test InvalidToolError exception.""" + + def test_creates_with_item(self) -> None: + """Test exception stores the invalid item.""" + exc = InvalidToolError(42) + assert exc.item == 42 + assert "int" in str(exc) + + def test_message_includes_type_name(self) -> None: + """Test exception message shows the actual type passed.""" + exc = InvalidToolError("web_search") + assert str(exc) == "Expected Tool instance or dict, got str" + + def test_inherits_from_validation_error(self) -> None: + """Test InvalidToolError inherits from ValidationError.""" + exc = InvalidToolError(42) + assert isinstance(exc, ValidationError) + assert isinstance(exc, Error)