diff --git a/gooddata-api-client/gooddata_api_client/model/anthropic_api_key_auth.py b/gooddata-api-client/gooddata_api_client/model/anthropic_api_key_auth.py new file mode 100644 index 000000000..c865feea7 --- /dev/null +++ b/gooddata-api-client/gooddata_api_client/model/anthropic_api_key_auth.py @@ -0,0 +1,332 @@ +""" + OpenAPI definition + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: v0 + Contact: support@gooddata.com + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from gooddata_api_client.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, + OpenApiModel +) +from gooddata_api_client.exceptions import ApiAttributeError + + +def lazy_import(): + from gooddata_api_client.model.anthropic_api_key_auth_all_of import AnthropicApiKeyAuthAllOf + globals()['AnthropicApiKeyAuthAllOf'] = AnthropicApiKeyAuthAllOf + + +class AnthropicApiKeyAuth(ModelComposed): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + ('type',): { + 'API_KEY': "API_KEY", + }, + } + + validations = { + ('api_key',): { + 'max_length': 255, + }, + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + 'type': (str,), # noqa: E501 + 'api_key': (str, none_type,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'type': 'type', # noqa: E501 + 'api_key': 'apiKey', # noqa: E501 + } + + read_only_vars = { + } + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, *args, **kwargs): # noqa: E501 + """AnthropicApiKeyAuth - a model defined in OpenAPI + + Keyword Args: + type (str): Authentication type.. defaults to "API_KEY", must be one of ["API_KEY", ] # noqa: E501 + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + api_key (str, none_type): Anthropic API key.. [optional] # noqa: E501 + """ + + type = kwargs.get('type', "API_KEY") + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + for arg in args: + if isinstance(arg, dict): + kwargs.update(arg) + else: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + constant_args = { + '_check_type': _check_type, + '_path_to_item': _path_to_item, + '_spec_property_naming': _spec_property_naming, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + composed_info = validate_get_composed_info( + constant_args, kwargs, self) + self._composed_instances = composed_info[0] + self._var_name_to_model_instances = composed_info[1] + self._additional_properties_model_instances = composed_info[2] + discarded_args = composed_info[3] + + for var_name, var_value in kwargs.items(): + if var_name in discarded_args and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self._additional_properties_model_instances: + # discard variable. + continue + setattr(self, var_name, var_value) + + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + '_composed_instances', + '_var_name_to_model_instances', + '_additional_properties_model_instances', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): # noqa: E501 + """AnthropicApiKeyAuth - a model defined in OpenAPI + + Keyword Args: + type (str): Authentication type.. defaults to "API_KEY", must be one of ["API_KEY", ] # noqa: E501 + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + api_key (str, none_type): Anthropic API key.. [optional] # noqa: E501 + """ + + type = kwargs.get('type', "API_KEY") + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + for arg in args: + if isinstance(arg, dict): + kwargs.update(arg) + else: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + constant_args = { + '_check_type': _check_type, + '_path_to_item': _path_to_item, + '_spec_property_naming': _spec_property_naming, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + composed_info = validate_get_composed_info( + constant_args, kwargs, self) + self._composed_instances = composed_info[0] + self._var_name_to_model_instances = composed_info[1] + self._additional_properties_model_instances = composed_info[2] + discarded_args = composed_info[3] + + for var_name, var_value in kwargs.items(): + if var_name in discarded_args and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self._additional_properties_model_instances: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") + + @cached_property + def _composed_schemas(): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + lazy_import() + return { + 'anyOf': [ + ], + 'allOf': [ + AnthropicApiKeyAuthAllOf, + ], + 'oneOf': [ + ], + } diff --git a/gooddata-api-client/gooddata_api_client/model/anthropic_api_key_auth_all_of.py b/gooddata-api-client/gooddata_api_client/model/anthropic_api_key_auth_all_of.py new file mode 100644 index 000000000..ce9ecf477 --- /dev/null +++ b/gooddata-api-client/gooddata_api_client/model/anthropic_api_key_auth_all_of.py @@ -0,0 +1,274 @@ +""" + OpenAPI definition + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: v0 + Contact: support@gooddata.com + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from gooddata_api_client.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, + OpenApiModel +) +from gooddata_api_client.exceptions import ApiAttributeError + + + +class AnthropicApiKeyAuthAllOf(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + ('type',): { + 'API_KEY': "API_KEY", + }, + } + + validations = { + ('api_key',): { + 'max_length': 255, + }, + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'api_key': (str, none_type,), # noqa: E501 + 'type': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'api_key': 'apiKey', # noqa: E501 + 'type': 'type', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, *args, **kwargs): # noqa: E501 + """AnthropicApiKeyAuthAllOf - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + api_key (str, none_type): Anthropic API key.. [optional] # noqa: E501 + type (str): Authentication type.. [optional] if omitted the server will use the default value of "API_KEY" # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', True) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + for arg in args: + if isinstance(arg, dict): + kwargs.update(arg) + else: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): # noqa: E501 + """AnthropicApiKeyAuthAllOf - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + api_key (str, none_type): Anthropic API key.. [optional] # noqa: E501 + type (str): Authentication type.. [optional] if omitted the server will use the default value of "API_KEY" # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + for arg in args: + if isinstance(arg, dict): + kwargs.update(arg) + else: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/gooddata-api-client/gooddata_api_client/model/anthropic_provider_auth.py b/gooddata-api-client/gooddata_api_client/model/anthropic_provider_auth.py new file mode 100644 index 000000000..d30f70059 --- /dev/null +++ b/gooddata-api-client/gooddata_api_client/model/anthropic_provider_auth.py @@ -0,0 +1,330 @@ +""" + OpenAPI definition + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: v0 + Contact: support@gooddata.com + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from gooddata_api_client.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, + OpenApiModel +) +from gooddata_api_client.exceptions import ApiAttributeError + + +def lazy_import(): + from gooddata_api_client.model.anthropic_api_key_auth import AnthropicApiKeyAuth + globals()['AnthropicApiKeyAuth'] = AnthropicApiKeyAuth + + +class AnthropicProviderAuth(ModelComposed): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + ('type',): { + 'API_KEY': "API_KEY", + }, + } + + validations = { + ('api_key',): { + 'max_length': 255, + }, + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + 'api_key': (str, none_type,), # noqa: E501 + 'type': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'api_key': 'apiKey', # noqa: E501 + 'type': 'type', # noqa: E501 + } + + read_only_vars = { + } + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, *args, **kwargs): # noqa: E501 + """AnthropicProviderAuth - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + api_key (str, none_type): Anthropic API key.. [optional] # noqa: E501 + type (str): Authentication type.. [optional] if omitted the server will use the default value of "API_KEY" # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + for arg in args: + if isinstance(arg, dict): + kwargs.update(arg) + else: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + constant_args = { + '_check_type': _check_type, + '_path_to_item': _path_to_item, + '_spec_property_naming': _spec_property_naming, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + composed_info = validate_get_composed_info( + constant_args, kwargs, self) + self._composed_instances = composed_info[0] + self._var_name_to_model_instances = composed_info[1] + self._additional_properties_model_instances = composed_info[2] + discarded_args = composed_info[3] + + for var_name, var_value in kwargs.items(): + if var_name in discarded_args and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self._additional_properties_model_instances: + # discard variable. + continue + setattr(self, var_name, var_value) + + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + '_composed_instances', + '_var_name_to_model_instances', + '_additional_properties_model_instances', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): # noqa: E501 + """AnthropicProviderAuth - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + api_key (str, none_type): Anthropic API key.. [optional] # noqa: E501 + type (str): Authentication type.. [optional] if omitted the server will use the default value of "API_KEY" # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + for arg in args: + if isinstance(arg, dict): + kwargs.update(arg) + else: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + constant_args = { + '_check_type': _check_type, + '_path_to_item': _path_to_item, + '_spec_property_naming': _spec_property_naming, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + composed_info = validate_get_composed_info( + constant_args, kwargs, self) + self._composed_instances = composed_info[0] + self._var_name_to_model_instances = composed_info[1] + self._additional_properties_model_instances = composed_info[2] + discarded_args = composed_info[3] + + for var_name, var_value in kwargs.items(): + if var_name in discarded_args and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self._additional_properties_model_instances: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") + + @cached_property + def _composed_schemas(): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error because the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + lazy_import() + return { + 'anyOf': [ + ], + 'allOf': [ + ], + 'oneOf': [ + AnthropicApiKeyAuth, + ], + } diff --git a/gooddata-api-client/gooddata_api_client/model/anthropic_provider_config.py b/gooddata-api-client/gooddata_api_client/model/anthropic_provider_config.py new file mode 100644 index 000000000..6f173d065 --- /dev/null +++ b/gooddata-api-client/gooddata_api_client/model/anthropic_provider_config.py @@ -0,0 +1,294 @@ +""" + OpenAPI definition + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 + + The version of the OpenAPI document: v0 + Contact: support@gooddata.com + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from gooddata_api_client.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, + OpenApiModel +) +from gooddata_api_client.exceptions import ApiAttributeError + + +def lazy_import(): + from gooddata_api_client.model.anthropic_provider_auth import AnthropicProviderAuth + globals()['AnthropicProviderAuth'] = AnthropicProviderAuth + + +class AnthropicProviderConfig(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + ('type',): { + 'ANTHROPIC': "ANTHROPIC", + }, + } + + validations = { + ('base_url',): { + 'max_length': 255, + }, + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + 'auth': (AnthropicProviderAuth,), # noqa: E501 + 'type': (str,), # noqa: E501 + 'base_url': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'auth': 'auth', # noqa: E501 + 'type': 'type', # noqa: E501 + 'base_url': 'baseUrl', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, auth, *args, **kwargs): # noqa: E501 + """AnthropicProviderConfig - a model defined in OpenAPI + + Args: + auth (AnthropicProviderAuth): + + Keyword Args: + type (str): Provider type.. defaults to "ANTHROPIC", must be one of ["ANTHROPIC", ] # noqa: E501 + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + base_url (str): Custom base URL for Anthropic API.. [optional] if omitted the server will use the default value of "https://api.anthropic.com" # noqa: E501 + """ + + type = kwargs.get('type', "ANTHROPIC") + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', True) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + for arg in args: + if isinstance(arg, dict): + kwargs.update(arg) + else: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.auth = auth + self.type = type + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, auth, *args, **kwargs): # noqa: E501 + """AnthropicProviderConfig - a model defined in OpenAPI + + Args: + auth (AnthropicProviderAuth): + + Keyword Args: + type (str): Provider type.. defaults to "ANTHROPIC", must be one of ["ANTHROPIC", ] # noqa: E501 + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + base_url (str): Custom base URL for Anthropic API.. [optional] if omitted the server will use the default value of "https://api.anthropic.com" # noqa: E501 + """ + + type = kwargs.get('type', "ANTHROPIC") + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + for arg in args: + if isinstance(arg, dict): + kwargs.update(arg) + else: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.auth = auth + self.type = type + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/gooddata-api-client/gooddata_api_client/model/json_api_llm_provider_in_attributes_provider_config.py b/gooddata-api-client/gooddata_api_client/model/json_api_llm_provider_in_attributes_provider_config.py index c37b32b2a..9efc6d403 100644 --- a/gooddata-api-client/gooddata_api_client/model/json_api_llm_provider_in_attributes_provider_config.py +++ b/gooddata-api-client/gooddata_api_client/model/json_api_llm_provider_in_attributes_provider_config.py @@ -31,10 +31,12 @@ def lazy_import(): + from gooddata_api_client.model.anthropic_provider_config import AnthropicProviderConfig from gooddata_api_client.model.aws_bedrock_provider_config import AwsBedrockProviderConfig from gooddata_api_client.model.azure_foundry_provider_config import AzureFoundryProviderConfig from gooddata_api_client.model.open_ai_provider_auth import OpenAiProviderAuth from gooddata_api_client.model.open_ai_provider_config import OpenAIProviderConfig + globals()['AnthropicProviderConfig'] = AnthropicProviderConfig globals()['AwsBedrockProviderConfig'] = AwsBedrockProviderConfig globals()['AzureFoundryProviderConfig'] = AzureFoundryProviderConfig globals()['OpenAIProviderConfig'] = OpenAIProviderConfig @@ -68,6 +70,7 @@ class JsonApiLlmProviderInAttributesProviderConfig(ModelComposed): allowed_values = { ('type',): { 'OPENAI': "OPENAI", + 'ANTHROPIC': "ANTHROPIC", }, } @@ -356,6 +359,7 @@ def _composed_schemas(): 'allOf': [ ], 'oneOf': [ + AnthropicProviderConfig, AwsBedrockProviderConfig, AzureFoundryProviderConfig, OpenAIProviderConfig, diff --git a/gooddata-api-client/gooddata_api_client/model/list_llm_provider_models_request_provider_config.py b/gooddata-api-client/gooddata_api_client/model/list_llm_provider_models_request_provider_config.py index 21d26004d..4803cbfb4 100644 --- a/gooddata-api-client/gooddata_api_client/model/list_llm_provider_models_request_provider_config.py +++ b/gooddata-api-client/gooddata_api_client/model/list_llm_provider_models_request_provider_config.py @@ -31,10 +31,12 @@ def lazy_import(): + from gooddata_api_client.model.anthropic_provider_config import AnthropicProviderConfig from gooddata_api_client.model.aws_bedrock_provider_config import AwsBedrockProviderConfig from gooddata_api_client.model.azure_foundry_provider_config import AzureFoundryProviderConfig from gooddata_api_client.model.open_ai_provider_auth import OpenAiProviderAuth from gooddata_api_client.model.open_ai_provider_config import OpenAIProviderConfig + globals()['AnthropicProviderConfig'] = AnthropicProviderConfig globals()['AwsBedrockProviderConfig'] = AwsBedrockProviderConfig globals()['AzureFoundryProviderConfig'] = AzureFoundryProviderConfig globals()['OpenAIProviderConfig'] = OpenAIProviderConfig @@ -68,6 +70,7 @@ class ListLlmProviderModelsRequestProviderConfig(ModelComposed): allowed_values = { ('type',): { 'OPENAI': "OPENAI", + 'ANTHROPIC': "ANTHROPIC", }, } @@ -356,6 +359,7 @@ def _composed_schemas(): 'allOf': [ ], 'oneOf': [ + AnthropicProviderConfig, AwsBedrockProviderConfig, AzureFoundryProviderConfig, OpenAIProviderConfig, diff --git a/gooddata-api-client/gooddata_api_client/model/llm_provider_config.py b/gooddata-api-client/gooddata_api_client/model/llm_provider_config.py index 2ed845ed4..a65ad9737 100644 --- a/gooddata-api-client/gooddata_api_client/model/llm_provider_config.py +++ b/gooddata-api-client/gooddata_api_client/model/llm_provider_config.py @@ -31,10 +31,12 @@ def lazy_import(): + from gooddata_api_client.model.anthropic_provider_config import AnthropicProviderConfig from gooddata_api_client.model.aws_bedrock_provider_config import AwsBedrockProviderConfig from gooddata_api_client.model.azure_foundry_provider_config import AzureFoundryProviderConfig from gooddata_api_client.model.open_ai_provider_auth import OpenAiProviderAuth from gooddata_api_client.model.open_ai_provider_config import OpenAIProviderConfig + globals()['AnthropicProviderConfig'] = AnthropicProviderConfig globals()['AwsBedrockProviderConfig'] = AwsBedrockProviderConfig globals()['AzureFoundryProviderConfig'] = AzureFoundryProviderConfig globals()['OpenAIProviderConfig'] = OpenAIProviderConfig @@ -68,6 +70,7 @@ class LlmProviderConfig(ModelComposed): allowed_values = { ('type',): { 'OPENAI': "OPENAI", + 'ANTHROPIC': "ANTHROPIC", }, } @@ -356,6 +359,7 @@ def _composed_schemas(): 'allOf': [ ], 'oneOf': [ + AnthropicProviderConfig, AwsBedrockProviderConfig, AzureFoundryProviderConfig, OpenAIProviderConfig, diff --git a/gooddata-api-client/gooddata_api_client/models/__init__.py b/gooddata-api-client/gooddata_api_client/models/__init__.py index 557074746..e6c48ecd3 100644 --- a/gooddata-api-client/gooddata_api_client/models/__init__.py +++ b/gooddata-api-client/gooddata_api_client/models/__init__.py @@ -102,6 +102,10 @@ from gooddata_api_client.model.automation_tabular_export import AutomationTabularExport from gooddata_api_client.model.automation_visual_export import AutomationVisualExport from gooddata_api_client.model.available_assignees import AvailableAssignees +from gooddata_api_client.model.anthropic_api_key_auth import AnthropicApiKeyAuth +from gooddata_api_client.model.anthropic_api_key_auth_all_of import AnthropicApiKeyAuthAllOf +from gooddata_api_client.model.anthropic_provider_auth import AnthropicProviderAuth +from gooddata_api_client.model.anthropic_provider_config import AnthropicProviderConfig from gooddata_api_client.model.aws_bedrock_access_key_auth import AwsBedrockAccessKeyAuth from gooddata_api_client.model.aws_bedrock_access_key_auth_all_of import AwsBedrockAccessKeyAuthAllOf from gooddata_api_client.model.aws_bedrock_provider_config import AwsBedrockProviderConfig diff --git a/packages/gooddata-sdk/src/gooddata_sdk/__init__.py b/packages/gooddata-sdk/src/gooddata_sdk/__init__.py index 91f87c918..84f9d6954 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/__init__.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/__init__.py @@ -116,6 +116,8 @@ CatalogRsaSpecification, ) from gooddata_sdk.catalog.organization.entity_model.llm_provider import ( + CatalogAnthropicApiKeyAuth, + CatalogAnthropicProviderConfig, CatalogAwsBedrockProviderConfig, CatalogAzureFoundryApiKeyAuth, CatalogAzureFoundryProviderConfig, diff --git a/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/entity_model/llm_provider.py b/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/entity_model/llm_provider.py index 51d089eb5..c172c0c13 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/entity_model/llm_provider.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/entity_model/llm_provider.py @@ -3,7 +3,9 @@ from typing import Any, Union -from attr import define +from attrs import define +from gooddata_api_client.model.anthropic_provider_auth import AnthropicProviderAuth +from gooddata_api_client.model.anthropic_provider_config import AnthropicProviderConfig from gooddata_api_client.model.aws_bedrock_provider_config import AwsBedrockProviderConfig from gooddata_api_client.model.azure_foundry_provider_auth import AzureFoundryProviderAuth from gooddata_api_client.model.azure_foundry_provider_config import AzureFoundryProviderConfig @@ -75,6 +77,23 @@ def client_class() -> type[AzureFoundryProviderAuth]: CatalogAzureFoundryAuth = Union[CatalogAzureFoundryApiKeyAuth] +# --- Anthropic auth --- + + +@define(kw_only=True) +class CatalogAnthropicApiKeyAuth(Base): + """API key authentication for the Anthropic provider.""" + + api_key: str | None = None + type: str = "API_KEY" + + @staticmethod + def client_class() -> type[AnthropicProviderAuth]: + return AnthropicProviderAuth + + +CatalogAnthropicAuth = Union[CatalogAnthropicApiKeyAuth] + # --- Provider config types --- @@ -118,10 +137,24 @@ def client_class() -> type[AzureFoundryProviderConfig]: return AzureFoundryProviderConfig +@define(kw_only=True) +class CatalogAnthropicProviderConfig(Base): + """Anthropic provider configuration.""" + + auth: CatalogAnthropicAuth | None = None + base_url: str | None = None + type: str = "ANTHROPIC" + + @staticmethod + def client_class() -> type[AnthropicProviderConfig]: + return AnthropicProviderConfig + + CatalogLlmProviderConfig = Union[ CatalogOpenAiProviderConfig, CatalogAwsBedrockProviderConfig, CatalogAzureFoundryProviderConfig, + CatalogAnthropicProviderConfig, ] @@ -157,6 +190,16 @@ def _azure_foundry_auth_from_api(data: dict[str, Any]) -> CatalogAzureFoundryAut raise ValueError(f"Unknown Azure Foundry auth type: {auth_type}") +def _anthropic_auth_from_api(data: dict[str, Any]) -> CatalogAnthropicAuth: + auth_type = safeget(data, ["type"]) or "API_KEY" + if auth_type == "API_KEY": + return CatalogAnthropicApiKeyAuth( + api_key="", # Credentials are not returned for security reasons + type=auth_type, + ) + raise ValueError(f"Unknown Anthropic auth type: {auth_type}") + + def _provider_config_from_api(data: dict[str, Any]) -> CatalogLlmProviderConfig: provider_type = safeget(data, ["type"]) or "OPENAI" auth_data = safeget(data, ["auth"]) @@ -173,6 +216,12 @@ def _provider_config_from_api(data: dict[str, Any]) -> CatalogLlmProviderConfig: endpoint=safeget(data, ["endpoint"]), ) + if provider_type == "ANTHROPIC": + return CatalogAnthropicProviderConfig( + auth=_anthropic_auth_from_api(auth_data) if auth_data is not None else None, + base_url=safeget(data, ["baseUrl"]), + ) + # Default: OpenAI return CatalogOpenAiProviderConfig( auth=_openai_auth_from_api(auth_data) if auth_data is not None else None, diff --git a/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_create_llm_provider_anthropic.yaml b/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_create_llm_provider_anthropic.yaml new file mode 100644 index 000000000..dde422da7 --- /dev/null +++ b/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_create_llm_provider_anthropic.yaml @@ -0,0 +1,272 @@ +interactions: + - request: + body: null + headers: + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: DELETE + uri: http://localhost:3000/api/v1/entities/llmProviders/test-anthropic-provider + response: + body: + string: '' + headers: + Content-Type: + - application/vnd.gooddata.api+json + DATE: &id001 + - PLACEHOLDER + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 204 + message: No Content + - request: + body: null + headers: + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: DELETE + uri: http://localhost:3000/api/v1/entities/llmProviders/test-anthropic-provider + response: + body: + string: '' + headers: + Content-Type: + - application/vnd.gooddata.api+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 204 + message: No Content + - request: + body: + data: + attributes: + models: + - family: ANTHROPIC + id: claude-3-5-sonnet-20241022 + name: Test Anthropic Provider + providerConfig: + auth: + apiKey: test-api-key + type: API_KEY + type: ANTHROPIC + id: test-anthropic-provider + type: llmProvider + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + Content-Type: + - application/json + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: POST + uri: http://localhost:3000/api/v1/entities/llmProviders + response: + body: + string: + detail: Default model ID is required for LLM provider. + status: 400 + title: Bad Request + traceId: NORMALIZED_TRACE_ID_000000000000 + headers: + Content-Type: + - application/problem+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 400 + message: Bad Request + - request: + body: null + headers: + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: DELETE + uri: http://localhost:3000/api/v1/entities/llmProviders/test-anthropic-provider + response: + body: + string: '' + headers: + Content-Type: + - application/vnd.gooddata.api+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 204 + message: No Content + - request: + body: + data: + attributes: + defaultModelId: claude-3-5-sonnet-20241022 + models: + - family: ANTHROPIC + id: claude-3-5-sonnet-20241022 + name: Test Anthropic Provider + providerConfig: + auth: + apiKey: test-api-key + type: API_KEY + type: ANTHROPIC + id: test-anthropic-provider + type: llmProvider + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + Content-Type: + - application/json + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: POST + uri: http://localhost:3000/api/v1/entities/llmProviders + response: + body: + string: + data: + attributes: + defaultModelId: claude-3-5-sonnet-20241022 + models: + - family: ANTHROPIC + id: claude-3-5-sonnet-20241022 + name: Test Anthropic Provider + providerConfig: + auth: + type: API_KEY + baseUrl: https://api.anthropic.com + type: ANTHROPIC + id: test-anthropic-provider + type: llmProvider + links: + self: http://localhost:3000/api/v1/entities/llmProviders/test-anthropic-provider + headers: + Content-Type: + - application/json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 201 + message: Created + - request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: GET + uri: http://localhost:3000/api/v1/entities/llmProviders/test-anthropic-provider + response: + body: + string: + data: + attributes: + defaultModelId: claude-3-5-sonnet-20241022 + models: + - family: ANTHROPIC + id: claude-3-5-sonnet-20241022 + name: Test Anthropic Provider + providerConfig: + auth: + type: API_KEY + baseUrl: https://api.anthropic.com + type: ANTHROPIC + id: test-anthropic-provider + type: llmProvider + links: + self: http://localhost:3000/api/v1/entities/llmProviders/test-anthropic-provider + headers: + Content-Type: + - application/json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: DELETE + uri: http://localhost:3000/api/v1/entities/llmProviders/test-anthropic-provider + response: + body: + string: '' + headers: + Content-Type: + - application/vnd.gooddata.api+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 204 + message: No Content +version: 1 diff --git a/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py b/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py index 53e88c566..0c9d8a39e 100644 --- a/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py +++ b/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py @@ -5,9 +5,13 @@ from gooddata_api_client.exceptions import NotFoundException from gooddata_sdk import ( + CatalogAnthropicApiKeyAuth, + CatalogAnthropicProviderConfig, CatalogCspDirective, CatalogDeclarativeNotificationChannel, CatalogJwk, + CatalogLlmProvider, + CatalogLlmProviderModel, CatalogOrganization, CatalogOrganizationSetting, CatalogRsaSpecification, @@ -563,3 +567,31 @@ def test_layout_notification_channels(test_config, snapshot_notification_channel # sdk.catalog_organization.put_declarative_identity_providers([]) # idps = sdk.catalog_organization.get_declarative_identity_providers() # assert len(idps) == 0 + + +@gd_vcr.use_cassette(str(_fixtures_dir / "test_create_llm_provider_anthropic.yaml")) +def test_create_llm_provider_anthropic(test_config): + """Verify that an Anthropic LLM provider can be created, retrieved and deleted.""" + sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"]) + + provider_id = "test-anthropic-provider" + provider = CatalogLlmProvider.init( + id=provider_id, + models=[CatalogLlmProviderModel(id="claude-3-5-sonnet-20241022", family="ANTHROPIC")], + provider_config=CatalogAnthropicProviderConfig( + auth=CatalogAnthropicApiKeyAuth(api_key="test-api-key"), + ), + name="Test Anthropic Provider", + default_model_id="claude-3-5-sonnet-20241022", + ) + + try: + created = sdk.catalog_organization.create_llm_provider(provider) + assert created.id == provider_id + retrieved = sdk.catalog_organization.get_llm_provider(provider_id) + assert retrieved.id == provider_id + assert retrieved.attributes is not None + assert isinstance(retrieved.attributes.provider_config, CatalogAnthropicProviderConfig) + assert retrieved.attributes.provider_config.type == "ANTHROPIC" + finally: + safe_delete(sdk.catalog_organization.delete_llm_provider, provider_id) diff --git a/packages/gooddata-sdk/tests/catalog/unit_tests/test_llm_provider.py b/packages/gooddata-sdk/tests/catalog/unit_tests/test_llm_provider.py new file mode 100644 index 000000000..90ce8830e --- /dev/null +++ b/packages/gooddata-sdk/tests/catalog/unit_tests/test_llm_provider.py @@ -0,0 +1,165 @@ +# (C) 2026 GoodData Corporation +"""Unit tests for LLM provider SDK-side deserialization logic. + +These tests cover the branching logic in _provider_config_from_api and +the from_api path for CatalogLlmProvider. They run without a live stack +or VCR cassettes and exercise only the SDK layer. +""" + +from __future__ import annotations + +from gooddata_api_client.model.anthropic_provider_auth import AnthropicProviderAuth +from gooddata_api_client.model.anthropic_provider_config import AnthropicProviderConfig +from gooddata_sdk.catalog.organization.entity_model.llm_provider import ( + CatalogAnthropicApiKeyAuth, + CatalogAnthropicProviderConfig, + CatalogAwsBedrockProviderConfig, + CatalogAzureFoundryProviderConfig, + CatalogBedrockAccessKeyAuth, + CatalogLlmProvider, + CatalogOpenAiApiKeyAuth, + CatalogOpenAiProviderConfig, + _provider_config_from_api, +) + + +class TestProviderConfigFromApi: + """Tests for _provider_config_from_api branching logic.""" + + def test_anthropic_api_key_auth(self) -> None: + data = {"type": "ANTHROPIC", "auth": {"type": "API_KEY"}} + config = _provider_config_from_api(data) + assert isinstance(config, CatalogAnthropicProviderConfig) + assert config.type == "ANTHROPIC" + assert isinstance(config.auth, CatalogAnthropicApiKeyAuth) + assert config.auth.type == "API_KEY" + # API key is redacted by the server and filled with empty string + assert config.auth.api_key == "" + + def test_anthropic_with_custom_base_url(self) -> None: + data = { + "type": "ANTHROPIC", + "auth": {"type": "API_KEY"}, + "baseUrl": "https://custom.anthropic.example.com", + } + config = _provider_config_from_api(data) + assert isinstance(config, CatalogAnthropicProviderConfig) + assert config.base_url == "https://custom.anthropic.example.com" + + def test_anthropic_without_base_url(self) -> None: + data = {"type": "ANTHROPIC", "auth": {"type": "API_KEY"}} + config = _provider_config_from_api(data) + assert isinstance(config, CatalogAnthropicProviderConfig) + assert config.base_url is None + + def test_openai_falls_through_as_default(self) -> None: + data = {"type": "OPENAI", "auth": {"type": "API_KEY"}} + config = _provider_config_from_api(data) + assert isinstance(config, CatalogOpenAiProviderConfig) + assert isinstance(config.auth, CatalogOpenAiApiKeyAuth) + + def test_aws_bedrock(self) -> None: + data = {"type": "AWS_BEDROCK", "auth": {"type": "ACCESS_KEY"}, "region": "us-east-1"} + config = _provider_config_from_api(data) + assert isinstance(config, CatalogAwsBedrockProviderConfig) + assert isinstance(config.auth, CatalogBedrockAccessKeyAuth) + assert config.region == "us-east-1" + + def test_azure_foundry(self) -> None: + data = { + "type": "AZURE_FOUNDRY", + "auth": {"type": "API_KEY"}, + "endpoint": "https://mymodel.openai.azure.com", + } + config = _provider_config_from_api(data) + assert isinstance(config, CatalogAzureFoundryProviderConfig) + + def test_unknown_type_falls_through_to_openai(self) -> None: + # Unknown provider types default to OpenAI (existing behaviour). + data = {"type": "UNKNOWN_FUTURE_PROVIDER", "auth": {"type": "API_KEY"}} + config = _provider_config_from_api(data) + assert isinstance(config, CatalogOpenAiProviderConfig) + + +class TestCatalogLlmProviderFromApi: + """Tests for CatalogLlmProvider.from_api deserialization.""" + + def _anthropic_entity(self) -> dict: + return { + "id": "test-anthropic-provider", + "attributes": { + "providerConfig": { + "type": "ANTHROPIC", + "auth": {"type": "API_KEY"}, + "baseUrl": "https://api.anthropic.com", + }, + "models": [ + {"id": "claude-3-5-sonnet-20241022", "family": "ANTHROPIC"}, + ], + "name": "Test Anthropic Provider", + "defaultModelId": "claude-3-5-sonnet-20241022", + }, + } + + def test_from_api_returns_correct_type(self) -> None: + provider = CatalogLlmProvider.from_api(self._anthropic_entity()) + assert provider.id == "test-anthropic-provider" + assert isinstance(provider.attributes.provider_config, CatalogAnthropicProviderConfig) + + def test_from_api_provider_config_fields(self) -> None: + provider = CatalogLlmProvider.from_api(self._anthropic_entity()) + config = provider.attributes.provider_config + assert config.type == "ANTHROPIC" + assert config.base_url == "https://api.anthropic.com" + + def test_from_api_auth_fields(self) -> None: + provider = CatalogLlmProvider.from_api(self._anthropic_entity()) + auth = provider.attributes.provider_config.auth + assert isinstance(auth, CatalogAnthropicApiKeyAuth) + assert auth.type == "API_KEY" + + def test_from_api_models(self) -> None: + provider = CatalogLlmProvider.from_api(self._anthropic_entity()) + assert len(provider.attributes.models) == 1 + model = provider.attributes.models[0] + assert model.id == "claude-3-5-sonnet-20241022" + assert model.family == "ANTHROPIC" + + def test_from_api_name_and_default_model(self) -> None: + provider = CatalogLlmProvider.from_api(self._anthropic_entity()) + assert provider.attributes.name == "Test Anthropic Provider" + assert provider.attributes.default_model_id == "claude-3-5-sonnet-20241022" + + +class TestCatalogAnthropicApiKeyAuth: + """Tests for CatalogAnthropicApiKeyAuth model class.""" + + def test_default_type(self) -> None: + auth = CatalogAnthropicApiKeyAuth() + assert auth.type == "API_KEY" + assert auth.api_key is None + + def test_with_api_key(self) -> None: + auth = CatalogAnthropicApiKeyAuth(api_key="sk-ant-test-key") + assert auth.api_key == "sk-ant-test-key" + + def test_client_class_is_anthropic_provider_auth(self) -> None: + assert CatalogAnthropicApiKeyAuth.client_class() is AnthropicProviderAuth + + +class TestCatalogAnthropicProviderConfig: + """Tests for CatalogAnthropicProviderConfig model class.""" + + def test_default_type(self) -> None: + config = CatalogAnthropicProviderConfig() + assert config.type == "ANTHROPIC" + assert config.base_url is None + assert config.auth is None + + def test_with_auth(self) -> None: + auth = CatalogAnthropicApiKeyAuth(api_key="sk-ant-test-key") + config = CatalogAnthropicProviderConfig(auth=auth) + assert isinstance(config.auth, CatalogAnthropicApiKeyAuth) + + def test_client_class_is_anthropic_provider_config(self) -> None: + assert CatalogAnthropicProviderConfig.client_class() is AnthropicProviderConfig