Skip to content

Commit af6d231

Browse files
authored
Merge pull request #1388 from hkad98/jkd/llm-provider
feat(gooddata-sdk): add LLM Provider support (Beta)
2 parents 93d0c4d + 4c84ae9 commit af6d231

File tree

3 files changed

+441
-0
lines changed

3 files changed

+441
-0
lines changed

packages/gooddata-sdk/src/gooddata_sdk/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@
103103
CatalogLlmEndpoint,
104104
CatalogLlmEndpointDocument,
105105
)
106+
from gooddata_sdk.catalog.organization.entity_model.llm_provider import (
107+
CatalogAwsBedrockProviderConfig,
108+
CatalogAzureFoundryApiKeyAuth,
109+
CatalogAzureFoundryProviderConfig,
110+
CatalogBedrockAccessKeyAuth,
111+
CatalogLlmProvider,
112+
CatalogLlmProviderDocument,
113+
CatalogLlmProviderModel,
114+
CatalogLlmProviderPatch,
115+
CatalogLlmProviderPatchDocument,
116+
CatalogOpenAiApiKeyAuth,
117+
CatalogOpenAiProviderConfig,
118+
)
106119
from gooddata_sdk.catalog.organization.entity_model.organization import CatalogOrganization
107120
from gooddata_sdk.catalog.organization.entity_model.setting import CatalogOrganizationSetting
108121
from gooddata_sdk.catalog.organization.layout.export_template import (
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
# (C) 2026 GoodData Corporation
2+
from __future__ import annotations
3+
4+
from typing import Any, Union
5+
6+
from attr import define
7+
from gooddata_api_client.model.aws_bedrock_provider_config import AwsBedrockProviderConfig
8+
from gooddata_api_client.model.azure_foundry_provider_auth import AzureFoundryProviderAuth
9+
from gooddata_api_client.model.azure_foundry_provider_config import AzureFoundryProviderConfig
10+
from gooddata_api_client.model.bedrock_provider_auth import BedrockProviderAuth
11+
from gooddata_api_client.model.json_api_llm_provider_in import JsonApiLlmProviderIn
12+
from gooddata_api_client.model.json_api_llm_provider_in_attributes import JsonApiLlmProviderInAttributes
13+
from gooddata_api_client.model.json_api_llm_provider_in_attributes_models_inner import (
14+
JsonApiLlmProviderInAttributesModelsInner,
15+
)
16+
from gooddata_api_client.model.json_api_llm_provider_in_document import JsonApiLlmProviderInDocument
17+
from gooddata_api_client.model.json_api_llm_provider_patch import JsonApiLlmProviderPatch
18+
from gooddata_api_client.model.json_api_llm_provider_patch_attributes import JsonApiLlmProviderPatchAttributes
19+
from gooddata_api_client.model.json_api_llm_provider_patch_document import JsonApiLlmProviderPatchDocument
20+
from gooddata_api_client.model.open_ai_provider_auth import OpenAiProviderAuth
21+
from gooddata_api_client.model.open_ai_provider_config import OpenAIProviderConfig
22+
23+
from gooddata_sdk.catalog.base import Base
24+
from gooddata_sdk.utils import safeget
25+
26+
# --- OpenAI auth ---
27+
28+
29+
@define(kw_only=True)
30+
class CatalogOpenAiApiKeyAuth(Base):
31+
"""API key authentication for the OpenAI provider."""
32+
33+
api_key: str | None = None
34+
type: str = "API_KEY"
35+
36+
@staticmethod
37+
def client_class() -> type[OpenAiProviderAuth]:
38+
return OpenAiProviderAuth
39+
40+
41+
CatalogOpenAiAuth = Union[CatalogOpenAiApiKeyAuth]
42+
43+
# --- AWS Bedrock auth ---
44+
45+
46+
@define(kw_only=True)
47+
class CatalogBedrockAccessKeyAuth(Base):
48+
"""AWS access key authentication for the Bedrock provider."""
49+
50+
access_key_id: str | None = None
51+
secret_access_key: str | None = None
52+
session_token: str | None = None
53+
type: str = "ACCESS_KEY"
54+
55+
@staticmethod
56+
def client_class() -> type[BedrockProviderAuth]:
57+
return BedrockProviderAuth
58+
59+
60+
CatalogBedrockAuth = Union[CatalogBedrockAccessKeyAuth]
61+
62+
# --- Azure Foundry auth ---
63+
64+
65+
@define(kw_only=True)
66+
class CatalogAzureFoundryApiKeyAuth(Base):
67+
"""API key authentication for the Azure Foundry provider."""
68+
69+
api_key: str | None = None
70+
type: str = "API_KEY"
71+
72+
@staticmethod
73+
def client_class() -> type[AzureFoundryProviderAuth]:
74+
return AzureFoundryProviderAuth
75+
76+
77+
CatalogAzureFoundryAuth = Union[CatalogAzureFoundryApiKeyAuth]
78+
79+
# --- Provider config types ---
80+
81+
82+
@define(kw_only=True)
83+
class CatalogOpenAiProviderConfig(Base):
84+
"""OpenAI provider configuration."""
85+
86+
auth: CatalogOpenAiAuth | None = None
87+
base_url: str | None = None
88+
organization: str | None = None
89+
type: str = "OPENAI"
90+
91+
@staticmethod
92+
def client_class() -> type[OpenAIProviderConfig]:
93+
return OpenAIProviderConfig
94+
95+
96+
@define(kw_only=True)
97+
class CatalogAwsBedrockProviderConfig(Base):
98+
"""AWS Bedrock provider configuration."""
99+
100+
auth: CatalogBedrockAuth | None = None
101+
region: str | None = None
102+
type: str = "AWS_BEDROCK"
103+
104+
@staticmethod
105+
def client_class() -> type[AwsBedrockProviderConfig]:
106+
return AwsBedrockProviderConfig
107+
108+
109+
@define(kw_only=True)
110+
class CatalogAzureFoundryProviderConfig(Base):
111+
"""Azure Foundry provider configuration."""
112+
113+
auth: CatalogAzureFoundryAuth | None = None
114+
endpoint: str | None = None
115+
type: str = "AZURE_FOUNDRY"
116+
117+
@staticmethod
118+
def client_class() -> type[AzureFoundryProviderConfig]:
119+
return AzureFoundryProviderConfig
120+
121+
122+
CatalogLlmProviderConfig = Union[
123+
CatalogOpenAiProviderConfig,
124+
CatalogAwsBedrockProviderConfig,
125+
CatalogAzureFoundryProviderConfig,
126+
]
127+
128+
129+
def _openai_auth_from_api(data: dict[str, Any]) -> CatalogOpenAiAuth:
130+
auth_type = safeget(data, ["type"]) or "API_KEY"
131+
if auth_type == "API_KEY":
132+
return CatalogOpenAiApiKeyAuth(
133+
api_key="", # Credentials are not returned for security reasons
134+
type=auth_type,
135+
)
136+
raise ValueError(f"Unknown OpenAI auth type: {auth_type}")
137+
138+
139+
def _bedrock_auth_from_api(data: dict[str, Any]) -> CatalogBedrockAuth:
140+
auth_type = safeget(data, ["type"]) or "ACCESS_KEY"
141+
if auth_type == "ACCESS_KEY":
142+
return CatalogBedrockAccessKeyAuth(
143+
access_key_id="", # Credentials are not returned for security reasons
144+
secret_access_key="",
145+
session_token=safeget(data, ["sessionToken"]),
146+
type=auth_type,
147+
)
148+
raise ValueError(f"Unknown Bedrock auth type: {auth_type}")
149+
150+
151+
def _azure_foundry_auth_from_api(data: dict[str, Any]) -> CatalogAzureFoundryAuth:
152+
auth_type = safeget(data, ["type"]) or "API_KEY"
153+
if auth_type == "API_KEY":
154+
return CatalogAzureFoundryApiKeyAuth(
155+
api_key="", # Credentials are not returned for security reasons
156+
type=auth_type,
157+
)
158+
raise ValueError(f"Unknown Azure Foundry auth type: {auth_type}")
159+
160+
161+
def _provider_config_from_api(data: dict[str, Any]) -> CatalogLlmProviderConfig:
162+
provider_type = safeget(data, ["type"]) or "OPENAI"
163+
auth_data = safeget(data, ["auth"])
164+
165+
if provider_type == "AWS_BEDROCK":
166+
return CatalogAwsBedrockProviderConfig(
167+
auth=_bedrock_auth_from_api(auth_data) if auth_data is not None else None,
168+
region=safeget(data, ["region"]),
169+
)
170+
171+
if provider_type == "AZURE_FOUNDRY":
172+
return CatalogAzureFoundryProviderConfig(
173+
auth=_azure_foundry_auth_from_api(auth_data) if auth_data is not None else None,
174+
endpoint=safeget(data, ["endpoint"]),
175+
)
176+
177+
# Default: OpenAI
178+
return CatalogOpenAiProviderConfig(
179+
auth=_openai_auth_from_api(auth_data) if auth_data is not None else None,
180+
base_url=safeget(data, ["baseUrl"]),
181+
organization=safeget(data, ["organization"]),
182+
)
183+
184+
185+
# --- Document wrappers ---
186+
187+
188+
@define(kw_only=True)
189+
class CatalogLlmProviderDocument(Base):
190+
data: CatalogLlmProvider
191+
192+
@staticmethod
193+
def client_class() -> type[JsonApiLlmProviderInDocument]:
194+
return JsonApiLlmProviderInDocument
195+
196+
197+
@define(kw_only=True)
198+
class CatalogLlmProviderPatchDocument(Base):
199+
data: CatalogLlmProviderPatch
200+
201+
@staticmethod
202+
def client_class() -> type[JsonApiLlmProviderPatchDocument]:
203+
return JsonApiLlmProviderPatchDocument
204+
205+
206+
# --- Model type ---
207+
208+
209+
@define(kw_only=True)
210+
class CatalogLlmProviderModel(Base):
211+
"""Represents a single LLM model available for a provider."""
212+
213+
id: str
214+
family: str
215+
216+
@staticmethod
217+
def client_class() -> type[JsonApiLlmProviderInAttributesModelsInner]:
218+
return JsonApiLlmProviderInAttributesModelsInner
219+
220+
221+
# --- Main entity types ---
222+
223+
224+
@define(kw_only=True)
225+
class CatalogLlmProvider(Base):
226+
id: str
227+
attributes: CatalogLlmProviderAttributes | None = None
228+
229+
@staticmethod
230+
def client_class() -> type[JsonApiLlmProviderIn]:
231+
return JsonApiLlmProviderIn
232+
233+
@classmethod
234+
def init(
235+
cls,
236+
id: str,
237+
models: list[CatalogLlmProviderModel],
238+
provider_config: CatalogLlmProviderConfig,
239+
name: str | None = None,
240+
description: str | None = None,
241+
default_model_id: str | None = None,
242+
) -> CatalogLlmProvider:
243+
return cls(
244+
id=id,
245+
attributes=CatalogLlmProviderAttributes(
246+
models=models,
247+
provider_config=provider_config,
248+
name=name,
249+
description=description,
250+
default_model_id=default_model_id,
251+
),
252+
)
253+
254+
@classmethod
255+
def from_api(cls, entity: dict[str, Any]) -> CatalogLlmProvider:
256+
ea = entity["attributes"]
257+
raw_models = safeget(ea, ["models"]) or []
258+
models = [
259+
CatalogLlmProviderModel(
260+
id=safeget(m, ["id"]),
261+
family=safeget(m, ["family"]),
262+
)
263+
for m in raw_models
264+
]
265+
raw_config = safeget(ea, ["providerConfig"]) or {}
266+
provider_config = _provider_config_from_api(raw_config)
267+
return cls(
268+
id=entity["id"],
269+
attributes=CatalogLlmProviderAttributes(
270+
models=models,
271+
provider_config=provider_config,
272+
name=safeget(ea, ["name"]),
273+
description=safeget(ea, ["description"]),
274+
default_model_id=safeget(ea, ["defaultModelId"]),
275+
),
276+
)
277+
278+
279+
@define(kw_only=True)
280+
class CatalogLlmProviderPatch(Base):
281+
id: str
282+
attributes: CatalogLlmProviderPatchAttributes | None = None
283+
284+
@staticmethod
285+
def client_class() -> type[JsonApiLlmProviderPatch]:
286+
return JsonApiLlmProviderPatch
287+
288+
@classmethod
289+
def init(
290+
cls,
291+
id: str,
292+
models: list[CatalogLlmProviderModel] | None = None,
293+
provider_config: CatalogLlmProviderConfig | None = None,
294+
name: str | None = None,
295+
description: str | None = None,
296+
default_model_id: str | None = None,
297+
) -> CatalogLlmProviderPatch:
298+
return cls(
299+
id=id,
300+
attributes=CatalogLlmProviderPatchAttributes(
301+
models=models,
302+
provider_config=provider_config,
303+
name=name,
304+
description=description,
305+
default_model_id=default_model_id,
306+
),
307+
)
308+
309+
310+
# --- Attributes ---
311+
312+
313+
@define(kw_only=True)
314+
class CatalogLlmProviderAttributes(Base):
315+
models: list[CatalogLlmProviderModel]
316+
provider_config: CatalogLlmProviderConfig
317+
name: str | None = None
318+
description: str | None = None
319+
default_model_id: str | None = None
320+
321+
@staticmethod
322+
def client_class() -> type[JsonApiLlmProviderInAttributes]:
323+
return JsonApiLlmProviderInAttributes
324+
325+
326+
@define(kw_only=True)
327+
class CatalogLlmProviderPatchAttributes(Base):
328+
models: list[CatalogLlmProviderModel] | None = None
329+
provider_config: CatalogLlmProviderConfig | None = None
330+
name: str | None = None
331+
description: str | None = None
332+
default_model_id: str | None = None
333+
334+
@staticmethod
335+
def client_class() -> type[JsonApiLlmProviderPatchAttributes]:
336+
return JsonApiLlmProviderPatchAttributes

0 commit comments

Comments
 (0)