From 71c61720a280c5d8e1e3e19b2bae7510c7bd111b Mon Sep 17 00:00:00 2001 From: yenkins-admin <5391010+yenkins-admin@users.noreply.github.com> Date: Mon, 13 Apr 2026 13:20:44 +0000 Subject: [PATCH] feat(gooddata-sdk): [AUTO] Deprecate LLM Endpoint API and add resolveLlmProviders endpoint --- .../gooddata-sdk/src/gooddata_sdk/__init__.py | 5 + .../catalog/workspace/content_service.py | 19 ++++ .../workspace/entity_model/resolved_llm.py | 95 +++++++++++++++++++ .../catalog/test_catalog_workspace_content.py | 8 ++ 4 files changed, 127 insertions(+) create mode 100644 packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/entity_model/resolved_llm.py diff --git a/packages/gooddata-sdk/src/gooddata_sdk/__init__.py b/packages/gooddata-sdk/src/gooddata_sdk/__init__.py index 77397b92d..f0a49b969 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/__init__.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/__init__.py @@ -264,6 +264,11 @@ CatalogDependentEntitiesResponse, CatalogEntityIdentifier, ) +from gooddata_sdk.catalog.workspace.entity_model.resolved_llm import ( + CatalogResolvedLlmModel, + CatalogResolvedLlmProvider, + CatalogResolvedLlms, +) from gooddata_sdk.catalog.workspace.entity_model.user_data_filter import ( CatalogUserDataFilter, CatalogUserDataFilterAttributes, diff --git a/packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/content_service.py b/packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/content_service.py index 7be97bee2..8506b4fed 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/content_service.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/content_service.py @@ -31,6 +31,7 @@ CatalogDependentEntitiesRequest, CatalogDependentEntitiesResponse, ) +from gooddata_sdk.catalog.workspace.entity_model.resolved_llm import CatalogResolvedLlms from gooddata_sdk.catalog.workspace.model_container import CatalogWorkspaceContent from gooddata_sdk.client import GoodDataApiClient from gooddata_sdk.compute.model.attribute import Attribute @@ -685,3 +686,21 @@ def get_label_elements( workspace_id, request, _check_return_type=False, **paging_params ) return [v["title"] for v in values["elements"]] + + def resolve_llm_providers(self, workspace_id: str) -> CatalogResolvedLlms: + """Resolve the active LLM configuration for a given workspace. + + Returns the active LLM configuration. When the ENABLE_LLM_ENDPOINT_REPLACEMENT feature + flag is enabled, returns LLM Providers with their associated models. Otherwise, falls + back to the legacy LLM Endpoints. + + Args: + workspace_id (str): + Workspace identification string e.g. "demo" + + Returns: + CatalogResolvedLlms: + Object containing the resolved LLM configuration, or None if no LLM is configured. + """ + response = self._actions_api.resolve_llm_providers(workspace_id, _check_return_type=False) + return CatalogResolvedLlms.from_api(response) diff --git a/packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/entity_model/resolved_llm.py b/packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/entity_model/resolved_llm.py new file mode 100644 index 000000000..5f187117c --- /dev/null +++ b/packages/gooddata-sdk/src/gooddata_sdk/catalog/workspace/entity_model/resolved_llm.py @@ -0,0 +1,95 @@ +# (C) 2026 GoodData Corporation +from __future__ import annotations + +from typing import Any + +import attrs + +from gooddata_sdk.catalog.base import Base + + +@attrs.define(kw_only=True) +class CatalogResolvedLlmModel(Base): + """Represents a single LLM model available in the resolved LLM configuration.""" + + id: str + family: str | None = None + + @staticmethod + def client_class() -> Any: + return NotImplemented + + @classmethod + def from_api(cls, data: Any) -> CatalogResolvedLlmModel: + family = None + try: + family = data["family"] + except (KeyError, TypeError): + pass + return cls( + id=data["id"], + family=family, + ) + + +@attrs.define(kw_only=True) +class CatalogResolvedLlmProvider(Base): + """Represents a resolved LLM provider configuration for a workspace. + + Returned by the resolveLlmProviders endpoint. When the ENABLE_LLM_ENDPOINT_REPLACEMENT + feature flag is enabled, contains LLM provider information with associated models. + Otherwise, falls back to the legacy LLM endpoint representation. + """ + + id: str + title: str | None = None + models: list[CatalogResolvedLlmModel] = attrs.field(factory=list) + + @staticmethod + def client_class() -> Any: + return NotImplemented + + @classmethod + def from_api(cls, data: Any) -> CatalogResolvedLlmProvider: + raw_models = None + try: + raw_models = data["models"] + except (KeyError, TypeError): + pass + models = [CatalogResolvedLlmModel.from_api(m) for m in raw_models] if raw_models is not None else [] + title = None + try: + title = data["title"] + except (KeyError, TypeError): + pass + return cls( + id=data["id"], + title=title, + models=models, + ) + + +@attrs.define(kw_only=True) +class CatalogResolvedLlms(Base): + """Represents the resolved LLM configuration for a workspace. + + Returned by the resolveLlmProviders endpoint. The data field contains the active + LLM configuration, or None if no LLM is configured for the workspace. + """ + + data: CatalogResolvedLlmProvider | None = None + + @staticmethod + def client_class() -> Any: + return NotImplemented + + @classmethod + def from_api(cls, response: Any) -> CatalogResolvedLlms: + data_raw = None + try: + data_raw = response["data"] + except (KeyError, TypeError): + pass + if data_raw is None: + return cls(data=None) + return cls(data=CatalogResolvedLlmProvider.from_api(data_raw)) diff --git a/packages/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py b/packages/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py index 312088e9f..b16ef5e12 100644 --- a/packages/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py +++ b/packages/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py @@ -18,6 +18,7 @@ CatalogDependsOn, CatalogDependsOnDateFilter, CatalogEntityIdentifier, + CatalogResolvedLlms, CatalogValidateByItem, CatalogWorkspace, DataSourceValidator, @@ -502,3 +503,10 @@ def test_export_definition_analytics_layout(test_config): assert deep_eq(analytics_o.analytics.export_definitions, analytics_e.analytics.export_definitions) finally: safe_delete(_refresh_workspaces, sdk) + + +@gd_vcr.use_cassette(str(_fixtures_dir / "test_resolve_llm_providers.yaml")) +def test_resolve_llm_providers(test_config): + sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"]) + result = sdk.catalog_workspace_content.resolve_llm_providers(test_config["workspace"]) + assert isinstance(result, CatalogResolvedLlms)