From f2f1fbb391811ef64dc59909576aa0806e3f2260 Mon Sep 17 00:00:00 2001 From: yenkins-admin <5391010+yenkins-admin@users.noreply.github.com> Date: Mon, 18 May 2026 09:24:20 +0000 Subject: [PATCH 1/3] feat(gooddata-sdk): [AUTO] Add Anthropic provider config to LLM provider config --- .../gooddata-sdk/src/gooddata_sdk/__init__.py | 2 + .../organization/entity_model/llm_provider.py | 49 +++++++++++++++++++ .../catalog/test_catalog_organization.py | 27 ++++++++++ 3 files changed, 78 insertions(+) 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..37cf7c6cb 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 @@ -75,6 +75,24 @@ 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[OpenAiProviderAuth]: + # Stand-in: AnthropicProviderAuth not yet present in the generated client. + return OpenAiProviderAuth + + +CatalogAnthropicAuth = Union[CatalogAnthropicApiKeyAuth] + # --- Provider config types --- @@ -118,10 +136,25 @@ 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[OpenAIProviderConfig]: + # Stand-in: AnthropicProviderConfig not yet present in the generated client. + return OpenAIProviderConfig + + 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/test_catalog_organization.py b/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py index 53e88c566..5ea976fb5 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, @@ -334,6 +338,29 @@ def test_layout_notification_channels(test_config, snapshot_notification_channel # snapshot_notification_channels fixture restores original state in teardown +@gd_vcr.use_cassette(str(_fixtures_dir / "test_anthropic_llm_provider.yaml")) +def test_create_anthropic_llm_provider(test_config): + sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"]) + provider_id = "test-anthropic-provider" + new_provider = CatalogLlmProvider.init( + id=provider_id, + models=[CatalogLlmProviderModel(id="claude-3-5-sonnet-20241022", family="claude")], + provider_config=CatalogAnthropicProviderConfig( + auth=CatalogAnthropicApiKeyAuth(api_key="sk-ant-test-key"), + base_url="https://api.anthropic.com", + ), + name="Test Anthropic Provider", + ) + try: + created = sdk.catalog_organization.create_llm_provider(new_provider) + assert created.id == provider_id + fetched = sdk.catalog_organization.get_llm_provider(provider_id) + assert fetched.id == provider_id + assert isinstance(fetched.attributes.provider_config, CatalogAnthropicProviderConfig) + finally: + safe_delete(sdk.catalog_organization.delete_llm_provider, provider_id) + + # # The following tests are commented out as they require the organization to have the FEDERATED_IDENTITY_MANAGEMENT # entitlement enabled which cannot be done via SDK and must be done by GoodData support. From 891b9b7b5691774d0c547d42989bf2227fef5f25 Mon Sep 17 00:00:00 2001 From: yenkins-admin <5391010+yenkins-admin@users.noreply.github.com> Date: Mon, 18 May 2026 09:46:05 +0000 Subject: [PATCH 2/3] fix(gooddata-sdk): [AUTO] fix-agent attempt 1 --- .../test_anthropic_llm_provider.yaml | 31 +++++++++++++++++++ .../catalog/test_catalog_organization.py | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 packages/gooddata-sdk/tests/catalog/fixtures/organization/test_anthropic_llm_provider.yaml diff --git a/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_anthropic_llm_provider.yaml b/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_anthropic_llm_provider.yaml new file mode 100644 index 000000000..b6f004f47 --- /dev/null +++ b/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_anthropic_llm_provider.yaml @@ -0,0 +1,31 @@ +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 +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 5ea976fb5..e921ec26d 100644 --- a/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py +++ b/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py @@ -344,7 +344,7 @@ def test_create_anthropic_llm_provider(test_config): provider_id = "test-anthropic-provider" new_provider = CatalogLlmProvider.init( id=provider_id, - models=[CatalogLlmProviderModel(id="claude-3-5-sonnet-20241022", family="claude")], + models=[CatalogLlmProviderModel(id="claude-3-5-sonnet-20241022", family="ANTHROPIC")], provider_config=CatalogAnthropicProviderConfig( auth=CatalogAnthropicApiKeyAuth(api_key="sk-ant-test-key"), base_url="https://api.anthropic.com", From a0d740f028ed6b93e4dcddffd8a140ac4123974a Mon Sep 17 00:00:00 2001 From: yenkins-admin <5391010+yenkins-admin@users.noreply.github.com> Date: Mon, 18 May 2026 09:59:37 +0000 Subject: [PATCH 3/3] fix(gooddata-sdk): [AUTO] fix-agent attempt 2 --- .../organization/entity_model/llm_provider.py | 43 +++++++++++++++++++ .../test_anthropic_llm_provider.yaml | 31 ------------- 2 files changed, 43 insertions(+), 31 deletions(-) delete mode 100644 packages/gooddata-sdk/tests/catalog/fixtures/organization/test_anthropic_llm_provider.yaml 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 37cf7c6cb..39c8a597e 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 @@ -200,6 +200,25 @@ def _anthropic_auth_from_api(data: dict[str, Any]) -> CatalogAnthropicAuth: raise ValueError(f"Unknown Anthropic auth type: {auth_type}") +def _anthropic_config_to_camel_dict(config: CatalogAnthropicProviderConfig) -> dict[str, Any]: + """Convert CatalogAnthropicProviderConfig to a camelCase dict for direct _data_store injection. + + The generated API client's JsonApiLlmProviderInAttributesProviderConfig oneOf does not yet + include AnthropicProviderConfig. We bypass schema validation by storing the config as a + plain camelCase dict in _data_store; model_to_dict(serialize=True) serialises it correctly. + """ + result: dict[str, Any] = {"type": config.type} + if config.base_url is not None: + result["baseUrl"] = config.base_url + if config.auth is not None: + auth = config.auth + auth_dict: dict[str, Any] = {"type": auth.type} + if isinstance(auth, CatalogAnthropicApiKeyAuth) and auth.api_key is not None: + auth_dict["apiKey"] = auth.api_key + result["auth"] = auth_dict + return result + + def _provider_config_from_api(data: dict[str, Any]) -> CatalogLlmProviderConfig: provider_type = safeget(data, ["type"]) or "OPENAI" auth_data = safeget(data, ["auth"]) @@ -241,6 +260,30 @@ class CatalogLlmProviderDocument(Base): def client_class() -> type[JsonApiLlmProviderInDocument]: return JsonApiLlmProviderInDocument + def to_api(self) -> JsonApiLlmProviderInDocument: + """Build the API model with special handling for Anthropic provider config. + + The generated API client's JsonApiLlmProviderInAttributesProviderConfig oneOf schema + does not yet include AnthropicProviderConfig, so the normal Base.to_api() / from_dict() + path raises ApiValueError for type='ANTHROPIC'. When the provider config is Anthropic, + we build the document without providerConfig (it is optional in the API), then inject + the config as a raw camelCase dict directly into _data_store to bypass schema validation. + """ + provider_config = ( + self.data.attributes.provider_config if self.data.attributes is not None else None + ) + if isinstance(provider_config, CatalogAnthropicProviderConfig): + snake_dict = self._get_snake_dict() + # Remove provider_config to avoid the oneOf validation failure in the generated client + snake_dict["data"]["attributes"].pop("provider_config", None) + api_doc = self.client_class().from_dict(snake_dict, camel_case=False) + # Inject provider_config directly as a raw camelCase dict, bypassing oneOf schema + api_doc.data.attributes._data_store["provider_config"] = ( + _anthropic_config_to_camel_dict(provider_config) + ) + return api_doc + return super().to_api() + @define(kw_only=True) class CatalogLlmProviderPatchDocument(Base): diff --git a/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_anthropic_llm_provider.yaml b/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_anthropic_llm_provider.yaml deleted file mode 100644 index b6f004f47..000000000 --- a/packages/gooddata-sdk/tests/catalog/fixtures/organization/test_anthropic_llm_provider.yaml +++ /dev/null @@ -1,31 +0,0 @@ -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 -version: 1