Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/gooddata-sdk/src/gooddata_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Original file line number Diff line number Diff line change
@@ -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))
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
CatalogDependsOn,
CatalogDependsOnDateFilter,
CatalogEntityIdentifier,
CatalogResolvedLlms,
CatalogValidateByItem,
CatalogWorkspace,
DataSourceValidator,
Expand Down Expand Up @@ -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)
Loading